From f1cb9b9a839b2f80f2b4b5daab3000503b1353fc Mon Sep 17 00:00:00 2001 From: Myk Date: Tue, 5 Jul 2022 12:21:41 -0700 Subject: [PATCH 001/334] Build and install text help alongside html (#2236) * build text docs alongside html also: - capture more doc dependencies that should cause rebuilds - move intermediate build output (doctree data) into build dir - allow sphinx build to multitask more for faster completion times * install text help alongside html help * update settings in docs build action --- .github/workflows/build.yml | 4 ++-- .gitignore | 1 + CMakeLists.txt | 42 ++++++++++++++++++++++++------------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bef01888f..fc6eac094 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -128,14 +128,14 @@ jobs: python-version: 3 - name: Install dependencies run: | - pip install 'sphinx<4.4.0' + pip install 'sphinx' - name: Clone DFHack uses: actions/checkout@v1 with: submodules: true - name: Build docs run: | - sphinx-build -W --keep-going -j3 --color . docs/html + sphinx-build -W --keep-going -j auto --color . docs/html - name: Upload docs uses: actions/upload-artifact@v1 with: diff --git a/.gitignore b/.gitignore index 8e401a7df..0b54f4a84 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ build/VC2010 docs/_* docs/html/ docs/pdf/ +docs/text/ # in-place build build/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 8271840e6..6bee1c04a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # main project file. use it from a build sub-folder, see COMPILE for details ## some generic CMake magic -cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.6 FATAL_ERROR) cmake_policy(SET CMP0048 NEW) project(dfhack) @@ -450,37 +450,47 @@ if(BUILD_DOCS) message(SEND_ERROR "Sphinx not found but BUILD_DOCS enabled") endif() - file(GLOB SPHINX_DEPS - "${CMAKE_CURRENT_SOURCE_DIR}/docs/*.rst" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/guides/*.rst" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/changelog.txt" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/gen_changelog.py" + file(GLOB SPHINX_GLOB_DEPS + LIST_DIRECTORIES false "${CMAKE_CURRENT_SOURCE_DIR}/docs/images/*.png" "${CMAKE_CURRENT_SOURCE_DIR}/docs/styles/*" - "${CMAKE_CURRENT_SOURCE_DIR}/conf.py" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/about.txt" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*/about.txt" + "${CMAKE_CURRENT_SOURCE_DIR}/data/init/*init" + ) + file(GLOB_RECURSE SPHINX_GLOB_RECURSE_DEPS + "${CMAKE_CURRENT_SOURCE_DIR}/*.rst" + "${CMAKE_CURRENT_SOURCE_DIR}/changelog.txt" + ) + list(FILTER SPHINX_GLOB_RECURSE_DEPS + EXCLUDE REGEX "docs/_" ) file(GLOB_RECURSE SPHINX_SCRIPT_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.lua" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.rb" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.txt" ) - set(SPHINX_DEPS ${SPHINX_DEPS} ${SPHINX_SCRIPT_DEPS} - "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.rst" + set(SPHINX_DEPS ${SPHINX_GLOB_DEPS} ${SPHINX_GLOB_RECURSE_DEPS} ${SPHINX_SCRIPT_DEPS} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt" + "${CMAKE_CURRENT_SOURCE_DIR}/conf.py" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/gen_changelog.py" ) set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/html/.buildinfo") set_source_files_properties(${SPHINX_OUTPUT} PROPERTIES GENERATED TRUE) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND ${SPHINX_EXECUTABLE} - -a -E -q -b html + -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/html" - -w "${CMAKE_CURRENT_SOURCE_DIR}/docs/_sphinx-warnings.txt" - -j 2 + -w "${CMAKE_BINARY_DIR}/docs/html/_sphinx-warnings.txt" + -j auto + COMMAND ${SPHINX_EXECUTABLE} + -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/text" + -w "${CMAKE_BINARY_DIR}/docs/text/_sphinx-warnings.txt" + -j auto DEPENDS ${SPHINX_DEPS} - COMMENT "Building HTML documentation with Sphinx" + COMMENT "Building documentation with Sphinx" ) add_custom_target(dfhack_docs ALL @@ -493,6 +503,8 @@ if(BUILD_DOCS) install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/ DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) + install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/ + DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) install(FILES docs/_auto/news.rst docs/_auto/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES "README.html" DESTINATION "${DFHACK_DATA_DESTINATION}") endif() From 27d7c3acc64009f0f4e361a4c3da891e9df1b239 Mon Sep 17 00:00:00 2001 From: Myk Date: Tue, 5 Jul 2022 14:05:34 -0700 Subject: [PATCH 002/334] Myk sample command help (#2238) * add example tool documentation in proposed format * refine Tools.rst --- conf.py | 6 +----- docs/Tools.rst | 13 +++++++++++++ docs/tools/cromulate.rst | 34 ++++++++++++++++++++++++++++++++++ index.rst | 1 + 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 docs/Tools.rst create mode 100644 docs/tools/cromulate.rst diff --git a/conf.py b/conf.py index 71ba65fb7..53b192467 100644 --- a/conf.py +++ b/conf.py @@ -66,11 +66,7 @@ def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, """Custom role parser for DFHack default keybinds.""" roles.set_classes(options) if text not in KEYBINDS: - msg = inliner.reporter.error( - 'no keybinding for {} in dfhack.init-example'.format(text), - line=lineno) - prb = inliner.problematic(rawtext, rawtext, msg) - return [prb], [msg] + return [], [] newnode = nodes.paragraph() for cmd, key, ctx in KEYBINDS[text]: n = nodes.paragraph() diff --git a/docs/Tools.rst b/docs/Tools.rst new file mode 100644 index 000000000..15c5f878e --- /dev/null +++ b/docs/Tools.rst @@ -0,0 +1,13 @@ +.. _tools-index: + +############ +DFHack Tools +############ + +These are the DFHack commands you can run. + +.. toctree:: + :titlesonly: + :glob: + + /docs/tools/* diff --git a/docs/tools/cromulate.rst b/docs/tools/cromulate.rst new file mode 100644 index 000000000..ca07ae0ca --- /dev/null +++ b/docs/tools/cromulate.rst @@ -0,0 +1,34 @@ +cromulate +========= + +Tags: productivity, unit, adventure +:dfhack-keybind:`cromulate` + +Collects all widgets into a frobozz electric cromufiler. You might want to do +this if you discover that your widgets have become decromulated. It is safe to +run this command periodically even if you are unsure if that's the case. + +Usage:: + + cromulate [all|here] [] + +When run without parameters, it lists all your widgets. Add the ``all`` keyword +to collect all widgets into the cromufiler, or the ``here`` keyword to just +collect those under the cursor. + +Options: + +:``-d``, ``--destroy``: + Destroy the widgets instead of collecting them into the cromufiler. +:``-q``, ``--quiet``: + Don't display any informational output. Errors will still be printed to the + console. + +Examples: + +- ``cromulate`` + Lists all widgets and their positions +- ``cromlate all`` + Gather all widgets into the cromufiler +- ``cromulate here --destroy`` + Destroys the widgets under the cursor diff --git a/index.rst b/index.rst index 2e5454f40..63ab02a86 100644 --- a/index.rst +++ b/index.rst @@ -32,6 +32,7 @@ User Manual /docs/Core /docs/Plugins /docs/Scripts + /docs/Tools /docs/guides/index /docs/index-about /docs/index-dev From b0e7325d4fc54ab9d0cf6b2876351bcc7df0823a Mon Sep 17 00:00:00 2001 From: Myk Date: Tue, 5 Jul 2022 14:13:25 -0700 Subject: [PATCH 003/334] add sample plugin to go with the sample help (#2239) --- plugins/CMakeLists.txt | 1 + plugins/cromulate.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 plugins/cromulate.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4a4b74eaa..732673926 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -106,6 +106,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(command-prompt command-prompt.cpp) dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) dfhack_plugin(createitem createitem.cpp) + dfhack_plugin(cromulate cromulate.cpp) dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) dfhack_plugin(deramp deramp.cpp) diff --git a/plugins/cromulate.cpp b/plugins/cromulate.cpp new file mode 100644 index 000000000..9f7fdb9f9 --- /dev/null +++ b/plugins/cromulate.cpp @@ -0,0 +1,28 @@ +#include "Core.h" +#include +#include +#include + +using namespace DFHack; +using namespace df::enums; + +DFHACK_PLUGIN("cromulate"); + +command_result cromulate (color_ostream &out, std::vector & parameters); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { + commands.push_back(PluginCommand("cromulate", + "in-cpp plugin short desc", //to use one line in the ``[DFHack]# ls`` output + cromulate, + false, + "in-cpp plugin long help")); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + return CR_OK; +} + +command_result cromulate (color_ostream &out, std::vector ¶meters) { + return CR_OK; +} From 64aba7a8a38f87dfb8dc984ccefb773e871c4cbe Mon Sep 17 00:00:00 2001 From: Myk Date: Wed, 6 Jul 2022 11:57:48 -0700 Subject: [PATCH 004/334] Myk rendered help (#2240) * implement help db * add initial list of tags * read tags fr help, read script short desc, filter --- docs/Tags.rst | 14 ++ index.rst | 1 + library/LuaApi.cpp | 76 ++++++++ library/lua/helpdb.lua | 410 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 501 insertions(+) create mode 100644 docs/Tags.rst create mode 100644 library/lua/helpdb.lua diff --git a/docs/Tags.rst b/docs/Tags.rst new file mode 100644 index 000000000..effc0ad23 --- /dev/null +++ b/docs/Tags.rst @@ -0,0 +1,14 @@ +Tags +==== + +This is the list of tags and their descriptions. + +- adventure: tools relevant to adventure mode +- fort: tools relevant to fort mode +- legends: tools relevant to legends mode +- enable: tools that are able to be enabled/disabled for some persistent effect +- items: tools that create or modify in-game items +- units: tools that create or modify units +- jobs: tools that create or modify jobs +- labors: tools that deal with labor assignment +- auto: tools that automatically manage some aspect of your fortress diff --git a/index.rst b/index.rst index 63ab02a86..3e761bbc6 100644 --- a/index.rst +++ b/index.rst @@ -32,6 +32,7 @@ User Manual /docs/Core /docs/Plugins /docs/Scripts + /docs/Tags /docs/Tools /docs/guides/index /docs/index-about diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 101b645fd..fb557329d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3023,6 +3023,79 @@ static int internal_findScript(lua_State *L) return 1; } +static int internal_listPlugins(lua_State *L) +{ + auto plugins = Core::getInstance().getPluginManager(); + + int i = 1; + lua_newtable(L); + for (auto it = plugins->begin(); it != plugins->end(); ++it) + { + lua_pushinteger(L, i++); + lua_pushstring(L, it->first.c_str()); + lua_settable(L, -3); + } + return 1; +} + +static int internal_listCommands(lua_State *L) +{ + auto plugins = Core::getInstance().getPluginManager(); + + const char *name = luaL_checkstring(L, 1); + + auto plugin = plugins->getPluginByName(name); + if (!plugin) + { + lua_pushnil(L); + return 1; + } + + size_t num_commands = plugin->size(); + lua_newtable(L); + for (size_t i = 0; i < num_commands; ++i) + { + lua_pushinteger(L, i + 1); + lua_pushstring(L, (*plugin)[i].name.c_str()); + lua_settable(L, -3); + } + return 1; +} +static int internal_getCommandHelp(lua_State *L) +{ + auto plugins = Core::getInstance().getPluginManager(); + + const char *name = luaL_checkstring(L, 1); + + auto plugin = plugins->getPluginByCommand(name); + if (!plugin) + { + lua_pushnil(L); + return 1; + } + + size_t num_commands = plugin->size(); + for (size_t i = 0; i < num_commands; ++i) + { + if ((*plugin)[i].name == name) + { + const auto &pc = (*plugin)[i]; + std::string help = pc.description; + if (help.size() && help[help.size()-1] != '.') + { + help += "."; + } + help += "\n" + pc.usage; + lua_pushstring(L, help.c_str()); + return 1; + } + } + + // not found (somehow) + lua_pushnil(L); + return 1; +} + static int internal_threadid(lua_State *L) { std::stringstream ss; @@ -3094,6 +3167,9 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "removeScriptPath", internal_removeScriptPath }, { "getScriptPaths", internal_getScriptPaths }, { "findScript", internal_findScript }, + { "listPlugins", internal_listPlugins }, + { "listCommands", internal_listCommands }, + { "getCommandHelp", internal_getCommandHelp }, { "threadid", internal_threadid }, { "md5File", internal_md5file }, { NULL, NULL } diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua new file mode 100644 index 000000000..cc8665aa1 --- /dev/null +++ b/library/lua/helpdb.lua @@ -0,0 +1,410 @@ +-- The help text database. +-- +-- Command help is read from the the following sources: +-- 1. rendered text in hack/docs/docs/ +-- 2. (for scripts) the script sources if no pre-rendered text exists or if the +-- script file has a modification time that is more recent than the +-- pre-rendered text +-- 3. (for plugins) the string passed to the PluginCommand initializer if no +-- pre-rendered text exists +-- +-- For plugins that don't register any commands, the plugin name serves as the +-- command so documentation on what happens when you enable the plugin can be +-- found. + +local _ENV = mkmodule('helpdb') + +local RENDERED_PATH = 'hack/docs/docs/tools/' +local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' + +local SCRIPT_DOC_BEGIN = '[====[' +local SCRIPT_DOC_END = ']====]' + +local SOURCES = { + STUB='stub', + RENDERED='rendered', + PLUGIN='plugin', + SCRIPT='script', +} + +-- command name -> {short_help, long_help, tags, source, source_timestamp} +-- also includes a script_source_path element if the source is a script +-- and a unrunnable boolean if the source is a plugin that does not provide any +-- commands to invoke directly. +db = db or {} + +-- tag name -> list of command names +tag_index = tag_index or {} + +local function get_rendered_path(command) + return RENDERED_PATH .. command .. '.txt' +end + +local function has_rendered_help(command) + return dfhack.filesystem.mtime(get_rendered_path(command)) ~= -1 +end + +local DEFAULT_HELP_TEMPLATE = [[ +%s +%s + +Tags: None + +No help available. +]] + +local function make_default_entry(command, source) + local default_long_help = DEFAULT_HELP_TEMPLATE:format( + command, ('*'):rep(#command)) + return {short_help='No help available.', long_help=default_long_help, + tags={}, source=source, source_timestamp=0} +end + +-- updates the short_text, the long_text, and the tags in the given entry +local function update_entry(entry, iterator, opts) + opts = opts or {} + local lines = {} + local begin_marker_found,header_found = not opts.begin_marker,opts.no_header + local tags_found, short_help_found, in_short_help = false, false, false + for line in iterator do + if not short_help_found and opts.first_line_is_short_help then + local _,_,text = line:trim():find('^%-%-%s*(.*)') + if text[#text] ~= '.' then + text = text .. '.' + end + entry.short_help = text + short_help_found = true + goto continue + end + if not begin_marker_found then + local _, endpos = line:find(opts.begin_marker, 1, true) + if endpos == #line then + begin_marker_found = true + end + goto continue + end + if opts.end_marker then + local _, endpos = line:find(opts.end_marker, 1, true) + if endpos == #line then + break + end + end + if not header_found and line:find('%w') then + header_found = true + elseif not tags_found and line:find('^Tags: [%w, ]+$') then + -- tags must appear before the help text begins + local _,_,tags = line:trim():find('Tags: (.*)') + entry.tags = tags:split('[ ,]+') + table.sort(entry.tags) + tags_found = true + elseif not short_help_found and not line:find('^Keybinding:') and + line:find('%w') then + if in_short_help then + entry.short_help = entry.short_help .. ' ' .. line + else + entry.short_help = line + end + local sentence_end = entry.short_help:find('.', 1, true) + if sentence_end then + entry.short_help = entry.short_help:sub(1, sentence_end) + short_help_found = true + else + in_short_help = true + end + end + table.insert(lines, line) + ::continue:: + end + entry.long_help = table.concat(lines, '\n') +end + +local function make_rendered_entry(old_entry, command) + local rendered_path = get_rendered_path(command) + local source_timestamp = dfhack.filesystem.mtime(rendered_path) + if old_entry and old_entry.source == SOURCES.RENDERED and + old_entry.source_timestamp >= source_timestamp then + -- we already have the latest info + return old_entry + end + local entry = make_default_entry(command, SOURCES.RENDERED) + update_entry(entry, io.lines(rendered_path)) + entry.source_timestamp = source_timestamp + return entry +end + +local function make_plugin_entry(old_entry, command) + if old_entry and old_entry.source == SOURCES.PLUGIN then + -- we can't tell when a plugin is reloaded, so we can either choose to + -- always refresh or never refresh. let's go with never for now for + -- performance. + return old_entry + end + local entry = make_default_entry(command, SOURCES.PLUGIN) + local long_help = dfhack.internal.getCommandHelp(command) + if long_help and #long_help:trim() > 0 then + update_entry(entry, long_help:trim():gmatch('[^\n]*'), {no_header=true}) + end + return entry +end + +local function make_script_entry(old_entry, command, script_source_path) + local source_timestamp = dfhack.filesystem.mtime(script_source_path) + if old_entry and old_entry.source == SOURCES.SCRIPT and + old_entry.script_source_path == script_source_path and + old_entry.source_timestamp >= source_timestamp then + -- we already have the latest info + return old_entry + end + local entry = make_default_entry(command, SOURCES.SCRIPT) + update_entry(entry, io.lines(script_source_path), + {begin_marker=SCRIPT_DOC_BEGIN, end_marker=SCRIPT_DOC_END, + first_line_is_short_help=true}) + entry.source_timestamp = source_timestamp + return entry +end + +local function update_db(old_db, db, source, command, flags) + if db[command] then + -- already in db (e.g. from a higher-priority script dir); skip + return + end + local entry, old_entry = nil, old_db[command] + if source == SOURCES.RENDERED then + entry = make_rendered_entry(old_entry, command) + elseif source == SOURCES.PLUGIN then + entry = make_plugin_entry(old_entry, command) + elseif source == SOURCES.SCRIPT then + entry = make_script_entry(old_entry, command, flags.script_source) + elseif source == SOURCES.STUB then + entry = make_default_entry(command, SOURCES.STUB) + else + error('unhandled help source: ' .. source) + end + + entry.unrunnable = (flags or {}).unrunnable + + db[command] = entry + for _,tag in ipairs(entry.tags) do + -- unknown tags are ignored + if tag_index[tag] then + table.insert(tag_index[tag], command) + end + end +end + +local function scan_plugins(old_db, db) + local plugin_names = dfhack.internal.listPlugins() + for _,plugin in ipairs(plugin_names) do + local commands = dfhack.internal.listCommands(plugin) + if #commands == 0 then + -- use plugin name as the command so we have something to anchor the + -- documentation to + update_db(old_db, db, + has_rendered_help(plugin) and + SOURCES.RENDERED or SOURCES.STUB, + plugin, {unrunnable=true}) + goto continue + end + for _,command in ipairs(commands) do + update_db(old_db, db, + has_rendered_help(command) and + SOURCES.RENDERED or SOURCES.PLUGIN, + command) + end + ::continue:: + end +end + +local function scan_scripts(old_db, db) + for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do + local files = dfhack.filesystem.listdir_recursive( + script_path, nil, false) + if not files then goto skip_path end + for _,f in ipairs(files) do + if f.isdir or not f.path:endswith('.lua') or + f.path:startswith('test/') or + f.path:startswith('internal/') then + goto continue + end + local script_source = script_path .. '/' .. f.path + local script_is_newer = dfhack.filesystem.mtime(script_source) > + dfhack.filesystem.mtime(get_rendered_path(f.path)) + update_db(old_db, db, + script_is_newer and SOURCES.SCRIPT or SOURCES.RENDERED, + f.path:sub(1, #f.path - 4), {script_source=script_source}) + ::continue:: + end + ::skip_path:: + end +end + +local function initialize_tags() + local tag, desc, in_desc = nil, nil, false + for line in io.lines(TAG_DEFINITIONS) do + if in_desc then + line = line:trim() + if #line == 0 then + in_desc = false + goto continue + end + desc = desc .. ' ' .. line + tag_index[tag].description = desc + else + _,_,tag,desc = line:find('^%* (%w+): (.+)') + if not tag then goto continue end + tag_index[tag] = {description=desc} + in_desc = true + end + ::continue:: + end +end + +-- ensures the db is up to date by scanning all help sources. does not do +-- anything if it has already been run within the last second. +last_refresh_ms = last_refresh_ms or 0 +local function ensure_db() + local now_ms = dfhack.getTickCount() + if now_ms - last_refresh_ms < 1000 then return end + last_refresh_ms = now_ms + + local old_db = db + db, tag_index = {}, {} + + initialize_tags() + scan_plugins(old_db, db) + scan_scripts(old_db, db) +end + +local function get_db_property(command, property) + ensure_db() + if not db[command] then + error(('command not found: "%s"'):format(command)) + end + return db[command][property] +end + +-- returns the ~54 char summary blurb associated with the entry +function get_entry_short_help(entry) + return get_db_property(entry, 'short_help') +end + +-- returns the full help documentation associated with the entry +function get_entry_long_help(entry) + return get_db_property(entry, 'long_help') +end + +-- returns the list of tags associated with the entry +function get_entry_tags(entry) + return get_db_property(entry, 'tags') +end + +local function chunk_for_sorting(str) + local parts = str:split('/') + local chunks = {} + for i=1,#parts do + chunks[#parts - i + 1] = parts[i] + end + return chunks +end + +-- sorts by last path component, then by parent path components. +-- something comes before nothing. +-- e.g. gui/autofarm comes immediately before autofarm +local function sort_by_basename(a, b) + local a = chunk_for_sorting(a) + local b = chunk_for_sorting(b) + local i = 1 + while a[i] do + if not b[i] then + return true + end + if a[i] ~= b[i] then + return a[i] < b[i] + end + i = i + 1 + end + return false +end + +local function add_if_matched(commands, command, strs, runnable) + if runnable and db[command].unrunnable then + return + end + if strs then + local matched = false + for _,str in ipairs(strs) do + if command:find(str, 1, true) then + matched = true + break + end + end + if not matched then + return + end + end + table.insert(commands, command) +end + +-- returns a list of identifiers, alphabetized by their last path component +-- (e.g. gui/autobutcher will immediately follow autobutcher). +-- the optional filter element is a map with the following elements: +-- str - if a string, filters by the given substring. if a table of strings, +-- includes commands that match any of the given substrings. +-- tag - if a string, filters by the given tag name. if a table of strings, +-- includes commands that match any of the given tags. +-- runnable - if true, filters out plugin names that do no correspond to +-- runnable commands. +function list_entries(filter) + ensure_db() + filter = filter or {} + local commands = {} + local strs = filter.str + if filter.str then + if type(strs) == 'string' then strs = {strs} end + end + local runnable = filter.runnable + if not filter.tag then + for command in pairs(db) do + add_if_matched(commands, command, strs, runnable) + end + else + local command_set = {} + local tags = filter.tag + if type(tags) == 'string' then tags = {tags} end + for _,tag in ipairs(tags) do + if not tag_index[tag] then + error('invalid tag: ' .. tag) + end + for _,command in ipairs(tag_index[tag]) do + command_set[command] = true + end + end + for command in pairs(command_set) do + add_if_matched(commands, command, strs, runnable) + end + end + table.sort(commands, sort_by_basename) + return commands +end + +-- returns the defined tags in alphabetical order +function list_tags() + ensure_db() + local tags = {} + for tag in pairs(tag_index) do + table.insert(tags, tag) + end + table.sort(tags) + return tags +end + +-- returns the description associated with the given tag +function get_tag_description(tag) + ensure_db() + if not tag_index[tag] then + error('invalid tag: ' .. tag) + end + return tag_index[tag].description +end + +return _ENV From 35a4d19ac9efce6081091fa5a63a21a3a192a146 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 6 Jul 2022 22:26:06 -0700 Subject: [PATCH 005/334] implement listing functions for helpdb --- library/lua/helpdb.lua | 152 +++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 50 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index cc8665aa1..8a27abad8 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -69,7 +69,7 @@ local function update_entry(entry, iterator, opts) for line in iterator do if not short_help_found and opts.first_line_is_short_help then local _,_,text = line:trim():find('^%-%-%s*(.*)') - if text[#text] ~= '.' then + if not text:endswith('.') then text = text .. '.' end entry.short_help = text @@ -92,10 +92,11 @@ local function update_entry(entry, iterator, opts) if not header_found and line:find('%w') then header_found = true elseif not tags_found and line:find('^Tags: [%w, ]+$') then - -- tags must appear before the help text begins local _,_,tags = line:trim():find('Tags: (.*)') - entry.tags = tags:split('[ ,]+') - table.sort(entry.tags) + entry.tags = {} + for _,tag in ipairs(tags:split('[ ,]+')) do + entry.tags[tag] = true + end tags_found = true elseif not short_help_found and not line:find('^Keybinding:') and line:find('%w') then @@ -260,11 +261,11 @@ local function initialize_tags() end -- ensures the db is up to date by scanning all help sources. does not do --- anything if it has already been run within the last second. +-- anything if it has already been run within the last 10 seconds. last_refresh_ms = last_refresh_ms or 0 local function ensure_db() local now_ms = dfhack.getTickCount() - if now_ms - last_refresh_ms < 1000 then return end + if now_ms - last_refresh_ms < 60000 then return end last_refresh_ms = now_ms local old_db = db @@ -293,9 +294,18 @@ function get_entry_long_help(entry) return get_db_property(entry, 'long_help') end --- returns the list of tags associated with the entry +local function set_to_sorted_list(set) + local list = {} + for item in pairs(set) do + table.insert(list, item) + end + table.sort(list) + return list +end + +-- returns the list of tags associated with the entry, in alphabetical order function get_entry_tags(entry) - return get_db_property(entry, 'tags') + return set_to_sorted_list(get_db_property(entry, 'tags')) end local function chunk_for_sorting(str) @@ -326,76 +336,109 @@ local function sort_by_basename(a, b) return false end -local function add_if_matched(commands, command, strs, runnable) - if runnable and db[command].unrunnable then - return +local function matches(command, filter) + local db_entry = db[command] + if filter.runnable and db_entry.unrunnable then + return false + end + if filter.tag then + local matched = false + for _,tag in ipairs(filter.tag) do + if db_entry.tags[tag] then + matched = true + break + end + end + if not matched then + return false + end end - if strs then + if filter.str then local matched = false - for _,str in ipairs(strs) do + for _,str in ipairs(filter.str) do if command:find(str, 1, true) then matched = true break end end if not matched then - return + return false end end - table.insert(commands, command) + return true +end + +local function normalize_string_list(l) + if not l then return nil end + if type(l) == 'string' then + return {l} + end + return l +end + +local function normalize_filter(f) + if not f then return nil end + local filter = {} + filter.str = normalize_string_list(f.str) + filter.tag = normalize_string_list(f.tag) + filter.runnable = f.runnable + if not filter.str and not filter.tag and not filter.runnable then + return nil + end + return filter end -- returns a list of identifiers, alphabetized by their last path component -- (e.g. gui/autobutcher will immediately follow autobutcher). --- the optional filter element is a map with the following elements: +-- the optional include and exclude filter params are maps with the following +-- elements: -- str - if a string, filters by the given substring. if a table of strings, -- includes commands that match any of the given substrings. -- tag - if a string, filters by the given tag name. if a table of strings, -- includes commands that match any of the given tags. --- runnable - if true, filters out plugin names that do no correspond to --- runnable commands. -function list_entries(filter) +-- runnable - if true, matches only runnable commands, not plugin names. +function get_entries(include, exclude) ensure_db() - filter = filter or {} + include = normalize_filter(include) + exclude = normalize_filter(exclude) local commands = {} - local strs = filter.str - if filter.str then - if type(strs) == 'string' then strs = {strs} end - end - local runnable = filter.runnable - if not filter.tag then - for command in pairs(db) do - add_if_matched(commands, command, strs, runnable) - end - else - local command_set = {} - local tags = filter.tag - if type(tags) == 'string' then tags = {tags} end - for _,tag in ipairs(tags) do - if not tag_index[tag] then - error('invalid tag: ' .. tag) - end - for _,command in ipairs(tag_index[tag]) do - command_set[command] = true - end - end - for command in pairs(command_set) do - add_if_matched(commands, command, strs, runnable) + for command in pairs(db) do + if (not include or matches(command, include)) and + (not exclude or not matches(command, exclude)) then + table.insert(commands, command) end end table.sort(commands, sort_by_basename) return commands end +local function get_max_width(list, min_width) + local width = min_width or 0 + for _,item in ipairs(list) do + width = math.max(width, #item) + end + return width +end + +-- prints the requested entries to the console. include and exclude filters are +-- as in get_entries above. +function list_entries(include_tags, include, exclude) + local entries = get_entries(include, exclude) + local width = get_max_width(entries, 10) + for _,entry in ipairs(entries) do + print((' %-'..width..'s %s'):format( + entry, get_entry_short_help(entry))) + if include_tags then + print((' '..(' '):rep(width)..' tags(%s)'):format( + table.concat(get_entry_tags(entry), ','))) + end + end +end + -- returns the defined tags in alphabetical order -function list_tags() +function get_tags() ensure_db() - local tags = {} - for tag in pairs(tag_index) do - table.insert(tags, tag) - end - table.sort(tags) - return tags + return set_to_sorted_list(tag_index) end -- returns the description associated with the given tag @@ -407,4 +450,13 @@ function get_tag_description(tag) return tag_index[tag].description end +-- prints the defined tags and their descriptions to the console +function list_tags() + local tags = get_tags() + local width = get_max_width(tags, 10) + for _,tag in ipairs(tags) do + print((' %-'..width..'s %s'):format(tag, get_tag_description(tag))) + end +end + return _ENV From 4ad8e7199a691f900cac1824d0f746924eb748c4 Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 8 Jul 2022 14:43:41 -0700 Subject: [PATCH 006/334] Support builtin commands in helpdb (#2241) * support builtin commands in helpdb, implement list API, document api --- docs/Builtin.rst | 269 +++++++++++++++++++++++++++ docs/Core.rst | 264 +-------------------------- index.rst | 1 + library/lua/helpdb.lua | 405 ++++++++++++++++++++++++++++------------- 4 files changed, 559 insertions(+), 380 deletions(-) create mode 100644 docs/Builtin.rst diff --git a/docs/Builtin.rst b/docs/Builtin.rst new file mode 100644 index 000000000..2d938e30f --- /dev/null +++ b/docs/Builtin.rst @@ -0,0 +1,269 @@ +.. _built-in-commands: + +Built-in Commands +================= +The following commands are provided by the 'core' components of DFHack, rather +than plugins or scripts. + +.. contents:: + :local: + +.. _alias: + +alias +----- +The ``alias`` command allows configuring aliases to other DFHack commands. +Aliases are resolved immediately after built-in commands, which means that an +alias cannot override a built-in command, but can override a command implemented +by a plugin or script. + +Usage: + +:``alias list``: lists all configured aliases +:``alias add [arguments...]``: adds an alias +:``alias replace [arguments...]``: replaces an existing + alias with a new command, or adds the alias if it does not already exist +:``alias delete ``: removes the specified alias + +Aliases can be given additional arguments when created and invoked, which will +be passed to the underlying command in order. An example with +`devel/print-args`:: + + [DFHack]# alias add pargs devel/print-args example + [DFHack]# pargs text + example + text + + +.. _cls: + +cls +--- +Clear the terminal. Does not delete command history. + + +.. _die: + +die +--- +Instantly kills DF without saving. + + +.. _disable: +.. _enable: + +enable +------ +Many plugins and scripts can be in a distinct enabled or disabled state. Some of +them activate and deactivate automatically depending on the contents of the +world raws. Others store their state in world data. However a number of them +have to be enabled globally, and the init file is the right place to do it. + +Most such plugins or scripts support the built-in ``enable`` and ``disable`` +commands. Calling them at any time without arguments prints a list of enabled +and disabled plugins, and shows whether that can be changed through the same +commands. Passing plugin names to these commands will enable or disable the +specified plugins. For example, to enable the `manipulator` plugin:: + + enable manipulator + +It is also possible to enable or disable multiple plugins at once:: + + enable manipulator search + + +.. _fpause: + +fpause +------ +Forces DF to pause. This is useful when your FPS drops below 1 and you lose +control of the game. + + +.. _help: + +help +---- +Most commands support using the ``help `` built-in command to retrieve +further help without having to look online. ``? `` and ``man `` are +aliases. + +Some commands (including many scripts) instead take ``help`` or ``?`` as an +option on their command line - ie `` help``. + + +.. _hide: + +hide +---- +Hides the DFHack terminal window. Only available on Windows. + + +.. _keybinding: + +keybinding +---------- +To set keybindings, use the built-in ``keybinding`` command. Like any other +command it can be used at any time from the console, but bindings are not +remembered between runs of the game unless re-created in `dfhack.init`. + +Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, or F1-F12 are +supported. + +Possible ways to call the command: + +``keybinding list `` + List bindings active for the key combination. +``keybinding clear ...`` + Remove bindings for the specified keys. +``keybinding add "cmdline" "cmdline"...`` + Add bindings for the specified key. +``keybinding set "cmdline" "cmdline"...`` + Clear, and then add bindings for the specified key. + +The ```` parameter above has the following *case-sensitive* syntax:: + + [Ctrl-][Alt-][Shift-]KEY[@context[|context...]] + +where the *KEY* part can be any recognized key and [] denote optional parts. + +When multiple commands are bound to the same key combination, DFHack selects +the first applicable one. Later ``add`` commands, and earlier entries within one +``add`` command have priority. Commands that are not specifically intended for +use as a hotkey are always considered applicable. + +The ``context`` part in the key specifier above can be used to explicitly +restrict the UI state where the binding would be applicable. If called without +parameters, the ``keybinding`` command among other things prints the current +context string. + +Only bindings with a ``context`` tag that either matches the current context +fully, or is a prefix ending at a ``/`` boundary would be considered for +execution, i.e. when in context ``foo/bar/baz``, keybindings restricted to any +of ``@foo/bar/baz``, ``@foo/bar``, ``@foo`` or none will be active. + +Multiple contexts can be specified by separating them with a pipe (``|``) - for +example, ``@foo|bar|baz/foo`` would match anything under ``@foo``, ``@bar``, or +``@baz/foo``. + +Interactive commands like `liquids` cannot be used as hotkeys. + + +.. _kill-lua: + +kill-lua +-------- +Stops any currently-running Lua scripts. By default, scripts can only be +interrupted every 256 instructions. Use ``kill-lua force`` to interrupt the next +instruction. + + +.. _load: +.. _unload: +.. _reload: + +load +---- +``load``, ``unload``, and ``reload`` control whether a plugin is loaded into +memory - note that plugins are loaded but disabled unless you explicitly enable +them. Usage:: + + load|unload|reload PLUGIN|(-a|--all) + +Allows dealing with plugins individually by name, or all at once. + +Note that plugins do not maintain their enabled state if they are reloaded, so +you may need to use `enable` to re-enable a plugin after reloading it. + + +.. _ls: +.. _dir: + +ls +-- +``ls`` (or ``dir``) does not list files like the Unix command, but rather +available commands. In order to group related commands, each command is +associated with a list of tags. You can filter the listed commands by a +tag or a substring of the command name. Usage: + +:``ls``: Lists all available commands and the tags associated with them + (if any). +:``ls TAG``: Shows only commands that have the given tag. Use the `tags` command + to see the list of available tags. +:``ls STRING``: Shows commands that include the given string. E.g. ``ls auto`` + will show all the commands with "auto" in their names. If the string is also + the name of a tag, then it will be interpreted as a tag name. + +You can also pass some optional parameters to change how ``ls`` behaves: + +:``--notags``: Don't print out the tags associated with each command. +:``--dev``: Include commands intended for developers and modders. + + +.. _plug: + +plug +---- +Lists available plugins and whether they are enabled. + +``plug`` + Lists available plugins (*not* commands implemented by plugins) +``plug [PLUGIN] [PLUGIN] ...`` + List state and detailed description of the given plugins, + including commands implemented by the plugin. + + +.. _sc-script: + +sc-script +--------- +Allows additional scripts to be run when certain events occur (similar to +onLoad\*.init scripts) + + +.. _script: + +script +------ +Reads a text file, and runs each line as a DFHack command as if it had been +typed in by the user - treating the input like `an init file `. + +Some other tools, such as `autobutcher` and `workflow`, export their settings as +the commands to create them - which can later be reloaded with ``script``. + + +.. _show: + +show +---- +Shows the terminal window after it has been `hidden `. Only available on +Windows. You'll need to use it from a `keybinding` set beforehand, or the +in-game `command-prompt`. + + +.. _tags: + +tags +---- + +List the strings that the DFHack tools can be tagged with. You can find groups +of related tools by passing the tag name to `ls`. + +.. _type: + +type +---- +``type command`` shows where ``command`` is implemented. + +Other Commands +-------------- +The following commands are *not* built-in, but offer similarly useful functions. + +* `command-prompt` +* `hotkeys` +* `lua` +* `multicmd` +* `nopause` +* `quicksave` +* `rb` +* `repeat` diff --git a/docs/Core.rst b/docs/Core.rst index fe881a54d..ffa620f55 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -11,22 +11,23 @@ DFHack Core Command Implementation ====================== -DFHack commands can be implemented in three ways, all of which -are used in the same way: +DFHack commands can be implemented in any of three ways: :builtin: commands are implemented by the core of DFHack. They manage other DFHack tools, interpret commands, and control basic - aspects of DF (force pause or quit). + aspects of DF (force pause or quit). They are documented + `here `. :plugins: are stored in ``hack/plugins/`` and must be compiled with the same version of DFHack. They are less flexible than scripts, but used for complex or ongoing tasks because they run faster. + Plugins included with DFHack are documented `here `. :scripts: are Ruby or Lua scripts stored in ``hack/scripts/``. Because they don't need to be compiled, scripts are more flexible about versions, and easier to distribute. - Most third-party DFHack addons are scripts. - + Most third-party DFHack addons are scripts. All scripts included + with DFHack are documented `here `. Using DFHack Commands ===================== @@ -113,259 +114,6 @@ second (Windows) example uses `kill-lua` to stop a Lua script. you have multiple copies of DF running simultaneously. To assign a different port, see `remote-server-config`. - -Built-in Commands -================= -The following commands are provided by the 'core' components -of DFHack, rather than plugins or scripts. - -.. contents:: - :local: - - -.. _alias: - -alias ------ -The ``alias`` command allows configuring aliases to other DFHack commands. -Aliases are resolved immediately after built-in commands, which means that an -alias cannot override a built-in command, but can override a command implemented -by a plugin or script. - -Usage: - -:``alias list``: lists all configured aliases -:``alias add [arguments...]``: adds an alias -:``alias replace [arguments...]``: replaces an existing - alias with a new command, or adds the alias if it does not already exist -:``alias delete ``: removes the specified alias - -Aliases can be given additional arguments when created and invoked, which will -be passed to the underlying command in order. An example with `devel/print-args`:: - - [DFHack]# alias add pargs devel/print-args example - [DFHack]# pargs text - example - text - - -.. _cls: - -cls ---- -Clear the terminal. Does not delete command history. - - -.. _die: - -die ---- -Instantly kills DF without saving. - - -.. _disable: - -.. _enable: - -enable ------- -Many plugins can be in a distinct enabled or disabled state. Some of -them activate and deactivate automatically depending on the contents -of the world raws. Others store their state in world data. However a -number of them have to be enabled globally, and the init file is the -right place to do it. - -Most such plugins or scripts support the built-in ``enable`` and ``disable`` -commands. Calling them at any time without arguments prints a list -of enabled and disabled plugins, and shows whether that can be changed -through the same commands. Passing plugin names to these commands will enable -or disable the specified plugins. For example, to enable the `manipulator` -plugin:: - - enable manipulator - -It is also possible to enable or disable multiple plugins at once:: - - enable manipulator search - - -.. _fpause: - -fpause ------- -Forces DF to pause. This is useful when your FPS drops below 1 and you lose -control of the game. - - -.. _help: - -help ----- -Most commands support using the ``help `` built-in command -to retrieve further help without having to look at this document. -``? `` and ``man `` are aliases. - -Some commands (including many scripts) instead take ``help`` or ``?`` -as an option on their command line - ie `` help``. - - -.. _hide: - -hide ----- -Hides the DFHack terminal window. Only available on Windows. - - -.. _keybinding: - -keybinding ----------- -To set keybindings, use the built-in ``keybinding`` command. Like any other -command it can be used at any time from the console, but bindings are not -remembered between runs of the game unless re-created in `dfhack.init`. - -Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, or F1-F12 are supported. - -Possible ways to call the command: - -``keybinding list `` - List bindings active for the key combination. -``keybinding clear ...`` - Remove bindings for the specified keys. -``keybinding add "cmdline" "cmdline"...`` - Add bindings for the specified key. -``keybinding set "cmdline" "cmdline"...`` - Clear, and then add bindings for the specified key. - -The ```` parameter above has the following *case-sensitive* syntax:: - - [Ctrl-][Alt-][Shift-]KEY[@context[|context...]] - -where the *KEY* part can be any recognized key and [] denote optional parts. - -When multiple commands are bound to the same key combination, DFHack selects -the first applicable one. Later ``add`` commands, and earlier entries within one -``add`` command have priority. Commands that are not specifically intended for use -as a hotkey are always considered applicable. - -The ``context`` part in the key specifier above can be used to explicitly restrict -the UI state where the binding would be applicable. If called without parameters, -the ``keybinding`` command among other things prints the current context string. - -Only bindings with a ``context`` tag that either matches the current context fully, -or is a prefix ending at a ``/`` boundary would be considered for execution, i.e. -when in context ``foo/bar/baz``, keybindings restricted to any of ``@foo/bar/baz``, -``@foo/bar``, ``@foo`` or none will be active. - -Multiple contexts can be specified by separating them with a -pipe (``|``) - for example, ``@foo|bar|baz/foo`` would match -anything under ``@foo``, ``@bar``, or ``@baz/foo``. - -Interactive commands like `liquids` cannot be used as hotkeys. - - -.. _kill-lua: - -kill-lua --------- -Stops any currently-running Lua scripts. By default, scripts can -only be interrupted every 256 instructions. Use ``kill-lua force`` -to interrupt the next instruction. - - -.. _load: -.. _unload: -.. _reload: - -load ----- -``load``, ``unload``, and ``reload`` control whether a plugin is loaded -into memory - note that plugins are loaded but disabled unless you do -something. Usage:: - - load|unload|reload PLUGIN|(-a|--all) - -Allows dealing with plugins individually by name, or all at once. - -Note that plugins do not maintain their enabled state if they are reloaded, so -you may need to use `enable` to re-enable a plugin after reloading it. - - -.. _ls: - -ls --- -``ls`` does not list files like the Unix command, but rather -available commands - first built in commands, then plugins, -and scripts at the end. Usage: - -:ls -a: Also list scripts in subdirectories of ``hack/scripts/``, - which are generally not intended for direct use. -:ls : List subcommands for the given plugin. - - -.. _plug: - -plug ----- -Lists available plugins, including their state and detailed description. - -``plug`` - Lists available plugins (*not* commands implemented by plugins) -``plug [PLUGIN] [PLUGIN] ...`` - List state and detailed description of the given plugins, - including commands implemented by the plugin. - - -.. _sc-script: - -sc-script ---------- -Allows additional scripts to be run when certain events occur -(similar to onLoad*.init scripts) - - -.. _script: - -script ------- -Reads a text file, and runs each line as a DFHack command -as if it had been typed in by the user - treating the -input like `an init file `. - -Some other tools, such as `autobutcher` and `workflow`, export -their settings as the commands to create them - which are later -loaded with ``script`` - - -.. _show: - -show ----- -Shows the terminal window after it has been `hidden `. -Only available on Windows. You'll need to use it from a -`keybinding` set beforehand, or the in-game `command-prompt`. - -.. _type: - -type ----- -``type command`` shows where ``command`` is implemented. - -Other Commands --------------- -The following commands are *not* built-in, but offer similarly useful functions. - -* `command-prompt` -* `hotkeys` -* `lua` -* `multicmd` -* `nopause` -* `quicksave` -* `rb` -* `repeat` - - .. _dfhack-config: Configuration Files diff --git a/index.rst b/index.rst index 3e761bbc6..3b46699a7 100644 --- a/index.rst +++ b/index.rst @@ -30,6 +30,7 @@ User Manual /docs/Installing /docs/Support /docs/Core + /docs/Builtin /docs/Plugins /docs/Scripts /docs/Tags diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 8a27abad8..2999ee67e 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -1,80 +1,122 @@ --- The help text database. +-- The help text database and query interface. -- --- Command help is read from the the following sources: --- 1. rendered text in hack/docs/docs/ --- 2. (for scripts) the script sources if no pre-rendered text exists or if the --- script file has a modification time that is more recent than the --- pre-rendered text --- 3. (for plugins) the string passed to the PluginCommand initializer if no --- pre-rendered text exists +-- Help text is read from the rendered text in hack/docs/docs/. If no rendered +-- text exists, it is read from the script sources (for scripts) or the string +-- passed to the PluginCommand initializer (for plugins). -- --- For plugins that don't register any commands, the plugin name serves as the --- command so documentation on what happens when you enable the plugin can be --- found. +-- For plugins that don't register a command with the same name as the plugin, +-- the plugin name is registered as a separate entry so documentation on what +-- happens when you enable the plugin can be found. +-- +-- The database is lazy-loaded when an API method is called. It rechecks its +-- help sources for updates if an API method has not been called in the last +-- 60 seconds. local _ENV = mkmodule('helpdb') +-- paths local RENDERED_PATH = 'hack/docs/docs/tools/' +local BUILTIN_HELP = 'hack/docs/docs/Builtin.txt' local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' +-- used when reading help text embedded in script sources local SCRIPT_DOC_BEGIN = '[====[' local SCRIPT_DOC_END = ']====]' +local SCRIPT_DOC_BEGIN_RUBY = '=begin' +local SCRIPT_DOC_END_RUBY = '=end' + +local ENTRY_TYPES = { + BUILTIN='builtin', + PLUGIN='plugin', + COMMAND='command' +} -local SOURCES = { +local HELP_SOURCES = { STUB='stub', RENDERED='rendered', PLUGIN='plugin', SCRIPT='script', } --- command name -> {short_help, long_help, tags, source, source_timestamp} --- also includes a script_source_path element if the source is a script --- and a unrunnable boolean if the source is a plugin that does not provide any --- commands to invoke directly. +-- entry name -> { +-- entry_types (set of ENTRY_TYPES), +-- short_help (string), +-- long_help (string), +-- tags (set), +-- help_source (element of HELP_SOURCES), +-- source_timestamp (mtime, 0 for non-files), +-- source_path (string, nil for non-files) +-- } +-- +-- entry_types is a set because plugin commands can also be the plugin names. db = db or {} --- tag name -> list of command names +-- tag name -> list of entry names +-- Tags defined in the TAG_DEFINITIONS file that have no associated db entries +-- will have an empty list. tag_index = tag_index or {} -local function get_rendered_path(command) - return RENDERED_PATH .. command .. '.txt' +local function get_rendered_path(entry_name) + return RENDERED_PATH .. entry_name .. '.txt' end -local function has_rendered_help(command) - return dfhack.filesystem.mtime(get_rendered_path(command)) ~= -1 +local function has_rendered_help(entry_name) + return dfhack.filesystem.mtime(get_rendered_path(entry_name)) ~= -1 end local DEFAULT_HELP_TEMPLATE = [[ %s %s -Tags: None - No help available. ]] - -local function make_default_entry(command, source) +local function make_default_entry(entry_name, entry_types, source, + source_timestamp, source_path) local default_long_help = DEFAULT_HELP_TEMPLATE:format( - command, ('*'):rep(#command)) - return {short_help='No help available.', long_help=default_long_help, - tags={}, source=source, source_timestamp=0} + entry_name, ('*'):rep(#entry_name)) + return { + entry_types=entry_types, + short_help='No help available.', + long_help=default_long_help, + tags={}, + help_source=source, + source_timestamp=source_timestamp or 0, + source_path=source_path} end --- updates the short_text, the long_text, and the tags in the given entry +-- updates the short_text, the long_text, and the tags in the given entry based +-- on the text returned from the iterator. +-- if defined, opts can have the following fields: +-- begin_marker (string that marks the beginning of the help text; all text +-- before this marker is ignored) +-- end_marker (string that marks the end of the help text; text will stop +-- being parsed after this marker is seen) +-- no_header (don't try to find the entity name at the top of the help text) +-- first_line_is_short_help (read the short help text from the first commented +-- line of the script instead of using the first +-- sentence of the long help text) local function update_entry(entry, iterator, opts) opts = opts or {} local lines = {} + local first_line_is_short_help = opts.first_line_is_short_help local begin_marker_found,header_found = not opts.begin_marker,opts.no_header local tags_found, short_help_found, in_short_help = false, false, false for line in iterator do - if not short_help_found and opts.first_line_is_short_help then - local _,_,text = line:trim():find('^%-%-%s*(.*)') - if not text:endswith('.') then - text = text .. '.' + if not short_help_found and first_line_is_short_help then + line = line:trim() + local _,_,text = line:find('^%-%-%s*(.*)') or line:find('^#%s*(.*)') + if not text then + -- if no first-line short help found, fall back to getting the + -- first sentence of the help text. + first_line_is_short_help = false + else + if not text:endswith('.') then + text = text .. '.' + end + entry.short_help = text + short_help_found = true + goto continue end - entry.short_help = text - short_help_found = true - goto continue end if not begin_marker_found then local _, endpos = line:find(opts.begin_marker, 1, true) @@ -119,80 +161,131 @@ local function update_entry(entry, iterator, opts) entry.long_help = table.concat(lines, '\n') end -local function make_rendered_entry(old_entry, command) - local rendered_path = get_rendered_path(command) +-- create db entry based on parsing sphinx-rendered help text +local function make_rendered_entry(old_entry, entry_name, entry_types) + local rendered_path = get_rendered_path(entry_name) local source_timestamp = dfhack.filesystem.mtime(rendered_path) - if old_entry and old_entry.source == SOURCES.RENDERED and + if old_entry and old_entry.source == HELP_SOURCES.RENDERED and old_entry.source_timestamp >= source_timestamp then -- we already have the latest info return old_entry end - local entry = make_default_entry(command, SOURCES.RENDERED) + local entry = make_default_entry(entry_name, entry_types, + HELP_SOURCES.RENDERED, source_timestamp, rendered_path) update_entry(entry, io.lines(rendered_path)) - entry.source_timestamp = source_timestamp return entry end -local function make_plugin_entry(old_entry, command) - if old_entry and old_entry.source == SOURCES.PLUGIN then +-- create db entry based on the help text in the plugin source (used by +-- out-of-tree plugins) +local function make_plugin_entry(old_entry, entry_name, entry_types) + if old_entry and old_entry.source == HELP_SOURCES.PLUGIN then -- we can't tell when a plugin is reloaded, so we can either choose to -- always refresh or never refresh. let's go with never for now for -- performance. return old_entry end - local entry = make_default_entry(command, SOURCES.PLUGIN) - local long_help = dfhack.internal.getCommandHelp(command) + local entry = make_default_entry(entry_name, entry_types, + HELP_SOURCES.PLUGIN) + local long_help = dfhack.internal.getCommandHelp(entry_name) if long_help and #long_help:trim() > 0 then update_entry(entry, long_help:trim():gmatch('[^\n]*'), {no_header=true}) end return entry end -local function make_script_entry(old_entry, command, script_source_path) +-- create db entry based on the help text in the script source (used by +-- out-of-tree scripts) +local function make_script_entry(old_entry, entry_name, script_source_path) local source_timestamp = dfhack.filesystem.mtime(script_source_path) - if old_entry and old_entry.source == SOURCES.SCRIPT and + if old_entry and old_entry.source == HELP_SOURCES.SCRIPT and old_entry.script_source_path == script_source_path and old_entry.source_timestamp >= source_timestamp then -- we already have the latest info return old_entry end - local entry = make_default_entry(command, SOURCES.SCRIPT) + local entry = make_default_entry(entry_name, {[ENTRY_TYPES.COMMAND]=true}, + HELP_SOURCES.SCRIPT, source_timestamp, script_source_path) + local is_rb = script_source_path:endswith('.rb') update_entry(entry, io.lines(script_source_path), - {begin_marker=SCRIPT_DOC_BEGIN, end_marker=SCRIPT_DOC_END, - first_line_is_short_help=true}) - entry.source_timestamp = source_timestamp + {begin_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_BEGIN), + end_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_END), + first_line_is_short_help=true}) return entry end -local function update_db(old_db, db, source, command, flags) - if db[command] then +-- updates the db (and associated tag index) with a new entry if the entry_name +-- doesn't already exist in the db. +local function update_db(old_db, db, source, entry_name, kwargs) + if db[entry_name] then -- already in db (e.g. from a higher-priority script dir); skip return end - local entry, old_entry = nil, old_db[command] - if source == SOURCES.RENDERED then - entry = make_rendered_entry(old_entry, command) - elseif source == SOURCES.PLUGIN then - entry = make_plugin_entry(old_entry, command) - elseif source == SOURCES.SCRIPT then - entry = make_script_entry(old_entry, command, flags.script_source) - elseif source == SOURCES.STUB then - entry = make_default_entry(command, SOURCES.STUB) + local entry, old_entry = nil, old_db[entry_name] + if source == HELP_SOURCES.RENDERED then + entry = make_rendered_entry(old_entry, entry_name, kwargs.entry_types) + elseif source == HELP_SOURCES.PLUGIN then + entry = make_plugin_entry(old_entry, entry_name, kwargs.entry_types) + elseif source == HELP_SOURCES.SCRIPT then + entry = make_script_entry(old_entry, entry_name, kwargs.script_source) + elseif source == HELP_SOURCES.STUB then + entry = make_default_entry(entry_name, kwargs.entry_types, + HELP_SOURCES.STUB) else error('unhandled help source: ' .. source) end - - entry.unrunnable = (flags or {}).unrunnable - - db[command] = entry + db[entry_name] = entry for _,tag in ipairs(entry.tags) do - -- unknown tags are ignored + -- ignore unknown tags if tag_index[tag] then - table.insert(tag_index[tag], command) + table.insert(tag_index[tag], entry_name) end end end +local BUILTINS = { + alias='Configure helper aliases for other DFHack commands.', + cls='Clear the console screen.', + clear='Clear the console screen.', + die='Force DF to close immediately, without saving.', + enable='Enable a plugin or persistent script.', + disable='Disable a plugin or persistent script.', + fpause='Force DF to pause.', + help='Usage help for the given plugin, command, or script.', + hide='Hide the terminal window (Windows only).', + keybinding='Modify bindings of commands to in-game key shortcuts.', + ['kill-lua']='Stop a misbehaving Lua script.', + ['load']='Load and register a plugin library.', + unload='Unregister and unload a plugin.', + reload='Unload and reload a plugin library.', + ls='List commands, optionally filtered by a tag or substring.', + dir='List commands, optionally filtered by a tag or substring.', + plug='List plugins and whether they are enabled.', + ['sc-script']='Automatically run specified scripts on state change events.', + script='Run commands specified in a file.', + show='Show a hidden terminal window (Windows only).', + tags='List the tags that the DFHack tools are grouped by.', + ['type']='Discover how a command is implemented.', +} + +-- add the builtin commands to the db +local function scan_builtins(old_db, db) + local entry = make_default_entry('builtin', + {[ENTRY_TYPES.BUILTIN]=true, [ENTRY_TYPES.COMMAND]=true}, + HELP_SOURCES.RENDERED, 0, BUILTIN_HELP) + -- read in builtin help + local f = io.open(BUILTIN_HELP) + if f then + entry.long_help = f:read('*all') + end + for b,short_help in pairs(BUILTINS) do + local builtin_entry = copyall(entry) + builtin_entry.short_help = short_help + db[b] = builtin_entry + end +end + +-- scan for plugins and plugin-provided commands and add their help to the db local function scan_plugins(old_db, db) local plugin_names = dfhack.internal.listPlugins() for _,plugin in ipairs(plugin_names) do @@ -202,36 +295,41 @@ local function scan_plugins(old_db, db) -- documentation to update_db(old_db, db, has_rendered_help(plugin) and - SOURCES.RENDERED or SOURCES.STUB, - plugin, {unrunnable=true}) + HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, + plugin, {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) goto continue end for _,command in ipairs(commands) do + local entry_types = {[ENTRY_TYPES.COMMAND]=true} + if command == plugin then + entry_types[ENTRY_TYPES.PLUGIN]=true + end update_db(old_db, db, has_rendered_help(command) and - SOURCES.RENDERED or SOURCES.PLUGIN, - command) + HELP_SOURCES.RENDERED or HELP_SOURCES.PLUGIN, + command, {entry_types=entry_types}) end ::continue:: end end +-- scan for scripts and add their help to the db local function scan_scripts(old_db, db) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( script_path, nil, false) if not files then goto skip_path end for _,f in ipairs(files) do - if f.isdir or not f.path:endswith('.lua') or + if f.isdir or + (not f.path:endswith('.lua') and not f.path:endswith('.rb')) or f.path:startswith('test/') or f.path:startswith('internal/') then goto continue end local script_source = script_path .. '/' .. f.path - local script_is_newer = dfhack.filesystem.mtime(script_source) > - dfhack.filesystem.mtime(get_rendered_path(f.path)) update_db(old_db, db, - script_is_newer and SOURCES.SCRIPT or SOURCES.RENDERED, + has_rendered_help(f.path) and + HELP_SOURCES.RENDERED or HELP_SOURCES.SCRIPT, f.path:sub(1, #f.path - 4), {script_source=script_source}) ::continue:: end @@ -239,6 +337,8 @@ local function scan_scripts(old_db, db) end end +-- read tags and descriptions from the TAG_DEFINITIONS file and add them all +-- to tag_index, initizlizing each entry with an empty list. local function initialize_tags() local tag, desc, in_desc = nil, nil, false for line in io.lines(TAG_DEFINITIONS) do @@ -261,7 +361,7 @@ local function initialize_tags() end -- ensures the db is up to date by scanning all help sources. does not do --- anything if it has already been run within the last 10 seconds. +-- anything if it has already been run within the last 60 seconds. last_refresh_ms = last_refresh_ms or 0 local function ensure_db() local now_ms = dfhack.getTickCount() @@ -272,16 +372,21 @@ local function ensure_db() db, tag_index = {}, {} initialize_tags() + scan_builtins(old_db, db) scan_plugins(old_db, db) scan_scripts(old_db, db) end -local function get_db_property(command, property) +--------------------------------------------------------------------------- +-- get API +--------------------------------------------------------------------------- + +local function get_db_property(entry_name, property) ensure_db() - if not db[command] then - error(('command not found: "%s"'):format(command)) + if not db[entry_name] then + error(('entry not found: "%s"'):format(entry_name)) end - return db[command][property] + return db[entry_name][property] end -- returns the ~54 char summary blurb associated with the entry @@ -308,6 +413,32 @@ function get_entry_tags(entry) return set_to_sorted_list(get_db_property(entry, 'tags')) end +-- returns whether the given string matches a tag name +function is_tag(str) + ensure_db() + return not not tag_index[str] +end + +-- returns the defined tags in alphabetical order +function get_tags() + ensure_db() + return set_to_sorted_list(tag_index) +end + +-- returns the description associated with the given tag +function get_tag_description(tag) + ensure_db() + if not tag_index[tag] then + error('invalid tag: ' .. tag) + end + return tag_index[tag].description +end + +--------------------------------------------------------------------------- +-- search API +--------------------------------------------------------------------------- + +-- returns a list of path elements in reverse order local function chunk_for_sorting(str) local parts = str:split('/') local chunks = {} @@ -336,11 +467,8 @@ local function sort_by_basename(a, b) return false end -local function matches(command, filter) - local db_entry = db[command] - if filter.runnable and db_entry.unrunnable then - return false - end +local function matches(entry_name, filter) + local db_entry = db[entry_name] if filter.tag then local matched = false for _,tag in ipairs(filter.tag) do @@ -353,10 +481,22 @@ local function matches(command, filter) return false end end + if filter.types then + local matched = false + for _,etype in ipairs(filter.types) do + if db_entry.entry_types[etype] then + matched = true + break + end + end + if not matched then + return false + end + end if filter.str then local matched = false for _,str in ipairs(filter.str) do - if command:find(str, 1, true) then + if entry_name:find(str, 1, true) then matched = true break end @@ -368,6 +508,7 @@ local function matches(command, filter) return true end +-- converts strings into single-element lists containing that string local function normalize_string_list(l) if not l then return nil end if type(l) == 'string' then @@ -376,28 +517,35 @@ local function normalize_string_list(l) return l end +-- normalizes the lists in the filter and returns nil if no filter elements are +-- populated local function normalize_filter(f) if not f then return nil end local filter = {} filter.str = normalize_string_list(f.str) filter.tag = normalize_string_list(f.tag) - filter.runnable = f.runnable - if not filter.str and not filter.tag and not filter.runnable then + filter.types = normalize_string_list(f.types) + if not filter.str and not filter.tag and not filter.types then return nil end return filter end --- returns a list of identifiers, alphabetized by their last path component --- (e.g. gui/autobutcher will immediately follow autobutcher). +-- returns a list of entry names, alphabetized by their last path component, +-- with populated path components coming before null path components (e.g. +-- autobutcher will immediately follow gui/autobutcher). -- the optional include and exclude filter params are maps with the following -- elements: -- str - if a string, filters by the given substring. if a table of strings, --- includes commands that match any of the given substrings. +-- includes entry names that match any of the given substrings. -- tag - if a string, filters by the given tag name. if a table of strings, --- includes commands that match any of the given tags. --- runnable - if true, matches only runnable commands, not plugin names. -function get_entries(include, exclude) +-- includes entries that match any of the given tags. +-- types - if a string, matches entries of the given type. if a table of +-- strings, includes entries that match any of the given types. valid +-- types are: "builtin", "plugin", "command". note that many plugin +-- commands have the same name as the plugin, so those entries will +-- match both "plugin" and "command" types. +function search_entries(include, exclude) ensure_db() include = normalize_filter(include) exclude = normalize_filter(exclude) @@ -412,6 +560,10 @@ function get_entries(include, exclude) return commands end +--------------------------------------------------------------------------- +-- list API (outputs to console) +--------------------------------------------------------------------------- + local function get_max_width(list, min_width) local width = min_width or 0 for _,item in ipairs(list) do @@ -420,43 +572,52 @@ local function get_max_width(list, min_width) return width end +-- prints the defined tags and their descriptions to the console +function list_tags() + local tags = get_tags() + local width = get_max_width(tags, 10) + for _,tag in ipairs(tags) do + print((' %-'..width..'s %s'):format(tag, get_tag_description(tag))) + end +end + -- prints the requested entries to the console. include and exclude filters are --- as in get_entries above. -function list_entries(include_tags, include, exclude) - local entries = get_entries(include, exclude) +-- defined as in search_entries() above. +function list_entries(skip_tags, include, exclude) + local entries = search_entries(include, exclude) local width = get_max_width(entries, 10) for _,entry in ipairs(entries) do - print((' %-'..width..'s %s'):format( + print((' %-'..width..'s %s'):format( entry, get_entry_short_help(entry))) - if include_tags then - print((' '..(' '):rep(width)..' tags(%s)'):format( - table.concat(get_entry_tags(entry), ','))) + if not skip_tags then + local tags = get_entry_tags(entry) + if #tags > 0 then + print((' tags: %s'):format(table.concat(tags, ', '))) + end end end -end - --- returns the defined tags in alphabetical order -function get_tags() - ensure_db() - return set_to_sorted_list(tag_index) -end - --- returns the description associated with the given tag -function get_tag_description(tag) - ensure_db() - if not tag_index[tag] then - error('invalid tag: ' .. tag) + if #entries == 0 then + print('no entries found.') end - return tag_index[tag].description end --- prints the defined tags and their descriptions to the console -function list_tags() - local tags = get_tags() - local width = get_max_width(tags, 10) - for _,tag in ipairs(tags) do - print((' %-'..width..'s %s'):format(tag, get_tag_description(tag))) +-- wraps the list_entries() API to provide a more convenient interface for Core +-- to implement the 'ls' builtin command. +-- filter_str - if a tag name, will filter by that tag. otherwise, will filter +-- as a substring +-- skip_tags - whether to skip printing tag info +-- show_dev_commands - if true, will include scripts in the modtools/ and +-- devel/ directories. otherwise those scripts will be +-- excluded +function ls(filter_str, skip_tags, show_dev_commands) + local include = {types={ENTRY_TYPES.COMMAND}} + if is_tag(filter_str) then + include.tag = filter_str + else + include.str = filter_str end + list_entries(skip_tags, include, + show_dev_commands and {} or {str={'modtools/', 'devel/'}}) end return _ENV From e899510b8bc3374896ae79575cb840ecc210e195 Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 8 Jul 2022 15:53:51 -0700 Subject: [PATCH 007/334] Use helpdb to implement help and ls built-in commands and dfhack.script_help() (#2242) * use helpdb to implement the help and ls builtins * use helpdb to implement dfhack.script_help() --- library/Core.cpp | 234 +++++++++++++++-------------------------- library/lua/dfhack.lua | 38 +------ library/lua/helpdb.lua | 35 ++++-- 3 files changed, 117 insertions(+), 190 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index aa43f3c2b..62fe366f1 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -446,6 +446,7 @@ command_result Core::runCommand(color_ostream &out, const std::string &command) static const std::set built_in_commands = { "ls" , "help" , + "tags" , "type" , "load" , "unload" , @@ -674,25 +675,71 @@ string getBuiltinCommand(std::string cmd) return builtin; } -void ls_helper(color_ostream &con, const string &name, const string &desc) -{ - const size_t help_line_length = 80 - 22 - 5; - const string padding = string(80 - help_line_length, ' '); - vector lines; - con.print(" %-22s - ", name.c_str()); - word_wrap(&lines, desc, help_line_length); +void help_helper(color_ostream &con, const string &entry_name) { + CoreSuspender suspend; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 2) || + !Lua::PushModulePublic(con, L, "helpdb", "help")) { + con.printerr("Failed to load helpdb Lua code\n"); + return; + } + + Lua::Push(L, entry_name); - // print first line, then any additional lines preceded by padding - for (size_t i = 0; i < lines.size(); i++) - con.print("%s%s\n", i ? padding.c_str() : "", lines[i].c_str()); + if (!Lua::SafeCall(con, L, 1, 0)) { + con.printerr("Failed Lua call to helpdb.help.\n"); + } } -void ls_helper(color_ostream &con, const PluginCommand &pcmd) -{ - if (pcmd.isHotkeyCommand()) - con.color(COLOR_CYAN); - ls_helper(con, pcmd.name, pcmd.description); - con.reset_color(); +void tags_helper(color_ostream &con) { + CoreSuspender suspend; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 1) || + !Lua::PushModulePublic(con, L, "helpdb", "tags")) { + con.printerr("Failed to load helpdb Lua code\n"); + return; + } + + if (!Lua::SafeCall(con, L, 0, 0)) { + con.printerr("Failed Lua call to helpdb.tags.\n"); + } +} + +void ls_helper(color_ostream &con, const vector ¶ms) { + vector filter; + bool skip_tags = false; + bool show_dev_commands = false; + + for (auto str : params) { + if (str == "--notags") + skip_tags = true; + else if (str == "--dev") + show_dev_commands = true; + else + filter.push_back(str); + } + + CoreSuspender suspend; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 4) || + !Lua::PushModulePublic(con, L, "helpdb", "ls")) { + con.printerr("Failed to load helpdb Lua code\n"); + return; + } + + Lua::PushVector(L, filter); + Lua::Push(L, skip_tags); + Lua::Push(L, show_dev_commands); + + if (!Lua::SafeCall(con, L, 3, 0)) { + con.printerr("Failed Lua call to helpdb.ls.\n"); + } } command_result Core::runCommand(color_ostream &con, const std::string &first_, vector &parts) @@ -733,70 +780,33 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v "On Windows, you may have to resize your console window. The appropriate menu is accessible\n" "by clicking on the program icon in the top bar of the window.\n\n"); } - con.print("Basic commands:\n" - " help|?|man - This text.\n" - " help COMMAND - Usage help for the given command.\n" - " ls|dir [-a] [PLUGIN] - List available commands. Optionally for single plugin.\n" - " cls|clear - Clear the console.\n" - " fpause - Force DF to pause.\n" - " die - Force DF to close immediately\n" - " keybinding - Modify bindings of commands to keys\n" - "Plugin management (useful for developers):\n" - " plug [PLUGIN|v] - List plugin state and description.\n" - " load PLUGIN|-all - Load a plugin by name or load all possible plugins.\n" - " unload PLUGIN|-all - Unload a plugin or all loaded plugins.\n" - " reload PLUGIN|-all - Reload a plugin or all loaded plugins.\n" + con.print("Here are some basic commands to get you started:\n" + " help|?|man - This text.\n" + " help - Usage help for the given plugin, command, or script.\n" + " tags - List the tags that the DFHack tools are grouped by.\n" + " ls|dir [] - List commands, optionally filtered by a tag or substring.\n" + " Optional parameters:\n" + " --notags: skip printing tags for each command.\n" + " --dev: include commands intended for developers and modders.\n" + " cls|clear - Clear the console.\n" + " fpause - Force DF to pause.\n" + " die - Force DF to close immediately, without saving.\n" + " keybinding - Modify bindings of commands to in-game key shortcuts.\n" + "\n" + "See more commands by running 'ls'.\n\n" ); - con.print("\nDFHack version %s\n", dfhack_version_desc().c_str()); - } - else if (parts.size() == 1) - { - if (getBuiltinCommand(parts[0]).size()) - { - con << parts[0] << ": built-in command; Use `ls`, `help`, or check hack/Readme.html for more information" << std::endl; - return CR_NOT_IMPLEMENTED; - } - Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); - if (plug) { - for (size_t j = 0; j < plug->size();j++) - { - const PluginCommand & pcmd = (plug->operator[](j)); - if (pcmd.name != parts[0]) - continue; - - if (pcmd.isHotkeyCommand()) - con.color(COLOR_CYAN); - con.print("%s: %s\n",pcmd.name.c_str(), pcmd.description.c_str()); - con.reset_color(); - if (!pcmd.usage.empty()) - con << "Usage:\n" << pcmd.usage << flush; - return CR_OK; - } - } - string file = findScript(parts[0] + ".lua"); - if ( file != "" ) { - string help = getScriptHelp(file, "--"); - con.print("%s: %s\n", parts[0].c_str(), help.c_str()); - return CR_OK; - } - if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) { - file = findScript(parts[0] + ".rb"); - if ( file != "" ) { - string help = getScriptHelp(file, "#"); - con.print("%s: %s\n", parts[0].c_str(), help.c_str()); - return CR_OK; - } - } - con.printerr("Unknown command: %s\n", parts[0].c_str()); - return CR_FAILURE; + con.print("DFHack version %s\n", dfhack_version_desc().c_str()); } else { - con.printerr("not implemented yet\n"); - return CR_NOT_IMPLEMENTED; + help_helper(con, parts[0]); } } + else if (builtin == "tags") + { + tags_helper(con); + } else if (builtin == "load" || builtin == "unload" || builtin == "reload") { bool all = false; @@ -906,83 +916,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } else if (builtin == "ls" || builtin == "dir") { - bool all = false; - if (parts.size() && parts[0] == "-a") - { - all = true; - vector_erase_at(parts, 0); - } - if(parts.size()) - { - string & plugname = parts[0]; - const Plugin * plug = (*plug_mgr)[plugname]; - if(!plug) - { - con.printerr("There's no plugin called %s!\n", plugname.c_str()); - } - else if (plug->getState() != Plugin::PS_LOADED) - { - con.printerr("Plugin %s is not loaded.\n", plugname.c_str()); - } - else if (!plug->size()) - { - con.printerr("Plugin %s is loaded but does not implement any commands.\n", plugname.c_str()); - } - else for (size_t j = 0; j < plug->size();j++) - { - ls_helper(con, plug->operator[](j)); - } - } - else - { - con.print( - "builtin:\n" - " help|?|man - This text or help specific to a plugin.\n" - " ls|dir [-a] [PLUGIN] - List available commands. Optionally for single plugin.\n" - " cls|clear - Clear the console.\n" - " fpause - Force DF to pause.\n" - " die - Force DF to close immediately\n" - " kill-lua - Stop an active Lua script\n" - " keybinding - Modify bindings of commands to keys\n" - " script FILENAME - Run the commands specified in a file.\n" - " sc-script - Automatically run specified scripts on state change events\n" - " plug [PLUGIN|v] - List plugin state and detailed description.\n" - " load PLUGIN|-all [...] - Load a plugin by name or load all possible plugins.\n" - " unload PLUGIN|-all [...] - Unload a plugin or all loaded plugins.\n" - " reload PLUGIN|-all [...] - Reload a plugin or all loaded plugins.\n" - " enable/disable PLUGIN [...] - Enable or disable a plugin if supported.\n" - " type COMMAND - Display information about where a command is implemented\n" - "\n" - "plugins:\n" - ); - std::set out; - for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) - { - const Plugin * plug = it->second; - if(!plug->size()) - continue; - for (size_t j = 0; j < plug->size();j++) - { - const PluginCommand & pcmd = (plug->operator[](j)); - out.insert(sortable(pcmd.isHotkeyCommand(),pcmd.name,pcmd.description)); - } - } - for(auto iter = out.begin();iter != out.end();iter++) - { - if ((*iter).recolor) - con.color(COLOR_CYAN); - ls_helper(con, iter->name, iter->description); - con.reset_color(); - } - std::map scripts; - listAllScripts(scripts, all); - if (!scripts.empty()) - { - con.print("\nscripts:\n"); - for (auto iter = scripts.begin(); iter != scripts.end(); ++iter) - ls_helper(con, iter->first, iter->second); - } - } + ls_helper(con, parts); } else if (builtin == "plug") { diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index e476ffa11..8af77e2e4 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -1,4 +1,4 @@ --- Common startup file for all dfhack plugins with lua support +-- Common startup file for all dfhack scripts and plugins with lua support -- The global dfhack table is already created by C++ init code. -- Setup the global environment. @@ -652,8 +652,9 @@ function Script:needs_update() return (not self.env) or self.mtime ~= dfhack.filesystem.mtime(self.path) end function Script:get_flags() - if self.flags_mtime ~= dfhack.filesystem.mtime(self.path) then - self.flags_mtime = dfhack.filesystem.mtime(self.path) + local mtime = dfhack.filesystem.mtime(self.path) + if self.flags_mtime ~= mtime then + self.flags_mtime = mtime self._flags = {} local f = io.open(self.path) local contents = f:read('*all') @@ -814,36 +815,7 @@ end function dfhack.script_help(script_name, extension) script_name = script_name or dfhack.current_script_name() - extension = extension or 'lua' - local full_name = script_name .. '.' .. extension - local path = dfhack.internal.findScript(script_name .. '.' .. extension) - or error("Could not find script: " .. full_name) - local begin_seq, end_seq - if extension == 'rb' then - begin_seq = '=begin' - end_seq = '=end' - else - begin_seq = '[====[' - end_seq = ']====]' - end - local f = io.open(path) or error("Could not open " .. path) - local in_help = false - local help = '' - for line in f:lines() do - if line:endswith(begin_seq) then - in_help = true - elseif in_help then - if line:endswith(end_seq) then - break - end - if line ~= script_name and line ~= ('='):rep(#script_name) then - help = help .. line .. '\n' - end - end - end - f:close() - help = help:gsub('^\n+', ''):gsub('\n+$', '') - return help + return require('helpdb').get_entry_long_help(script_name) end local function _run_command(args, use_console) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 2999ee67e..e72f0804d 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -326,11 +326,13 @@ local function scan_scripts(old_db, db) f.path:startswith('internal/') then goto continue end + local dot_index = f.path:find('%.[^.]*$') local script_source = script_path .. '/' .. f.path update_db(old_db, db, has_rendered_help(f.path) and HELP_SOURCES.RENDERED or HELP_SOURCES.SCRIPT, - f.path:sub(1, #f.path - 4), {script_source=script_source}) + f.path:sub(1, dot_index - 1), + {script_source=script_source}) ::continue:: end ::skip_path:: @@ -384,7 +386,7 @@ end local function get_db_property(entry_name, property) ensure_db() if not db[entry_name] then - error(('entry not found: "%s"'):format(entry_name)) + error(('helpdb entry not found: "%s"'):format(entry_name)) end return db[entry_name][property] end @@ -415,8 +417,17 @@ end -- returns whether the given string matches a tag name function is_tag(str) + if not str or #str == 0 then + return false + end ensure_db() - return not not tag_index[str] + if type(str) == "string" then str = {str} end + for _,s in ipairs(str) do + if not tag_index[s] then + return false + end + end + return true end -- returns the defined tags in alphabetical order @@ -510,7 +521,7 @@ end -- converts strings into single-element lists containing that string local function normalize_string_list(l) - if not l then return nil end + if not l or #l == 0 then return nil end if type(l) == 'string' then return {l} end @@ -561,9 +572,19 @@ function search_entries(include, exclude) end --------------------------------------------------------------------------- --- list API (outputs to console) +-- print API (outputs to console) --------------------------------------------------------------------------- +-- implements the 'help' builtin command +function help(entry) + ensure_db() + if not db[entry] then + dfhack.printerr(('No help entry found for "%s"'):format(entry)) + return + end + print(get_entry_long_help(entry)) +end + local function get_max_width(list, min_width) local width = min_width or 0 for _,item in ipairs(list) do @@ -572,8 +593,8 @@ local function get_max_width(list, min_width) return width end --- prints the defined tags and their descriptions to the console -function list_tags() +-- implements the 'tags' builtin command +function tags() local tags = get_tags() local width = get_max_width(tags, 10) for _,tag in ipairs(tags) do From 8d99b7e6e1f5d14ee5d1eb4c4ecea5750c4bddef Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 9 Jul 2022 23:01:31 -0700 Subject: [PATCH 008/334] prepare for plugin doc split --- .gitignore | 3 +- CMakeLists.txt | 2 +- conf.py | 139 +- docs/Core.rst | 11 +- docs/Dev-intro.rst | 7 +- docs/Documentation.rst | 8 +- docs/History.rst | 4 +- docs/Introduction.rst | 2 +- docs/Memory-research.rst | 2 +- docs/NEWS-dev.rst | 2 +- docs/NEWS.rst | 2 +- docs/Plugins.rst | 3436 ------------------- docs/Removed.rst | 8 + docs/Scripts.rst | 19 - docs/Tools.rst | 13 - docs/_auto/.gitignore | 1 - docs/build-pdf.sh | 11 +- docs/build.sh | 12 +- docs/{_changelogs => changelogs}/.gitignore | 1 + docs/guides/examples-guide.rst | 4 +- docs/index-tools.rst | 24 + docs/{tools => plugins}/cromulate.rst | 0 docs/sphinx_extensions/dfhack/changelog.py | 14 +- index.rst | 6 +- 24 files changed, 127 insertions(+), 3604 deletions(-) delete mode 100644 docs/Plugins.rst delete mode 100644 docs/Scripts.rst delete mode 100644 docs/Tools.rst delete mode 100644 docs/_auto/.gitignore rename docs/{_changelogs => changelogs}/.gitignore (50%) create mode 100644 docs/index-tools.rst rename docs/{tools => plugins}/cromulate.rst (100%) diff --git a/.gitignore b/.gitignore index 0b54f4a84..5155f2bb9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,11 @@ build/VC2010 !build/ # Sphinx generated documentation -docs/_* +docs/changelogs/ docs/html/ docs/pdf/ docs/text/ +docs/tools/ # in-place build build/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bee1c04a..46bd1917b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,7 +505,7 @@ if(BUILD_DOCS) DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/ DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) - install(FILES docs/_auto/news.rst docs/_auto/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) + install(FILES docs/changelogs/news.rst docs/changelogs/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES "README.html" DESTINATION "${DFHACK_DATA_DESTINATION}") endif() diff --git a/conf.py b/conf.py index 53b192467..d7be99a15 100644 --- a/conf.py +++ b/conf.py @@ -29,8 +29,6 @@ import sys from docutils import nodes from docutils.parsers.rst import roles -sphinx_major_version = sphinx.version_info[0] - def get_keybinds(root, files, keybindings): """Add keybindings in the specified files to the given keybindings dict. @@ -85,107 +83,86 @@ def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func) -# -- Autodoc for DFhack scripts ------------------------------------------- +# -- Autodoc for DFhack plugins and scripts ------------------------------- -def doc_dir(dirname, files): - """Yield (command, includepath) for each script in the directory.""" +def doc_dir(dirname, files, prefix): + """Yield (name, includepath) for each file in the directory.""" sdir = os.path.relpath(dirname, '.').replace('\\', '/').replace('../', '') + if prefix == '.': + prefix = '' + else: + prefix += '/' for f in files: - if f[-3:] not in ('lua', '.rb'): + if f[-4:] != '.rst': continue - with open(os.path.join(dirname, f), 'r', encoding='utf8') as fstream: - text = [l.rstrip() for l in fstream.readlines() if l.strip()] - # Some legacy lua files use the ruby tokens (in 3rdparty scripts) - tokens = ('=begin', '=end') - if f[-4:] == '.lua' and any('[====[' in line for line in text): - tokens = ('[====[', ']====]') - command = None - for line in text: - if command and line == len(line) * '=': - yield command, sdir + '/' + f, tokens[0], tokens[1] - break - command = line + yield prefix + f[:-4], sdir + '/' + f def doc_all_dirs(): """Collect the commands and paths to include in our docs.""" - scripts = [] - for root, _, files in os.walk('scripts'): - scripts.extend(doc_dir(root, files)) - return tuple(scripts) + tools = [] + # TODO: as we scan the docs, parse out the tags and short descriptions and + # build a map for use in generating the tags pages and links in the tool + # doc footers + for root, _, files in os.walk('docs/plugins'): + tools.extend(doc_dir(root, files, os.path.relpath(root, 'docs/plugins'))) + for root, _, files in os.walk('scripts/docs'): + tools.extend(doc_dir(root, files, os.path.relpath(root, 'scripts/docs'))) + return tuple(tools) DOC_ALL_DIRS = doc_all_dirs() +def generate_tag_indices(): + #TODO: generate docs/tags/.rst with the tag description and links to the + # tools that have that tag + os.makedirs('docs/tags', mode=0o755, exist_ok=True) -def document_scripts(): - """Autodoc for files with the magic script documentation marker strings. - Returns a dict of script-kinds to lists of .rst include directives. +def write_tool_docs(): """ - # Next we split by type and create include directives sorted by command - kinds = {'base': [], 'devel': [], 'fix': [], 'gui': [], 'modtools': []} - for s in DOC_ALL_DIRS: - k_fname = s[0].split('/', 1) - if len(k_fname) == 1: - kinds['base'].append(s) - else: - kinds[k_fname[0]].append(s) - - def template(arg): - tmp = '.. _{}:\n\n.. include:: /{}\n' +\ - ' :start-after: {}\n :end-before: {}\n' - if arg[0] in KEYBINDS: - tmp += '\n:dfhack-keybind:`{}`\n'.format(arg[0]) - return tmp.format(*arg) - - return {key: '\n\n'.join(map(template, sorted(value))) - for key, value in kinds.items()} - - -def write_script_docs(): + Creates a file for each tool with the ".. include::" directives to pull in + the original documentation. Then we generate a label and useful info in the + footer. """ - Creates a file for eack kind of script (base/devel/fix/gui/modtools) - with all the ".. include::" directives to pull out docs between the - magic strings. - """ - kinds = document_scripts() - head = { - 'base': 'Basic Scripts', - 'devel': 'Development Scripts', - 'fix': 'Bugfixing Scripts', - 'gui': 'GUI Scripts', - 'modtools': 'Scripts for Modders'} - for k in head: - title = ('.. _scripts-{k}:\n\n{l}\n{t}\n{l}\n\n' - '.. include:: /scripts/{a}about.txt\n\n' - '.. contents:: Contents\n' - ' :local:\n\n').format( - k=k, t=head[k], - l=len(head[k])*'#', - a=('' if k == 'base' else k + '/') - ) + for k in DOC_ALL_DIRS: + label = ('.. _{name}:\n\n').format(name=k[0]) + # TODO: can we autogenerate the :dfhack-keybind: line? it would go beneath + # the tool header, which is currently in the middle of the included file. + # should we remove those headers from the doc files and just generate them + # here? That might be easier. But then where will the tags go? It would + # look better if they were above the keybinds, but then we'd be in the + # same situation. + include = ('.. include:: /{path}\n\n').format(path=k[1]) + # TODO: generate a footer with links to tools that share at least one + # tag with this tool. Just the tool names, strung across the bottom of + # the page in one long wrapped line, similar to how the wiki does it mode = 'w' if sys.version_info.major > 2 else 'wb' - with open('docs/_auto/{}.rst'.format(k), mode) as outfile: - outfile.write(title) - outfile.write(kinds[k]) + os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), + mode=0o755, exist_ok=True) + with open('docs/tools/{}.rst'.format(k[0]), mode) as outfile: + if k[0] != 'search': + outfile.write(label) + outfile.write(include) def all_keybinds_documented(): """Check that all keybindings are documented with the :dfhack-keybind: directive somewhere.""" - configured_binds = set(KEYBINDS) - script_commands = set(i[0] for i in DOC_ALL_DIRS) - with open('./docs/Plugins.rst') as f: - plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read())) - undocumented_binds = configured_binds - script_commands - plugin_binds + undocumented_binds = set(KEYBINDS) + tools = set(i[0] for i in DOC_ALL_DIRS) + for t in tools: + with open(('./docs/tools/{}.rst').format(t)) as f: + tool_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read())) + undocumented_binds -= tool_binds if undocumented_binds: raise ValueError('The following DFHack commands have undocumented ' 'keybindings: {}'.format(sorted(undocumented_binds))) # Actually call the docs generator and run test -write_script_docs() -all_keybinds_documented() +write_tool_docs() +generate_tag_indices() +#all_keybinds_documented() # comment out while we're transitioning # -- General configuration ------------------------------------------------ @@ -203,6 +180,8 @@ extensions = [ 'dfhack.lexer', ] +sphinx_major_version = sphinx.version_info[0] + def get_caption_str(prefix=''): return prefix + (sphinx_major_version >= 5 and '%s' or '') @@ -282,11 +261,11 @@ today_fmt = html_last_updated_fmt = '%Y-%m-%d' # directories to ignore when looking for source files. exclude_patterns = [ 'README.md', - 'docs/html*', - 'depends/*', 'build*', - 'docs/_auto/news*', - 'docs/_changelogs/', + 'depends/*', + 'docs/html/*', + 'docs/text/*', + 'docs/plugins/*', 'scripts/docs/*', ] diff --git a/docs/Core.rst b/docs/Core.rst index ffa620f55..f7ba0824b 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -15,19 +15,18 @@ DFHack commands can be implemented in any of three ways: :builtin: commands are implemented by the core of DFHack. They manage other DFHack tools, interpret commands, and control basic - aspects of DF (force pause or quit). They are documented - `here `. + aspects of DF (force pause or quit). :plugins: are stored in ``hack/plugins/`` and must be compiled with the same version of DFHack. They are less flexible than scripts, but used for complex or ongoing tasks because they run faster. - Plugins included with DFHack are documented `here `. :scripts: are Ruby or Lua scripts stored in ``hack/scripts/``. Because they don't need to be compiled, scripts are more flexible about versions, and easier to distribute. - Most third-party DFHack addons are scripts. All scripts included - with DFHack are documented `here `. + Most third-party DFHack addons are scripts. + +All tools distributed with DFHack are documented `here `. Using DFHack Commands ===================== @@ -186,7 +185,7 @@ where ``*`` can be any string, including the empty string. A world being loaded can mean a fortress, an adventurer, or legends mode. These files are best used for non-persistent commands, such as setting -a `fix ` script to run on `repeat`. +a `fix ` script to run on `repeat`. .. _onMapLoad.init: diff --git a/docs/Dev-intro.rst b/docs/Dev-intro.rst index 758bf225f..6f46d86c5 100644 --- a/docs/Dev-intro.rst +++ b/docs/Dev-intro.rst @@ -41,7 +41,7 @@ Installed plugins live in the ``hack/plugins`` folder of a DFHack installation, and the `load` family of commands can be used to load a recompiled plugin without restarting DF. -See `plugins-index` for a list of all plugins included in DFHack. +Run `plug` at the DFHack prompt for a list of all plugins included in DFHack. Scripts ------- @@ -51,8 +51,9 @@ is more complete and currently better-documented, however. Referring to existing scripts as well as the API documentation can be helpful when developing new scripts. -`Scripts included in DFHack ` live in a separate `scripts repository `_. -This can be found in the ``scripts`` submodule if you have +`Scripts included in DFHack ` live in a separate +`scripts repository `_. This can be found in +the ``scripts`` submodule if you have `cloned DFHack `, or the ``hack/scripts`` folder of an installed copy of DFHack. diff --git a/docs/Documentation.rst b/docs/Documentation.rst index be4511ba8..acfffdbbb 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -318,10 +318,10 @@ changelogs are combined as part of the changelog build process: * ``scripts/changelog.txt`` for changes made to scripts in the ``scripts`` repo * ``library/xml/changelog.txt`` for changes made in the ``df-structures`` repo -Building the changelogs generates two files: ``docs/_auto/news.rst`` and -``docs/_auto/news-dev.rst``. These correspond to `changelog` and `dev-changelog` -and contain changes organized by stable and development DFHack releases, -respectively. For example, an entry listed under "0.44.05-alpha1" in +Building the changelogs generates two files: ``docs/changelogs/news.rst`` and +``docs/changelogs/news-dev.rst``. These correspond to `changelog` and +`dev-changelog` and contain changes organized by stable and development DFHack +releases, respectively. For example, an entry listed under "0.44.05-alpha1" in changelog.txt will be listed under that version in the development changelog as well, but under "0.44.05-r1" in the stable changelog (assuming that is the closest stable release after 0.44.05-alpha1). An entry listed under a stable diff --git a/docs/History.rst b/docs/History.rst index dc02d02b0..67b65f3bc 100644 --- a/docs/History.rst +++ b/docs/History.rst @@ -1350,7 +1350,7 @@ Misc improvements - `createitem`: in adventure mode it now defaults to the controlled unit as maker. - `autotrade`: adds "(Un)mark All" options to both panes of trade screen. - `mousequery`: several usability improvements; show live overlay (in menu area) of what's on the tile under the mouse cursor. -- `search`: workshop profile search added. +- `search-plugin`: workshop profile search added. - `dwarfmonitor`: add screen to summarise preferences of fortress dwarfs. - `getplants`: add autochop function to automate woodcutting. - `stocks`: added more filtering and display options. @@ -1510,7 +1510,7 @@ New binary patches New Plugins ----------- - `fix-armory`: Together with a couple of binary patches and the `gui/assign-rack` script, this plugin makes weapon racks, armor stands, chests and cabinets in properly designated barracks be used again for storage of squad equipment. -- `search`: Adds an incremental search function to the Stocks, Trading, Stockpile and Unit List screens. +- `search-plugin`: Adds an incremental search function to the Stocks, Trading, Stockpile and Unit List screens. - `automaterial`: Makes building constructions (walls, floors, fortifications, etc) a little bit easier by saving you from having to trawl through long lists of materials each time you place one. - Dfusion: Reworked to make use of lua modules, now all the scripts can be used from other scripts. - Eventful: A collection of lua events, that will allow new ways to interact with df world. diff --git a/docs/Introduction.rst b/docs/Introduction.rst index 27c887a8b..f0dc7a4b6 100644 --- a/docs/Introduction.rst +++ b/docs/Introduction.rst @@ -21,7 +21,7 @@ enhancements by default, and more can be enabled. There are also many tools You can even add third-party scripts and plugins to do almost anything! For modders, DFHack makes many things possible. Custom reactions, new -interactions, magic creature abilities, and more can be set through `scripts-modtools` +interactions, magic creature abilities, and more can be set through `DFHack tools ` and custom raws. Non-standard DFHack scripts and inits can be stored in the raw directory, making raws or saves fully self-contained for distribution - or for coexistence in a single DF install, even with incompatible components. diff --git a/docs/Memory-research.rst b/docs/Memory-research.rst index 9729072f5..bc7b45fd7 100644 --- a/docs/Memory-research.rst +++ b/docs/Memory-research.rst @@ -63,7 +63,7 @@ are not built by default, but can be built by setting the ``BUILD_DEVEL`` Scripts ~~~~~~~ -Several `development scripts ` can be useful for memory research. +Several `development scripts ` can be useful for memory research. These include (but are not limited to): - `devel/dump-offsets` diff --git a/docs/NEWS-dev.rst b/docs/NEWS-dev.rst index f0c47fe96..f77737176 100644 --- a/docs/NEWS-dev.rst +++ b/docs/NEWS-dev.rst @@ -17,4 +17,4 @@ See `changelog` for a list of changes grouped by stable releases. :local: :depth: 1 -.. include:: /docs/_auto/news-dev.rst +.. include:: /docs/changelogs/news-dev.rst diff --git a/docs/NEWS.rst b/docs/NEWS.rst index 1283b079a..fb33aebf9 100644 --- a/docs/NEWS.rst +++ b/docs/NEWS.rst @@ -17,7 +17,7 @@ See `dev-changelog` for a list of changes grouped by development releases. :local: :depth: 1 -.. include:: /docs/_auto/news.rst +.. include:: /docs/changelogs/news.rst Older Changelogs diff --git a/docs/Plugins.rst b/docs/Plugins.rst deleted file mode 100644 index 8acddfc6d..000000000 --- a/docs/Plugins.rst +++ /dev/null @@ -1,3436 +0,0 @@ -.. _plugins-index: - -############## -DFHack Plugins -############## - -DFHack plugins are the commands, that are compiled with a specific version. -They can provide anything from a small keybinding, to a complete overhaul of -game subsystems or the entire renderer. - -Most commands offered by plugins are listed here, -hopefully organised in a way you will find useful. - -.. contents:: Contents - :local: - :depth: 2 - -======== -Bugfixes -======== - -.. contents:: - :local: - -.. _fix-armory: - -fix-armory -========== -`This plugin requires a binpatch `, which has not -been available since DF 0.34.11 - -.. _fix-unit-occupancy: - -fix-unit-occupancy -================== -This plugin fixes issues with unit occupancy, notably phantom -"unit blocking tile" messages (:bug:`3499`). It can be run manually, or -periodically when enabled with the built-in enable/disable commands: - -:(no argument): Run the plugin once immediately, for the whole map. -:-h, here, cursor: Run immediately, only operate on the tile at the cursor -:-n, dry, dry-run: Run immediately, do not write changes to map -:interval : Run the plugin every ``X`` ticks (when enabled). - The default is 1200 ticks, or 1 day. - Ticks are only counted when the game is unpaused. - -.. _fixveins: - -fixveins -======== -Removes invalid references to mineral inclusions and restores missing ones. -Use this if you broke your embark with tools like `tiletypes`, or if you -accidentally placed a construction on top of a valuable mineral floor. - -.. _petcapRemover: - -petcapRemover -============= -Allows you to remove or raise the pet population cap. In vanilla -DF, pets will not reproduce unless the population is below 50 and the number of -children of that species is below a certain percentage. This plugin allows -removing the second restriction and removing or raising the first. Pets still -require PET or PET_EXOTIC tags in order to reproduce. Type ``help petcapRemover`` -for exact usage. In order to make population more stable and avoid sudden -population booms as you go below the raised population cap, this plugin counts -pregnancies toward the new population cap. It can still go over, but only in the -case of multiple births. - -Usage: - -:petcapRemover: cause pregnancies now and schedule the next check -:petcapRemover every n: set how often in ticks the plugin checks for possible pregnancies -:petcapRemover cap n: set the new cap to n. if n = 0, no cap -:petcapRemover pregtime n: sets the pregnancy duration to n ticks. natural pregnancies are - 300000 ticks for the current race and 200000 for everyone else - -.. _tweak: - -tweak -===== -Contains various tweaks for minor bugs. - -One-shot subcommands: - -:clear-missing: Remove the missing status from the selected unit. - This allows engraving slabs for ghostly, but not yet - found, creatures. -:clear-ghostly: Remove the ghostly status from the selected unit and mark - it as dead. This allows getting rid of bugged ghosts - which do not show up in the engraving slab menu at all, - even after using clear-missing. It works, but is - potentially very dangerous - so use with care. Probably - (almost certainly) it does not have the same effects like - a proper burial. You've been warned. -:fixmigrant: Remove the resident/merchant flag from the selected unit. - Intended to fix bugged migrants/traders who stay at the - map edge and don't enter your fort. Only works for - dwarves (or generally the player's race in modded games). - Do NOT abuse this for 'real' caravan merchants (if you - really want to kidnap them, use 'tweak makeown' instead, - otherwise they will have their clothes set to forbidden etc). -:makeown: Force selected unit to become a member of your fort. - Can be abused to grab caravan merchants and escorts, even if - they don't belong to the player's race. Foreign sentients - (humans, elves) can be put to work, but you can't assign rooms - to them and they don't show up in DwarfTherapist because the - game treats them like pets. Grabbing draft animals from - a caravan can result in weirdness (animals go insane or berserk - and are not flagged as tame), but you are allowed to mark them - for slaughter. Grabbing wagons results in some funny spam, then - they are scuttled. - -Subcommands that persist until disabled or DF quits: - -.. comment: sort these alphabetically - -:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (:bug:`6481`). -:advmode-contained: Works around :bug:`6202`, custom reactions with container inputs - in advmode. The issue is that the screen tries to force you to select - the contents separately from the container. This forcefully skips child - reagents. -:block-labors: Prevents labors that can't be used from being toggled -:burrow-name-cancel: Implements the "back" option when renaming a burrow, - which currently does nothing (:bug:`1518`) -:cage-butcher: Adds an option to butcher units when viewing cages with :kbd:`q` -:civ-view-agreement: Fixes overlapping text on the "view agreement" screen -:condition-material: Fixes a crash in the work order contition material list (:bug:`9905`). -:craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`). - With this tweak, items made from cloth and leather will gain a level of - wear every 20 years. -:do-job-now: Adds a job priority toggle to the jobs list -:embark-profile-name: Allows the use of lowercase letters when saving embark profiles -:eggs-fertile: Displays a fertility indicator on nestboxes -:farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus -:fast-heat: Further improves temperature update performance by ensuring that 1 degree - of item temperature is crossed in no more than specified number of frames - when updating from the environment temperature. This reduces the time it - takes for stable-temp to stop updates again when equilibrium is disturbed. -:fast-trade: Makes Shift-Down in the Move Goods to Depot and Trade screens select - the current item (fully, in case of a stack), and scroll down one line. -:fps-min: Fixes the in-game minimum FPS setting -:hide-priority: Adds an option to hide designation priority indicators -:hotkey-clear: Adds an option to clear currently-bound hotkeys (in the :kbd:`H` menu) -:import-priority-category: - Allows changing the priority of all goods in a - category when discussing an import agreement with the liaison -:kitchen-prefs-all: Adds an option to toggle cook/brew for all visible items in kitchen preferences -:kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences -:kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs (:bug:`9000`) -:max-wheelbarrow: Allows assigning more than 3 wheelbarrows to a stockpile -:military-color-assigned: - Color squad candidates already assigned to other squads in yellow/green - to make them stand out more in the list. - - .. image:: images/tweak-mil-color.png - -:military-stable-assign: - Preserve list order and cursor position when assigning to squad, - i.e. stop the rightmost list of the Positions page of the military - screen from constantly resetting to the top. -:nestbox-color: Fixes the color of built nestboxes -:partial-items: Displays percentages on partially-consumed items such as hospital cloth -:reaction-gloves: Fixes reactions to produce gloves in sets with correct handedness (:bug:`6273`) -:shift-8-scroll: Gives Shift-8 (or :kbd:`*`) priority when scrolling menus, instead of scrolling the map -:stable-cursor: Saves the exact cursor position between t/q/k/d/b/etc menus of fortress mode, if the - map view is near enough to its previous position. -:stone-status-all: Adds an option to toggle the economic status of all stones -:title-start-rename: Adds a safe rename option to the title screen "Start Playing" menu -:tradereq-pet-gender: Displays pet genders on the trade request screen - -.. comment: sort these alphabetically - - -=============================== -Data inspection and visualizers -=============================== - -.. contents:: - :local: - -.. _blueprint: - -blueprint -========= -The ``blueprint`` command exports the structure of a portion of your fortress in -a blueprint file that you (or anyone else) can later play back with `quickfort`. - -Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` -subdirectory of your DF folder. The map area to turn into a blueprint is either -selected interactively with the ``blueprint gui`` command or, if the GUI is not -used, starts at the active cursor location and extends right and down for the -requested width and height. - -**Usage:** - - ``blueprint [] [ []] []`` - - ``blueprint gui [ []] []`` - -**Examples:** - -``blueprint gui`` - Runs `gui/blueprint`, the interactive frontend, where all configuration for - a ``blueprint`` command can be set visually and interactively. - -``blueprint 30 40 bedrooms`` - Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting - from the active cursor on the current z-level. Blueprints are written - sequentially to ``bedrooms.csv`` in the ``blueprints`` directory. - -``blueprint 30 40 bedrooms dig --cursor 108,100,150`` - Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and - the start of the blueprint area is set to a specific value instead of using - the in-game cursor position. - -**Positional Parameters:** - -:``width``: Width of the area (in tiles) to translate. -:``height``: Height of the area (in tiles) to translate. -:``depth``: Number of z-levels to translate. Positive numbers go *up* from the - cursor and negative numbers go *down*. Defaults to 1 if not specified, - indicating that the blueprint should only include the current z-level. -:``name``: Base name for blueprint files created in the ``blueprints`` - directory. If no name is specified, "blueprint" is used by default. The - string must contain some characters other than numbers so the name won't be - confused with the optional ``depth`` parameter. - -**Phases:** - -If you want to generate blueprints only for specific phases, add their names to -the commandline, anywhere after the blueprint base name. You can list multiple -phases; just separate them with a space. - -:``dig``: Generate quickfort ``#dig`` blueprints for digging natural stone. -:``carve``: Generate quickfort ``#dig`` blueprints for smoothing and carving. -:``build``: Generate quickfort ``#build`` blueprints for constructions and - buildings. -:``place``: Generate quickfort ``#place`` blueprints for placing stockpiles. -:``zone``: Generate quickfort ``#zone`` blueprints for designating zones. -:``query``: Generate quickfort ``#query`` blueprints for configuring rooms. - -If no phases are specified, phases are autodetected. For example, a ``#place`` -blueprint will be created only if there are stockpiles in the blueprint area. - -**Options:** - -``-c``, ``--cursor ,,``: - Use the specified map coordinates instead of the current cursor position for - the upper left corner of the blueprint range. If this option is specified, - then an active game map cursor is not necessary. -``-e``, ``--engrave``: - Record engravings in the ``carve`` phase. If this option is not specified, - engravings are ignored. -``-f``, ``--format ``: - Select the output format of the generated files. See the ``Output formats`` - section below for options. If not specified, the output format defaults to - "minimal", which will produce a small, fast ``.csv`` file. -``-h``, ``--help``: - Show command help text. -``-s``, ``--playback-start ,,``: - Specify the column and row offsets (relative to the upper-left corner of the - blueprint, which is ``1,1``) where the player should put the cursor when the - blueprint is played back with `quickfort`, in - `quickfort start marker ` format, for example: - ``10,10,central stairs``. If there is a space in the comment, you will need - to surround the parameter string in double quotes: ``"-s10,10,central stairs"`` or - ``--playback-start "10,10,central stairs"`` or - ``"--playback-start=10,10,central stairs"``. -``-t``, ``--splitby ``: - Split blueprints into multiple files. See the ``Splitting output into - multiple files`` section below for details. If not specified, defaults to - "none", which will create a standard quickfort - `multi-blueprint ` file. - -**Output formats:** - -Here are the values that can be passed to the ``--format`` flag: - -:``minimal``: - Creates ``.csv`` files with minimal file size that are fast to read and - write. This is the default. -:``pretty``: - Makes the blueprints in the ``.csv`` files easier to read and edit with a text - editor by adding extra spacing and alignment markers. - -**Splitting output into multiple files:** - -The ``--splitby`` flag can take any of the following values: - -:``none``: - Writes all blueprints into a single file. This is the standard format for - quickfort fortress blueprint bundles and is the default. -:``phase``: - Creates a separate file for each phase. - -.. _cursecheck: - -cursecheck -========== -Checks a single map tile or the whole map/world for cursed creatures (ghosts, -vampires, necromancers, werebeasts, zombies). - -With an active in-game cursor only the selected tile will be observed. -Without a cursor the whole map will be checked. - -By default cursed creatures will be only counted in case you just want to find -out if you have any of them running around in your fort. Dead and passive -creatures (ghosts who were put to rest, killed vampires, ...) are ignored. -Undead skeletons, corpses, bodyparts and the like are all thrown into the curse -category "zombie". Anonymous zombies and resurrected body parts will show -as "unnamed creature". - -Options: - -:detail: Print full name, date of birth, date of curse and some status - info (some vampires might use fake identities in-game, though). -:ids: Print the creature and race IDs. -:nick: Set the type of curse as nickname (does not always show up - in-game, some vamps don't like nicknames). -:all: Include dead and passive cursed creatures (can result in a quite - long list after having FUN with necromancers). -:verbose: Print all curse tags (if you really want to know it all). - -Examples: - -``cursecheck detail all`` - Give detailed info about all cursed creatures including deceased ones (no - in-game cursor). -``cursecheck nick`` - Give a nickname all living/active cursed creatures on the map(no in-game - cursor). - -.. note:: - - If you do a full search (with the option "all") former ghosts will show up - with the cursetype "unknown" because their ghostly flag is not set. - - Please report any living/active creatures with cursetype "unknown" - - this is most likely with mods which introduce new types of curses. - -.. _flows: - -flows -===== -A tool for checking how many tiles contain flowing liquids. If you suspect that -your magma sea leaks into HFS, you can use this tool to be sure without -revealing the map. - -.. _isoworldremote: - -isoworldremote -============== -A plugin that implements a `remote API ` used by Isoworld. - -.. _probe: - -probe -===== - -This plugin provides multiple commands that print low-level properties of the -selected objects. - -* ``probe``: prints some properties of the tile selected with :kbd:`k`. Some of - these properties can be passed into `tiletypes`. -* ``cprobe``: prints some properties of the unit selected with :kbd:`v`, as well - as the IDs of any worn items. `gui/gm-unit` and `gui/gm-editor` are more - complete in-game alternatives. -* ``bprobe``: prints some properties of the building selected with :kbd:`q` or - :kbd:`t`. `gui/gm-editor` is a more complete in-game alternative. - -.. _prospect: -.. _prospector: - -prospect -======== - -**Usage:** - - ``prospect [all|hell] []`` - -Shows a summary of resources that exist on the map. By default, only the visible -part of the map is scanned. Include the ``all`` keyword if you want ``prospect`` -to scan the whole map as if it were revealed. Use ``hell`` instead of ``all`` if -you also want to see the Z range of HFS tubes in the 'features' report section. - -**Options:** - -:``-h``, ``--help``: - Shows this help text. -:``-s``, ``--show ``: - Shows only the named comma-separated list of report sections. Report section - names are: summary, liquids, layers, features, ores, gems, veins, shrubs, - and trees. If run during pre-embark, only the layers, ores, gems, and veins - report sections are available. -:``-v``, ``--values``: - Includes material value in the output. Most useful for the 'gems' report - section. - -**Examples:** - -``prospect all`` - Shows the entire report for the entire map. - -``prospect hell --show layers,ores,veins`` - Shows only the layers, ores, and other vein stone report sections, and - includes information on HFS tubes when a fort is loaded. - -``prospect all -sores`` - Show only information about ores for the pre-embark or fortress map report. - -**Pre-embark estimate:** - -If prospect is called during the embark selection screen, it displays an -estimate of layer stone availability. If the ``all`` keyword is specified, it -also estimates ores, gems, and vein material. The estimate covers all tiles of -the embark rectangle. - -.. note:: - - The results of pre-embark prospect are an *estimate*, and can at best be - expected to be somewhere within +/- 30% of the true amount; sometimes it - does a lot worse. Especially, it is not clear how to precisely compute how - many soil layers there will be in a given embark tile, so it can report a - whole extra layer, or omit one that is actually present. - -.. _remotefortressreader: - -remotefortressreader -==================== -An in-development plugin for realtime fortress visualisation. -See :forums:`Armok Vision <146473>`. - -.. _reveal: -.. _unreveal: -.. _revtoggle: -.. _revflood: -.. _revforget: - -reveal -====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. - -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. - -Usage and related commands: - -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. - -.. _showmood: - -showmood -======== -Shows all items needed for the currently active strange mood. - -.. _spectate: - -spectate -======== -Simple plugin to automate following random dwarves. Most of the time things will -be weighted towards z-levels with the highest job activity. Simply enter the -``spectate`` command to toggle the plugin's state. - -.. _plugin-stonesense: - -stonesense -========== -An isometric visualizer that runs in a second window. Usage: - -:stonesense: Open the visualiser in a new window. Alias ``ssense``. -:ssense overlay: Overlay DF window, replacing the map area. - -For more information, see `the full Stonesense README `. - -=========================== -Job and Fortress management -=========================== - -.. contents:: - :local: - - -.. _autobutcher: - -autobutcher -=========== -Assigns lifestock for slaughter once it reaches a specific count. Requires that -you add the target race(s) to a watch list. Only tame units will be processed. - -Units will be ignored if they are: - -* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool - individually, or `zone` ``nick`` for groups) -* Caged, if and only if the cage is defined as a room (to protect zoos) -* Trained for war or hunting - -Creatures who will not reproduce (because they're not interested in the -opposite sex or have been gelded) will be butchered before those who will. -Older adults and younger children will be butchered first if the population -is above the target (default 1 male, 5 female kids and adults). Note that -you may need to set a target above 1 to have a reliable breeding population -due to asexuality etc. See `fix-ster` if this is a problem. - -Options: - -:example: Print some usage examples. -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep : Changes the timer to sleep X frames between runs. -:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, - BIRD_TURKEY, etc) or a list of ids seperated by spaces or - the keyword 'all' which affects all races on your current - watchlist. -:unwatch R: Stop watching race(s). The current target settings will be - remembered. R can be a list of ids or the keyword 'all'. -:forget R: Stop watching race(s) and forget it's/their target settings. - R can be a list of ids or the keyword 'all'. -:autowatch: Automatically adds all new races (animals you buy from merchants, - tame yourself or get from migrants) to the watch list using - default target count. -:noautowatch: Stop auto-adding new races to the watchlist. -:list: Print the current status and watchlist. -:list_export: Print the commands needed to set up status and watchlist, - which can be used to import them to another save (see notes). -:target : - Set target count for specified race(s). The first four arguments - are the number of female and male kids, and female and male adults. - R can be a list of spceies ids, or the keyword ``all`` or ``new``. - ``R = 'all'``: change target count for all races on watchlist - and set the new default for the future. ``R = 'new'``: don't touch - current settings on the watchlist, only set the new default - for future entries. -:list_export: Print the commands required to rebuild your current settings. - -.. note:: - - Settings and watchlist are stored in the savegame, so that you can have - different settings for each save. If you want to copy your watchlist to - another savegame you must export the commands required to recreate your settings. - - To export, open an external terminal in the DF directory, and run - ``dfhack-run autobutcher list_export > filename.txt``. To import, load your - new save and run ``script filename.txt`` in the DFHack terminal. - - -Examples: - -You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, -1 male) of the race alpaca. Once the kids grow up the oldest adults will get -slaughtered. Excess kids will get slaughtered starting with the youngest -to allow that the older ones grow into adults. Any unnamed cats will -be slaughtered as soon as possible. :: - - autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY - autobutcher target 0 0 0 0 CAT - autobutcher watch ALPACA BIRD_TURKEY CAT - autobutcher start - -Automatically put all new races onto the watchlist and mark unnamed tame units -for slaughter as soon as they arrive in your fort. Settings already made -for specific races will be left untouched. :: - - autobutcher target 0 0 0 0 new - autobutcher autowatch - autobutcher start - -Stop watching the races alpaca and cat, but remember the target count -settings so that you can use 'unwatch' without the need to enter the -values again. Note: 'autobutcher unwatch all' works, but only makes sense -if you want to keep the plugin running with the 'autowatch' feature or manually -add some new races with 'watch'. If you simply want to stop it completely use -'autobutcher stop' instead. :: - - autobutcher unwatch ALPACA CAT - -.. _autochop: - -autochop -======== -Automatically manage tree cutting designation to keep available logs withing given -quotas. - -Open the dashboard by running:: - - enable autochop - -The plugin must be activated (with :kbd:`d`-:kbd:`t`-:kbd:`c`-:kbd:`a`) before -it can be used. You can then set logging quotas and restrict designations to -specific burrows (with 'Enter') if desired. The plugin's activity cycle runs -once every in game day. - -If you add ``enable autochop`` to your dfhack.init there will be a hotkey to -open the dashboard from the chop designation menu. - -.. _autoclothing: - -autoclothing -============ - -Automatically manage clothing work orders, allowing the user to set how many of -each clothing type every citizen should have. Usage:: - - autoclothing [number] - -Examples: - -* ``autoclothing cloth "short skirt" 10``: - Sets the desired number of cloth short skirts available per citizen to 10. -* ``autoclothing cloth dress``: - Displays the currently set number of cloth dresses chosen per citizen. - -.. _autodump: - -autodump -======== -This plugin adds an option to the :kbd:`q` menu for stckpiles when `enabled `. -When autodump is enabled for a stockpile, any items placed in the stockpile will -automatically be designated to be dumped. - -Alternatively, you can use it to quickly move all items designated to be dumped. -Items are instantly moved to the cursor position, the dump flag is unset, -and the forbid flag is set, as if it had been dumped normally. -Be aware that any active dump item tasks still point at the item. - -Cursor must be placed on a floor tile so the items can be dumped there. - -Options: - -:destroy: Destroy instead of dumping. Doesn't require a cursor. - If called again before the game is resumed, cancels destroy. -:destroy-here: As ``destroy``, but only the selected item in the :kbd:`k` list, - or inside a container. - Alias ``autodump-destroy-here``, for keybindings. - :dfhack-keybind:`autodump-destroy-here` -:visible: Only process items that are not hidden. -:hidden: Only process hidden items. -:forbidden: Only process forbidden items (default: only unforbidden). - -``autodump-destroy-item`` destroys the selected item, which may be selected -in the :kbd:`k` list, or inside a container. If called again before the game -is resumed, cancels destruction of the item. -:dfhack-keybind:`autodump-destroy-item` - -.. _autofarm: - -autofarm -======== - -Automatically handles crop selection in farm plots based on current plant -stocks, and selects crops for planting if current stock is below a threshold. -Selected crops are dispatched on all farmplots. (Note that this plugin replaces -an older Ruby script of the same name.) - -Use the `enable` or `disable ` commands to change whether this plugin is -enabled. - -Usage: - -* ``autofarm runonce``: - Updates all farm plots once, without enabling the plugin -* ``autofarm status``: - Prints status information, including any applied limits -* ``autofarm default 30``: - Sets the default threshold -* ``autofarm threshold 150 helmet_plump tail_pig``: - Sets thresholds of individual plants - -.. _autogems: - -autogems -======== -Creates a new Workshop Order setting, automatically cutting rough gems -when `enabled `. - -See `gui/autogems` for a configuration UI. If necessary, the ``autogems-reload`` -command reloads the configuration file produced by that script. - -.. _autohauler: - -autohauler -========== -Autohauler is an autolabor fork. - -Rather than the all-of-the-above means of autolabor, autohauler will instead -only manage hauling labors and leave skilled labors entirely to the user, who -will probably use Dwarf Therapist to do so. - -Idle dwarves will be assigned the hauling labors; everyone else (including -those currently hauling) will have the hauling labors removed. This is to -encourage every dwarf to do their assigned skilled labors whenever possible, -but resort to hauling when those jobs are not available. This also implies -that the user will have a very tight skill assignment, with most skilled -labors only being assigned to just one dwarf, no dwarf having more than two -active skilled labors, and almost every non-military dwarf having at least -one skilled labor assigned. - -Autohauler allows skills to be flagged as to prevent hauling labors from -being assigned when the skill is present. By default this is the unused -ALCHEMIST labor but can be changed by the user. - -.. _autolabor: - -autolabor -========= -Automatically manage dwarf labors to efficiently complete jobs. -Autolabor tries to keep as many dwarves as possible busy but -also tries to have dwarves specialize in specific skills. - -The key is that, for almost all labors, once a dwarf begins a job it will finish that -job even if the associated labor is removed. Autolabor therefore frequently checks -which dwarf or dwarves should take new jobs for that labor, and sets labors accordingly. -Labors with equipment (mining, hunting, and woodcutting), which are abandoned -if labors change mid-job, are handled slightly differently to minimise churn. - -.. warning:: - - *autolabor will override any manual changes you make to labors while - it is enabled, including through other tools such as Dwarf Therapist* - -Simple usage: - -:enable autolabor: Enables the plugin with default settings. (Persistent per fortress) -:disable autolabor: Disables the plugin. - -Anything beyond this is optional - autolabor works well on the default settings. - -By default, each labor is assigned to between 1 and 200 dwarves (2-200 for mining). -By default 33% of the workforce become haulers, who handle all hauling jobs as well -as cleaning, pulling levers, recovering wounded, removing constructions, and filling ponds. -Other jobs are automatically assigned as described above. Each of these settings can be adjusted. - -Jobs are rarely assigned to nobles with responsibilities for meeting diplomats or merchants, -never to the chief medical dwarf, and less often to the bookeeper and manager. - -Hunting is never assigned without a butchery, and fishing is never assigned without a fishery. - -For each labor a preference order is calculated based on skill, biased against masters of other -trades and excluding those who can't do the job. The labor is then added to the best -dwarves for that labor. We assign at least the minimum number of dwarfs, in order of preference, -and then assign additional dwarfs that meet any of these conditions: - -* The dwarf is idle and there are no idle dwarves assigned to this labor -* The dwarf has non-zero skill associated with the labor -* The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. - -We stop assigning dwarfs when we reach the maximum allowed. - -Advanced usage: - -:autolabor []: - Set number of dwarves assigned to a labor. -:autolabor haulers: Set a labor to be handled by hauler dwarves. -:autolabor disable: Turn off autolabor for a specific labor. -:autolabor reset: Return a labor to the default handling. -:autolabor reset-all: Return all labors to the default handling. -:autolabor list: List current status of all labors. -:autolabor status: Show basic status information. - -See `autolabor-artisans` for a differently-tuned setup. - -Examples: - -``autolabor MINE`` - Keep at least 5 dwarves with mining enabled. -``autolabor CUT_GEM 1 1`` - Keep exactly 1 dwarf with gemcutting enabled. -``autolabor COOK 1 1 3`` - Keep 1 dwarf with cooking enabled, selected only from the top 3. -``autolabor FEED_WATER_CIVILIANS haulers`` - Have haulers feed and water wounded dwarves. -``autolabor CUTWOOD disable`` - Turn off autolabor for wood cutting. - -.. _autonestbox: - -autonestbox -=========== -Assigns unpastured female egg-layers to nestbox zones. Requires that you create -pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox -must be in the top left corner. Only 1 unit will be assigned per pen, regardless -of the size. The age of the units is currently not checked, most birds grow up -quite fast. Egglayers who are also grazers will be ignored, since confining them -to a 1x1 pasture is not a good idea. Only tame and domesticated own units are -processed since pasturing half-trained wild egglayers could destroy your neat -nestbox zones when they revert to wild. When called without options autonestbox -will instantly run once. - -Options: - -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep: Must be followed by number X. Changes the timer to sleep X - frames between runs. - -.. _clean: - -clean -===== -Cleans all the splatter that get scattered all over the map, items and -creatures. In an old fortress, this can significantly reduce FPS lag. It can -also spoil your !!FUN!!, so think before you use it. - -Options: - -:map: Clean the map tiles. By default, it leaves mud and snow alone. -:units: Clean the creatures. Will also clean hostiles. -:items: Clean all the items. Even a poisoned blade. - -Extra options for ``map``: - -:mud: Remove mud in addition to the normal stuff. -:snow: Also remove snow coverings. - -.. _cleanowned: - -cleanowned -========== -Confiscates items owned by dwarfs. By default, owned food on the floor -and rotten items are confistacted and dumped. - -Options: - -:all: confiscate all owned items -:scattered: confiscated and dump all items scattered on the floor -:x: confiscate/dump items with wear level 'x' and more -:X: confiscate/dump items with wear level 'X' and more -:dryrun: a dry run. combine with other options to see what will happen - without it actually happening. - -Example: - -``cleanowned scattered X`` - This will confiscate rotten and dropped food, garbage on the floors and any - worn items with 'X' damage and above. - -.. _dwarfmonitor: - -dwarfmonitor -============ -Records dwarf activity to measure fort efficiency. - -Options: - -:enable : Start monitoring ``mode``. ``mode`` can be "work", "misery", - "weather", or "all". This will enable all corresponding widgets, - if applicable. -:disable : Stop monitoring ``mode``, and disable corresponding widgets, if applicable. -:stats: Show statistics summary -:prefs: Show dwarf preferences summary -:reload: Reload configuration file (``dfhack-config/dwarfmonitor.json``) - -:dfhack-keybind:`dwarfmonitor` - -Widget configuration: - -The following types of widgets (defined in :file:`hack/lua/plugins/dwarfmonitor.lua`) -can be displayed on the main fortress mode screen: - -:date: Show the in-game date -:misery: Show overall happiness levels of all dwarves -:weather: Show current weather (rain/snow) -:cursor: Show the current mouse cursor position - -The file :file:`dfhack-config/dwarfmonitor.json` can be edited to control the -positions and settings of all widgets displayed. This file should contain a -JSON object with the key ``widgets`` containing an array of objects - see the -included file in the ``dfhack-config`` folder for an example: - -.. code-block:: lua - - { - "widgets": [ - { - "type": "widget type (weather, misery, etc.)", - "x": X coordinate, - "y": Y coordinate - <...additional options...> - } - ] - } - -X and Y coordinates begin at zero (in the upper left corner of the screen). -Negative coordinates will be treated as distances from the lower right corner, -beginning at 1 - e.g. an x coordinate of 0 is the leftmost column, while an x -coordinate of 1 is the rightmost column. - -By default, the x and y coordinates given correspond to the leftmost tile of -the widget. Including an ``anchor`` option set to ``right`` will cause the -rightmost tile of the widget to be located at this position instead. - -Some widgets support additional options: - -* ``date`` widget: - - * ``format``: specifies the format of the date. The following characters - are replaced (all others, such as punctuation, are not modified) - - * ``Y`` or ``y``: The current year - * ``M``: The current month, zero-padded if necessary - * ``m``: The current month, *not* zero-padded - * ``D``: The current day, zero-padded if necessary - * ``d``: The current day, *not* zero-padded - - The default date format is ``Y-M-D``, per the ISO8601_ standard. - - .. _ISO8601: https://en.wikipedia.org/wiki/ISO_8601 - -* ``cursor`` widget: - - * ``format``: Specifies the format. ``X``, ``x``, ``Y``, and ``y`` are - replaced with the corresponding cursor cordinates, while all other - characters are unmodified. - * ``show_invalid``: If set to ``true``, the mouse coordinates will both be - displayed as ``-1`` when the cursor is outside of the DF window; otherwise, - nothing will be displayed. - -.. _dwarfvet: - -dwarfvet -======== -Enables Animal Caretaker functionality - -Always annoyed your dragons become useless after a minor injury? Well, with -dwarfvet, your animals become first rate members of your fort. It can also -be used to train medical skills. - -Animals need to be treated in an animal hospital, which is simply a hospital -that is also an animal training zone. The console will print out a list on game -load, and whenever one is added or removed. Dwarfs must have the Animal Caretaker -labor to treat animals. Normal medical skills are used (and no experience is given -to the Animal Caretaker skill). - -Options: - -:enable: Enables Animal Caretakers to treat and manage animals -:disable: Turns off the plguin -:report: Reports all zones that the game considers animal hospitals - -.. _fix-job-postings: - -fix-job-postings ----------------- -This command fixes crashes caused by previous versions of workflow, mostly in -DFHack 0.40.24-r4, and should be run automatically when loading a world (but can -also be run manually if desired). - -.. _job: - -job -=== -Command for general job query and manipulation. - -Options: - -*no extra options* - Print details of the current job. The job can be selected - in a workshop, or the unit/jobs screen. -**list** - Print details of all jobs in the selected workshop. -**item-material ** - Replace the exact material id in the job item. -**item-type ** - Replace the exact item type id in the job item. - -.. _job-duplicate: - -job-duplicate -============= -In :kbd:`q` mode, when a job is highlighted within a workshop or furnace -building, calling ``job-duplicate`` instantly duplicates the job. - -:dfhack-keybind:`job-duplicate` - -.. _job-material: - -job-material -============ -Alter the material of the selected job. Similar to ``job item-material ...`` - -Invoked as:: - - job-material - -:dfhack-keybind:`job-material` - -* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, - changes the material of the job. Only inorganic materials can be used - in this mode. -* In :kbd:`b` mode, during selection of building components positions the cursor - over the first available choice with the matching material. - -.. _labormanager: - -labormanager -============ -Automatically manage dwarf labors to efficiently complete jobs. -Labormanager is derived from autolabor (above) but uses a completely -different approach to assigning jobs to dwarves. While autolabor tries -to keep as many dwarves busy as possible, labormanager instead strives -to get jobs done as quickly as possible. - -Labormanager frequently scans the current job list, current list of -dwarfs, and the map to determine how many dwarves need to be assigned to -what labors in order to meet all current labor needs without starving -any particular type of job. - -.. warning:: - - *As with autolabor, labormanager will override any manual changes you - make to labors while it is enabled, including through other tools such - as Dwarf Therapist* - -Simple usage: - -:enable labormanager: Enables the plugin with default settings. - (Persistent per fortress) - -:disable labormanager: Disables the plugin. - -Anything beyond this is optional - labormanager works fairly well on the -default settings. - -The default priorities for each labor vary (some labors are higher -priority by default than others). The way the plugin works is that, once -it determines how many of each labor is needed, it then sorts them by -adjusted priority. (Labors other than hauling have a bias added to them -based on how long it's been since they were last used, to prevent job -starvation.) The labor with the highest priority is selected, the "best -fit" dwarf for that labor is assigned to that labor, and then its -priority is *halved*. This process is repeated until either dwarfs or -labors run out. - -Because there is no easy way to detect how many haulers are actually -needed at any moment, the plugin always ensures that at least one dwarf -is assigned to each of the hauling labors, even if no hauling jobs are -detected. At least one dwarf is always assigned to construction removing -and cleaning because these jobs also cannot be easily detected. Lever -pulling is always assigned to everyone. Any dwarfs for which there are -no jobs will be assigned hauling, lever pulling, and cleaning labors. If -you use animal trainers, note that labormanager will misbehave if you -assign specific trainers to specific animals; results are only guaranteed -if you use "any trainer", and animal trainers will probably be -overallocated in any case. - -Labormanager also sometimes assigns extra labors to currently busy -dwarfs so that when they finish their current job, they will go off and -do something useful instead of standing around waiting for a job. - -There is special handling to ensure that at least one dwarf is assigned -to haul food whenever food is detected left in a place where it will rot -if not stored. This will cause a dwarf to go idle if you have no -storepiles to haul food to. - -Dwarfs who are unable to work (child, in the military, wounded, -handless, asleep, in a meeting) are entirely excluded from labor -assignment. Any dwarf explicitly assigned to a burrow will also be -completely ignored by labormanager. - -The fitness algorithm for assigning jobs to dwarfs generally attempts to -favor dwarfs who are more skilled over those who are less skilled. It -also tries to avoid assigning female dwarfs with children to jobs that -are "outside", favors assigning "outside" jobs to dwarfs who are -carrying a tool that could be used as a weapon, and tries to minimize -how often dwarfs have to reequip. - -Labormanager automatically determines medical needs and reserves health -care providers as needed. Note that this may cause idling if you have -injured dwarfs but no or inadequate hospital facilities. - -Hunting is never assigned without a butchery, and fishing is never -assigned without a fishery, and neither of these labors is assigned -unless specifically enabled. - -The method by which labormanager determines what labor is needed for a -particular job is complicated and, in places, incomplete. In some -situations, labormanager will detect that it cannot determine what labor -is required. It will, by default, pause and print an error message on -the dfhack console, followed by the message "LABORMANAGER: Game paused -so you can investigate the above message.". If this happens, please open -an issue on github, reporting the lines that immediately preceded this -message. You can tell labormanager to ignore this error and carry on by -typing ``labormanager pause-on-error no``, but be warned that some job may go -undone in this situation. - -Advanced usage: - -:labormanager enable: Turn plugin on. -:labormanager disable: Turn plugin off. -:labormanager priority : Set the priority value (see above) for labor to . -:labormanager reset : Reset the priority value of labor to its default. -:labormanager reset-all: Reset all priority values to their defaults. -:labormanager allow-fishing: Allow dwarfs to fish. *Warning* This tends to result in most of the fort going fishing. -:labormanager forbid-fishing: Forbid dwarfs from fishing. Default behavior. -:labormanager allow-hunting: Allow dwarfs to hunt. *Warning* This tends to result in as many dwarfs going hunting as you have crossbows. -:labormanager forbid-hunting: Forbid dwarfs from hunting. Default behavior. -:labormanager list: Show current priorities and current allocation stats. -:labormanager pause-on-error yes: Make labormanager pause if the labor inference engine fails. See above. -:labormanager pause-on-error no: Allow labormanager to continue past a labor inference engine failure. - -.. _nestboxes: - -nestboxes -========= - -Automatically scan for and forbid fertile eggs incubating in a nestbox. -Toggle status with `enable` or `disable `. - -.. _orders: - -orders -====== - -A plugin for manipulating manager orders. - -Subcommands: - -:list: Shows the list of previously exported orders, including the orders library. -:export NAME: Exports the current list of manager orders to a file named ``dfhack-config/orders/NAME.json``. -:import NAME: Imports manager orders from a file named ``dfhack-config/orders/NAME.json``. -:clear: Deletes all manager orders in the current embark. -:sort: Sorts current manager orders by repeat frequency so daily orders don't - prevent other orders from ever being completed: one-time orders first, then - yearly, seasonally, monthly, then finally daily. - -You can keep your orders automatically sorted by adding the following command to -your ``onMapLoad.init`` file:: - - repeat -name orders-sort -time 1 -timeUnits days -command [ orders sort ] - - -The orders library ------------------- - -DFHack comes with a library of useful manager orders that are ready for import: - -:source:`basic.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This collection of orders handles basic fort necessities: - -- prepared meals and food products (and by-products like oil) -- booze/mead -- thread/cloth/dye -- pots/jugs/buckets/mugs -- bags of leather, cloth, silk, and yarn -- crafts and totems from otherwise unusable by-products -- mechanisms/cages -- splints/crutches -- lye/soap -- ash/potash -- beds/wheelbarrows/minecarts -- scrolls - -You should import it as soon as you have enough dwarves to perform the tasks. -Right after the first migration wave is usually a good time. - -:source:`furnace.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This collection creates basic items that require heat. It is separated out from -``basic.json`` to give players the opportunity to set up magma furnaces first in -order to save resources. It handles: - -- charcoal (including smelting of bituminous coal and lignite) -- pearlash -- sand -- green/clear/crystal glass -- adamantine processing -- item melting - -Orders are missing for plaster powder until DF :bug:`11803` is fixed. - -:source:`military.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This collection adds high-volume smelting jobs for military-grade metal ores and -produces weapons and armor: - -- leather backpacks/waterskins/cloaks/quivers/armor -- bone/wooden bolts -- smelting for platinum, silver, steel, bronze, bismuth bronze, and copper (and - their dependencies) -- bronze/bismuth bronze/copper bolts -- platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, - with checks to ensure only the best available materials are being used - -If you set a stockpile to take weapons and armor of less than masterwork quality -and turn on `automelt` (like what `dreamfort` provides on its industry level), -these orders will automatically upgrade your military equipment to masterwork. -Make sure you have a lot of fuel (or magma forges and furnaces) before you turn -``automelt`` on, though! - -This file should only be imported, of course, if you need to equip a military. - -:source:`smelting.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This collection adds smelting jobs for all ores. It includes handling the ores -already managed by ``military.json``, but has lower limits. This ensures all -ores will be covered if a player imports ``smelting`` but not ``military``, but -the higher-volume ``military`` orders will take priority if both are imported. - -:source:`rockstock.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This collection of orders keeps a small stock of all types of rock furniture. -This allows you to do ad-hoc furnishings of guildhalls, libraries, temples, or -other rooms with `buildingplan` and your masons will make sure there is always -stock on hand to fulfill the plans. - -:source:`glassstock.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Similar to ``rockstock`` above, this collection keeps a small stock of all types -of glass furniture. If you have a functioning glass industry, this is more -sustainable than ``rockstock`` since you can never run out of sand. If you have -plenty of rock and just want the variety, you can import both ``rockstock`` and -``glassstock`` to get a mixture of rock and glass furnishings in your fort. - -There are a few items that ``glassstock`` produces that ``rockstock`` does not, -since there are some items that can not be made out of rock, for example: - -- tubes and corkscrews for building magma-safe screw pumps -- windows -- terrariums (as an alternative to wooden cages) - -.. _seedwatch: - -seedwatch -========= -Watches the numbers of seeds available and enables/disables seed and plant cooking. - -Each plant type can be assigned a limit. If their number falls below that limit, -the plants and seeds of that type will be excluded from cookery. -If the number rises above the limit + 20, then cooking will be allowed. - -The plugin needs a fortress to be loaded and will deactivate automatically otherwise. -You have to reactivate with 'seedwatch start' after you load the game. - -Options: - -:all: Adds all plants from the abbreviation list to the watch list. -:start: Start watching. -:stop: Stop watching. -:info: Display whether seedwatch is watching, and the watch list. -:clear: Clears the watch list. - -Examples: - -``seedwatch MUSHROOM_HELMET_PLUMP 30`` - add ``MUSHROOM_HELMET_PLUMP`` to the watch list, limit = 30 -``seedwatch MUSHROOM_HELMET_PLUMP`` - removes ``MUSHROOM_HELMET_PLUMP`` from the watch list. -``seedwatch all 30`` - adds all plants from the abbreviation list to the watch list, the limit being 30. - -.. _spotclean: - -spotclean -========= -Works like ``clean map snow mud``, but only for the tile under the cursor. Ideal -if you want to keep that bloody entrance ``clean map`` would clean up. - -:dfhack-keybind:`spotclean` - -.. _stockflow: - -stockflow -========= -Allows the fortress bookkeeper to queue jobs through the manager, -based on space or items available in stockpiles. - -Inspired by `workflow`. - -Usage: - -``stockflow enable`` - Enable the plugin. -``stockflow disable`` - Disable the plugin. -``stockflow fast`` - Enable the plugin in fast mode. -``stockflow list`` - List any work order settings for your stockpiles. -``stockflow status`` - Display whether the plugin is enabled. - -While enabled, the :kbd:`q` menu of each stockpile will have two new options: - -* :kbd:`j`: Select a job to order, from an interface like the manager's screen. -* :kbd:`J`: Cycle between several options for how many such jobs to order. - -Whenever the bookkeeper updates stockpile records, new work orders will -be placed on the manager's queue for each such selection, reduced by the -number of identical orders already in the queue. - -In fast mode, new work orders will be enqueued once per day, instead of -waiting for the bookkeeper. - -.. _tailor: - -tailor -====== - -Whenever the bookkeeper updates stockpile records, this plugin will scan every unit in the fort, -count up the number that are worn, and then order enough more made to replace all worn items. -If there are enough replacement items in inventory to replace all worn items, the units wearing them -will have the worn items confiscated (in the same manner as the `cleanowned` plugin) so that they'll -reeequip with replacement items. - -Use the `enable` and `disable ` commands to toggle this plugin's status, or run -``tailor status`` to check its current status. - -.. _workflow: - -workflow -======== -Manage control of repeat jobs. `gui/workflow` provides a simple -front-end integrated in the game UI. - -Usage: - -``workflow enable [option...], workflow disable [option...]`` - If no options are specified, enables or disables the plugin. - Otherwise, enables or disables any of the following options: - - - drybuckets: Automatically empty abandoned water buckets. - - auto-melt: Resume melt jobs when there are objects to melt. -``workflow jobs`` - List workflow-controlled jobs (if in a workshop, filtered by it). -``workflow list`` - List active constraints, and their job counts. -``workflow list-commands`` - List active constraints as workflow commands that re-create them; - this list can be copied to a file, and then reloaded using the - ``script`` built-in command. -``workflow count [cnt-gap]`` - Set a constraint, counting every stack as 1 item. -``workflow amount [cnt-gap]`` - Set a constraint, counting all items within stacks. -``workflow unlimit `` - Delete a constraint. -``workflow unlimit-all`` - Delete all constraints. - -Function --------- -When the plugin is enabled, it protects all repeat jobs from removal. -If they do disappear due to any cause, they are immediately re-added to their -workshop and suspended. - -In addition, when any constraints on item amounts are set, repeat jobs that -produce that kind of item are automatically suspended and resumed as the item -amount goes above or below the limit. The gap specifies how much below the limit -the amount has to drop before jobs are resumed; this is intended to reduce -the frequency of jobs being toggled. - -Constraint format ------------------ -The constraint spec consists of 4 parts, separated with ``/`` characters:: - - ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,] - -The first part is mandatory and specifies the item type and subtype, -using the raw tokens for items (the same syntax used custom reaction inputs). -For more information, see :wiki:`this wiki page `. - -The subsequent parts are optional: - -- A generic material spec constrains the item material to one of - the hard-coded generic classes, which currently include:: - - PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN - METAL STONE SAND GLASS CLAY MILK - -- A specific material spec chooses the material exactly, using the - raw syntax for reaction input materials, e.g. ``INORGANIC:IRON``, - although for convenience it also allows just ``IRON``, or ``ACACIA:WOOD`` etc. - See the link above for more details on the unabbreviated raw syntax. - -- A comma-separated list of miscellaneous flags, which currently can - be used to ignore imported items or items below a certain quality. - -Constraint examples -------------------- -Keep metal bolts within 900-1000, and wood/bone within 150-200:: - - workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100 - workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50 - -Keep the number of prepared food & drink stacks between 90 and 120:: - - workflow count FOOD 120 30 - workflow count DRINK 120 30 - -Make sure there are always 25-30 empty bins/barrels/bags:: - - workflow count BIN 30 - workflow count BARREL 30 - workflow count BOX/CLOTH,SILK,YARN 30 - -Make sure there are always 15-20 coal and 25-30 copper bars:: - - workflow count BAR//COAL 20 - workflow count BAR//COPPER 30 - -Produce 15-20 gold crafts:: - - workflow count CRAFTS//GOLD 20 - -Collect 15-20 sand bags and clay boulders:: - - workflow count POWDER_MISC/SAND 20 - workflow count BOULDER/CLAY 20 - -Make sure there are always 80-100 units of dimple dye:: - - workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 - -.. note:: - - In order for this to work, you have to set the material of the PLANT input - on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the `job item-material ` - command. Otherwise the plugin won't be able to deduce the output material. - -Maintain 10-100 locally-made crafts of exceptional quality:: - - workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90 - -.. _workNow: - -workNow -======= -Don't allow dwarves to idle if any jobs are available. - -When workNow is active, every time the game pauses, DF will make dwarves -perform any appropriate available jobs. This includes when you one step -through the game using the pause menu. Usage: - -:workNow: print workNow status -:workNow 0: deactivate workNow -:workNow 1: activate workNow (look for jobs on pause, and only then) -:workNow 2: make dwarves look for jobs whenever a job completes - -.. _zone: - -zone -==== -Helps a bit with managing activity zones (pens, pastures and pits) and cages. - -:dfhack-keybind:`zone` - -Options: - -:set: Set zone or cage under cursor as default for future assigns. -:assign: Assign unit(s) to the pen or pit marked with the 'set' command. - If no filters are set a unit must be selected in the in-game ui. - Can also be followed by a valid zone id which will be set - instead. -:unassign: Unassign selected creature from it's zone. -:nick: Mass-assign nicknames, must be followed by the name you want - to set. -:remnick: Mass-remove nicknames. -:enumnick: Assign enumerated nicknames (e.g. "Hen 1", "Hen 2"...). Must be - followed by the prefix to use in nicknames. -:tocages: Assign unit(s) to cages inside a pasture. -:uinfo: Print info about unit(s). If no filters are set a unit must - be selected in the in-game ui. -:zinfo: Print info about zone(s). If no filters are set zones under - the cursor are listed. -:verbose: Print some more info. -:filters: Print list of valid filter options. -:examples: Print some usage examples. -:not: Negates the next filter keyword. - -Filters: - -:all: Process all units (to be used with additional filters). -:count: Must be followed by a number. Process only n units (to be used - with additional filters). -:unassigned: Not assigned to zone, chain or built cage. -:minage: Minimum age. Must be followed by number. -:maxage: Maximum age. Must be followed by number. -:race: Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, - etc). Negatable. -:caged: In a built cage. Negatable. -:own: From own civilization. Negatable. -:merchant: Is a merchant / belongs to a merchant. Should only be used for - pitting, not for stealing animals (slaughter should work). -:war: Trained war creature. Negatable. -:hunting: Trained hunting creature. Negatable. -:tamed: Creature is tame. Negatable. -:trained: Creature is trained. Finds war/hunting creatures as well as - creatures who have a training level greater than 'domesticated'. - If you want to specifically search for war/hunting creatures use - 'war' or 'hunting' Negatable. -:trainablewar: Creature can be trained for war (and is not already trained for - war/hunt). Negatable. -:trainablehunt: Creature can be trained for hunting (and is not already trained - for war/hunt). Negatable. -:male: Creature is male. Negatable. -:female: Creature is female. Negatable. -:egglayer: Race lays eggs. Negatable. -:grazer: Race is a grazer. Negatable. -:milkable: Race is milkable. Negatable. - -Usage with single units ------------------------ -One convenient way to use the zone tool is to bind the command 'zone assign' to -a hotkey, maybe also the command 'zone set'. Place the in-game cursor over -a pen/pasture or pit, use 'zone set' to mark it. Then you can select units -on the map (in 'v' or 'k' mode), in the unit list or from inside cages -and use 'zone assign' to assign them to their new home. Allows pitting your -own dwarves, by the way. - -Usage with filters ------------------- -All filters can be used together with the 'assign' command. - -Restrictions: It's not possible to assign units who are inside built cages -or chained because in most cases that won't be desirable anyways. -It's not possible to cage owned pets because in that case the owner -uncages them after a while which results in infinite hauling back and forth. - -Usually you should always use the filter 'own' (which implies tame) unless you -want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless -you specify 'race DWARF' (so it's safe to use 'assign all own' to one big -pasture if you want to have all your animals at the same place). 'egglayer' and -'milkable' should be used together with 'female' unless you have a mod with -egg-laying male elves who give milk or whatever. Merchants and their animals are -ignored unless you specify 'merchant' (pitting them should be no problem, -but stealing and pasturing their animals is not a good idea since currently they -are not properly added to your own stocks; slaughtering them should work). - -Most filters can be negated (e.g. 'not grazer' -> race is not a grazer). - -Mass-renaming -------------- -Using the 'nick' command you can set the same nickname for multiple units. -If used without 'assign', 'all' or 'count' it will rename all units in the -current default target zone. Combined with 'assign', 'all' or 'count' (and -further optional filters) it will rename units matching the filter conditions. - -Cage zones ----------- -Using the 'tocages' command you can assign units to a set of cages, for example -a room next to your butcher shop(s). They will be spread evenly among available -cages to optimize hauling to and butchering from them. For this to work you need -to build cages and then place one pen/pasture activity zone above them, covering -all cages you want to use. Then use 'zone set' (like with 'assign') and use -'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it -would make no sense, but can be used together with 'nick' or 'remnick' and all -the usual filters. - -Examples --------- -``zone assign all own ALPACA minage 3 maxage 10`` - Assign all own alpacas who are between 3 and 10 years old to the selected - pasture. -``zone assign all own caged grazer nick ineedgrass`` - Assign all own grazers who are sitting in cages on stockpiles (e.g. after - buying them from merchants) to the selected pasture and give them - the nickname 'ineedgrass'. -``zone assign all own not grazer not race CAT`` - Assign all own animals who are not grazers, excluding cats. -``zone assign count 5 own female milkable`` - Assign up to 5 own female milkable creatures to the selected pasture. -``zone assign all own race DWARF maxage 2`` - Throw all useless kids into a pit :) -``zone nick donttouchme`` - Nicknames all units in the current default zone or cage to 'donttouchme'. - Mostly intended to be used for special pastures or cages which are not marked - as rooms you want to protect from autobutcher. -``zone tocages count 50 own tame male not grazer`` - Stuff up to 50 owned tame male animals who are not grazers into cages built - on the current default zone. - -================ -Map modification -================ - -.. contents:: - :local: - -.. _3dveins: - -3dveins -======= -Removes all existing veins from the map and generates new ones using -3D Perlin noise, in order to produce a layout that smoothly flows between -Z levels. The vein distribution is based on the world seed, so running -the command for the second time should produce no change. It is best to -run it just once immediately after embark. - -This command is intended as only a cosmetic change, so it takes -care to exactly preserve the mineral counts reported by `prospect` ``all``. -The amounts of different layer stones may slightly change in some cases -if vein mass shifts between Z layers. - -The only undo option is to restore your save from backup. - -.. _alltraffic: - -alltraffic -========== -Set traffic designations for every single tile of the map - useful for resetting -traffic designations. See also `filltraffic`, `restrictice`, and `restrictliquids`. - -Options: - -:H: High Traffic -:N: Normal Traffic -:L: Low Traffic -:R: Restricted Traffic - -.. _burrows: - -burrows -======= -Miscellaneous burrow control. Allows manipulating burrows and automated burrow -expansion while digging. - -Options: - -:enable feature ...: - Enable features of the plugin. -:disable feature ...: - Disable features of the plugin. -:clear-unit burrow burrow ...: - Remove all units from the burrows. -:clear-tiles burrow burrow ...: - Remove all tiles from the burrows. -:set-units target-burrow src-burrow ...: - Clear target, and adds units from source burrows. -:add-units target-burrow src-burrow ...: - Add units from the source burrows to the target. -:remove-units target-burrow src-burrow ...: - Remove units in source burrows from the target. -:set-tiles target-burrow src-burrow ...: - Clear target and adds tiles from the source burrows. -:add-tiles target-burrow src-burrow ...: - Add tiles from the source burrows to the target. -:remove-tiles target-burrow src-burrow ...: - Remove tiles in source burrows from the target. - - For these three options, in place of a source burrow it is - possible to use one of the following keywords: ABOVE_GROUND, - SUBTERRANEAN, INSIDE, OUTSIDE, LIGHT, DARK, HIDDEN, REVEALED - -Features: - -:auto-grow: When a wall inside a burrow with a name ending in '+' is dug - out, the burrow is extended to newly-revealed adjacent walls. - This final '+' may be omitted in burrow name args of commands above. - Digging 1-wide corridors with the miner inside the burrow is SLOW. - -.. _changeitem: - -changeitem -========== -Allows changing item material and base quality. By default the item currently -selected in the UI will be changed (you can select items in the 'k' list -or inside containers/inventory). By default change is only allowed if materials -is of the same subtype (for example wood<->wood, stone<->stone etc). But since -some transformations work pretty well and may be desired you can override this -with 'force'. Note that some attributes will not be touched, possibly resulting -in weirdness. To get an idea how the RAW id should look like, check some items -with 'info'. Using 'force' might create items which are not touched by -crafters/haulers. - -Options: - -:info: Don't change anything, print some info instead. -:here: Change all items at the cursor position. Requires in-game cursor. -:material, m: Change material. Must be followed by valid material RAW id. -:quality, q: Change base quality. Must be followed by number (0-5). -:force: Ignore subtypes, force change to new material. - -Examples: - -``changeitem m INORGANIC:GRANITE here`` - Change material of all items under the cursor to granite. -``changeitem q 5`` - Change currently selected item to masterpiece quality. - -.. _changelayer: - -changelayer -=========== -Changes material of the geology layer under cursor to the specified inorganic -RAW material. Can have impact on all surrounding regions, not only your embark! -By default changing stone to soil and vice versa is not allowed. By default -changes only the layer at the cursor position. Note that one layer can stretch -across lots of z levels. By default changes only the geology which is linked -to the biome under the cursor. That geology might be linked to other biomes -as well, though. Mineral veins and gem clusters will stay on the map. Use -`changevein` for them. - -tl;dr: You will end up with changing quite big areas in one go, especially if -you use it in lower z levels. Use with care. - -Options: - -:all_biomes: Change selected layer for all biomes on your map. - Result may be undesirable since the same layer can AND WILL - be on different z-levels for different biomes. Use the tool - 'probe' to get an idea how layers and biomes are distributed - on your map. -:all_layers: Change all layers on your map (only for the selected biome - unless 'all_biomes' is added). - Candy mountain, anyone? Will make your map quite boring, - but tidy. -:force: Allow changing stone to soil and vice versa. !!THIS CAN HAVE - WEIRD EFFECTS, USE WITH CARE!! - Note that soil will not be magically replaced with stone. - You will, however, get a stone floor after digging so it - will allow the floor to be engraved. - Note that stone will not be magically replaced with soil. - You will, however, get a soil floor after digging so it - could be helpful for creating farm plots on maps with no - soil. -:verbose: Give some details about what is being changed. -:trouble: Give some advice about known problems. - -Examples: - -``changelayer GRANITE`` - Convert layer at cursor position into granite. -``changelayer SILTY_CLAY force`` - Convert layer at cursor position into clay even if it's stone. -``changelayer MARBLE all_biomes all_layers`` - Convert all layers of all biomes which are not soil into marble. - -.. note:: - - * If you use changelayer and nothing happens, try to pause/unpause the game - for a while and try to move the cursor to another tile. Then try again. - If that doesn't help try temporarily changing some other layer, undo your - changes and try again for the layer you want to change. Saving - and reloading your map might also help. - * You should be fine if you only change single layers without the use - of 'force'. Still it's advisable to save your game before messing with - the map. - * When you force changelayer to convert soil to stone you might experience - weird stuff (flashing tiles, tiles changed all over place etc). - Try reverting the changes manually or even better use an older savegame. - You did save your game, right? - -.. _changevein: - -changevein -========== -Changes material of the vein under cursor to the specified inorganic RAW -material. Only affects tiles within the current 16x16 block - for veins and -large clusters, you will need to use this command multiple times. - -Example: - -``changevein NATIVE_PLATINUM`` - Convert vein at cursor position into platinum ore. - -.. _cleanconst: - -cleanconst -========== -Cleans up construction materials. - -This utility alters all constructions on the map so that they spawn their -building component when they are disassembled, allowing their actual -build items to be safely deleted. This can improve FPS in extreme situations. - -.. _deramp: - -deramp -====== -Removes all ramps designated for removal from the map. This is useful for -replicating the old channel digging designation. It also removes any and -all 'down ramps' that can remain after a cave-in (you don't have to designate -anything for that to happen). - -.. _dig: -.. _digv: -.. _digvx: -.. _digl: -.. _diglx: - -dig -=== -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - -:dfhack-keybind:`digv` - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. - -.. _digcircle: - -digcircle -========= -A command for easy designation of filled and hollow circles. -It has several types of options. - -Shape: - -:hollow: Set the circle to hollow (default) -:filled: Set the circle to filled -:#: Diameter in tiles (default = 0, does nothing) - -Action: - -:set: Set designation (default) -:unset: Unset current designation -:invert: Invert designations already present - -Designation types: - -:dig: Normal digging designation (default) -:ramp: Ramp digging -:ustair: Staircase up -:dstair: Staircase down -:xstair: Staircase up/down -:chan: Dig channel - -After you have set the options, the command called with no options -repeats with the last selected parameters. - -Examples: - -``digcircle filled 3`` - Dig a filled circle with diameter = 3. -``digcircle`` - Do it again. - -.. _digexp: - -digexp -====== -This command is for :wiki:`exploratory mining `. - -There are two variables that can be set: pattern and filter. - -Patterns: - -:diag5: diagonals separated by 5 tiles -:diag5r: diag5 rotated 90 degrees -:ladder: A 'ladder' pattern -:ladderr: ladder rotated 90 degrees -:clear: Just remove all dig designations -:cross: A cross, exactly in the middle of the map. - -Filters: - -:all: designate whole z-level -:hidden: designate only hidden tiles of z-level (default) -:designated: Take current designation and apply pattern to it. - -After you have a pattern set, you can use ``expdig`` to apply it again. - -Examples: - -``expdig diag5 hidden`` - Designate the diagonal 5 patter over all hidden tiles -``expdig`` - Apply last used pattern and filter -``expdig ladder designated`` - Take current designations and replace them with the ladder pattern - -.. _digFlood: - -digFlood -======== -Automatically digs out specified veins as they are discovered. It runs once -every time a dwarf finishes a dig job. It will only dig out appropriate tiles -that are adjacent to the finished dig job. To add a vein type, use ``digFlood 1 [type]``. -This will also enable the plugin. To remove a vein type, use ``digFlood 0 [type] 1`` -to disable, then remove, then re-enable. - -Usage: - -:help digflood: detailed help message -:digFlood 0: disable the plugin -:digFlood 1: enable the plugin -:digFlood 0 MICROCLINE COAL_BITUMINOUS 1: - disable plugin, remove microcline and bituminous coal from monitoring, then re-enable the plugin -:digFlood CLEAR: remove all inorganics from monitoring -:digFlood digAll1: ignore the monitor list and dig any vein -:digFlood digAll0: disable digAll mode - -.. _digtype: - -digtype -======= -For every tile on the map of the same vein type as the selected tile, -this command designates it to have the same designation as the -selected tile. If the selected tile has no designation, they will be -dig designated. -If an argument is given, the designation of the selected tile is -ignored, and all appropriate tiles are set to the specified -designation. - -Options: - -:dig: -:channel: -:ramp: -:updown: up/down stairs -:up: up stairs -:down: down stairs -:clear: clear designation - -.. _filltraffic: - -filltraffic -=========== -Set traffic designations using flood-fill starting at the cursor. -See also `alltraffic`, `restrictice`, and `restrictliquids`. Options: - -:H: High Traffic -:N: Normal Traffic -:L: Low Traffic -:R: Restricted Traffic -:X: Fill across z-levels. -:B: Include buildings and stockpiles. -:P: Include empty space. - -Example: - -``filltraffic H`` - When used in a room with doors, it will set traffic to HIGH in just that room. - -.. _getplants: - -getplants -========= -This tool allows plant gathering and tree cutting by RAW ID. Specify the types -of trees to cut down and/or shrubs to gather by their plant names, separated -by spaces. - -Options: - -:``-t``: Tree: Select trees only (exclude shrubs) -:``-s``: Shrub: Select shrubs only (exclude trees) -:``-f``: Farming: Designate only shrubs that yield seeds for farming. Implies -s -:``-c``: Clear: Clear designations instead of setting them -:``-x``: eXcept: Apply selected action to all plants except those specified (invert - selection) -:``-a``: All: Select every type of plant (obeys ``-t``/``-s``/``-f``) -:``-v``: Verbose: Lists the number of (un)designations per plant -:``-n *``: Number: Designate up to * (an integer number) plants of each species - -Specifying both ``-t`` and ``-s`` or ``-f`` will have no effect. If no plant IDs are -specified, all valid plant IDs will be listed, with ``-t``, ``-s``, and ``-f`` -restricting the list to trees, shrubs, and farmable shrubs, respectively. - -.. note:: - - DF is capable of determining that a shrub has already been picked, leaving - an unusable structure part behind. This plugin does not perform such a check - (as the location of the required information has not yet been identified). - This leads to some shrubs being designated when they shouldn't be, causing a - plant gatherer to walk there and do nothing (except clearing the - designation). See :issue:`1479` for details. - - The implementation another known deficiency: it's incapable of detecting that - raw definitions that specify a seed extraction reaction for the structural part - but has no other use for it cannot actually yield any seeds, as the part is - never used (parts of :bug:`6940`, e.g. Red Spinach), even though DF - collects it, unless there's a workshop reaction to do it (which there isn't - in vanilla). - -.. _infiniteSky: - -infiniteSky -=========== -Automatically allocates new z-levels of sky at the top of the map as you build up, -or on request allocates many levels all at once. - -Usage: - -``infiniteSky n`` - Raise the sky by n z-levels. -``infiniteSky enable/disable`` - Enables/disables monitoring of constructions. If you build anything in the second to highest z-level, it will allocate one more sky level. This is so you can continue to build stairs upward. - -.. warning:: - - :issue:`Sometimes <254>` new z-levels disappear and cause cave-ins. - Saving and loading after creating new z-levels should fix the problem. - -.. _liquids: - -liquids -======= -Allows adding magma, water and obsidian to the game. It replaces the normal -dfhack command line and can't be used from a hotkey. Settings will be remembered -as long as dfhack runs. Intended for use in combination with the command -``liquids-here`` (which can be bound to a hotkey). See also :issue:`80`. - -.. warning:: - - Spawning and deleting liquids can mess up pathing data and - temperatures (creating heat traps). You've been warned. - -.. note:: - - `gui/liquids` is an in-game UI for this script. - -Settings will be remembered until you quit DF. You can call `liquids-here` to execute -the last configured action, which is useful in combination with keybindings. - -Usage: point the DF cursor at a tile you want to modify and use the commands. - -If you only want to add or remove water or magma from one tile, -`source` may be easier to use. - -Commands --------- -Misc commands: - -:q: quit -:help, ?: print this list of commands -:: put liquid - -Modes: - -:m: switch to magma -:w: switch to water -:o: make obsidian wall instead -:of: make obsidian floors -:rs: make a river source -:f: flow bits only -:wclean: remove salt and stagnant flags from tiles - -Set-Modes and flow properties (only for magma/water): - -:s+: only add mode -:s.: set mode -:s-: only remove mode -:f+: make the spawned liquid flow -:f.: don't change flow state (read state in flow mode) -:f-: make the spawned liquid static - -Permaflow (only for water): - -:pf.: don't change permaflow state -:pf-: make the spawned liquid static -:pf[NS][EW]: make the spawned liquid permanently flow -:0-7: set liquid amount - -Brush size and shape: - -:p, point: Single tile -:r, range: Block with cursor at bottom north-west (any place, any size) -:block: DF map block with cursor in it (regular spaced 16x16x1 blocks) -:column: Column from cursor, up through free space -:flood: Flood-fill water tiles from cursor (only makes sense with wclean) - -.. _liquids-here: - -liquids-here ------------- -Run the liquid spawner with the current/last settings made in liquids (if no -settings in liquids were made it paints a point of 7/7 magma by default). - -Intended to be used as keybinding. Requires an active in-game cursor. - -.. _plant: - -plant -===== -A tool for creating shrubs, growing, or getting rid of them. - -Subcommands: - -:create: Creates a new sapling under the cursor. Takes a raw ID as argument - (e.g. TOWER_CAP). The cursor must be located on a dirt or grass floor tile. -:grow: Turns saplings into trees; under the cursor if a sapling is selected, - or every sapling on the map if the cursor is hidden. - -For mass effects, use one of the additional options: - -:shrubs: affect all shrubs on the map -:trees: affect all trees on the map -:all: affect every plant! - -.. _regrass: - -regrass -======= -Regrows all the grass. Not much to it ;) - -.. _restrictice: - -restrictice -=========== -Restrict traffic on all tiles on top of visible ice. -See also `alltraffic`, `filltraffic`, and `restrictliquids`. - -.. _restrictliquids: - -restrictliquids -=============== -Restrict traffic on all visible tiles with liquid. -See also `alltraffic`, `filltraffic`, and `restrictice`. - -.. _tiletypes: - -tiletypes -========= -Can be used for painting map tiles and is an interactive command, much like -`liquids`. Some properties of existing tiles can be looked up with `probe`. If -something goes wrong, `fixveins` may help. - -The tool works with two set of options and a brush. The brush determines which -tiles will be processed. First set of options is the filter, which can exclude -some of the tiles from the brush by looking at the tile properties. The second -set of options is the paint - this determines how the selected tiles are -changed. - -Both paint and filter can have many different properties including things like -general shape (WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, -etc.), state of 'designated', 'hidden' and 'light' flags. - -The properties of filter and paint can be partially defined. This means that -you can for example turn all stone fortifications into floors, preserving the -material:: - - filter material STONE - filter shape FORTIFICATION - paint shape FLOOR - -Or turn mineral vein floors back into walls:: - - filter shape FLOOR - filter material MINERAL - paint shape WALL - -The tool also allows tweaking some tile flags:: - - paint hidden 1 - paint hidden 0 - -This will hide previously revealed tiles (or show hidden with the 0 option). - -More recently, the tool supports changing the base material of the tile to -an arbitrary stone from the raws, by creating new veins as required. Note -that this mode paints under ice and constructions, instead of overwriting -them. To enable, use:: - - paint stone MICROCLINE - -This mode is incompatible with the regular ``material`` setting, so changing -it cancels the specific stone selection:: - - paint material ANY - -Since different vein types have different drop rates, it is possible to choose -which one to use in painting:: - - paint veintype CLUSTER_SMALL - -When the chosen type is ``CLUSTER`` (the default), the tool may automatically -choose to use layer stone or lava stone instead of veins if its material matches -the desired one. - -Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:: - - paint hidden ANY - paint shape ANY - filter material any - filter shape any - filter any - -You can use several different brushes for painting tiles: - -:point: a single tile -:range: a rectangular range -:column: a column ranging from current cursor to the first solid tile above -:block: a DF map block - 16x16 tiles, in a regular grid - -Example:: - - range 10 10 1 - -This will change the brush to a rectangle spanning 10x10 tiles on one z-level. -The range starts at the position of the cursor and goes to the east, south and -up. - -For more details, use ``tiletypes help``. - -.. _tiletypes-command: - -tiletypes-command ------------------ -Runs tiletypes commands, separated by ``;``. This makes it possible to change -tiletypes modes from a hotkey or via dfhack-run. - -Example:: - - tiletypes-command p any ; p s wall ; p sp normal - -This resets the paint filter to unsmoothed walls. - -.. _tiletypes-here: - -tiletypes-here --------------- -Apply the current tiletypes options at the in-game cursor position, including -the brush. Can be used from a hotkey. - -Options: - -:``-c``, ``--cursor ,,``: - Use the specified map coordinates instead of the current cursor position. If - this option is specified, then an active game map cursor is not necessary. -:``-h``, ``--help``: - Show command help text. -:``-q``, ``--quiet``: - Suppress non-error status output. - -.. _tiletypes-here-point: - -tiletypes-here-point --------------------- -Apply the current tiletypes options at the in-game cursor position to a single -tile. Can be used from a hotkey. - -This command supports the same options as `tiletypes-here` above. - -.. _tubefill: - -tubefill -======== -Fills all the adamantine veins again. Veins that were hollow will be left -alone. - -Options: - -:hollow: fill in naturally hollow veins too - -Beware that filling in hollow veins will trigger a demon invasion on top of -your miner when you dig into the region that used to be hollow. - - - -================= -Mods and Cheating -================= - -.. contents:: - :local: - -.. _add-spatter: - -add-spatter -=========== -This plugin makes reactions with names starting with ``SPATTER_ADD_`` -produce contaminants on the items instead of improvements. The plugin is -intended to give some use to all those poisons that can be bought from caravans, -so they're immune to being washed away by water or destroyed by `clean`. - -.. _adv-bodyswap: - -adv-bodyswap -============ -This allows taking control over your followers and other creatures in adventure -mode. For example, you can make them pick up new arms and armor and equip them -properly. - -Usage: - -* When viewing unit details, body-swaps into that unit. -* In the main adventure mode screen, reverts transient swap. - -:dfhack-keybind:`adv-bodyswap` - -.. _createitem: - -createitem -========== -Allows creating new items of arbitrary types and made of arbitrary materials. A -unit must be selected in-game to use this command. By default, items created are -spawned at the feet of the selected unit. - -Specify the item and material information as you would indicate them in -custom reaction raws, with the following differences: - -* Separate the item and material with a space rather than a colon -* If the item has no subtype, the ``:NONE`` can be omitted -* If the item is ``REMAINS``, ``FISH``, ``FISH_RAW``, ``VERMIN``, ``PET``, or ``EGG``, - specify a ``CREATURE:CASTE`` pair instead of a material token. -* If the item is a ``PLANT_GROWTH``, specify a ``PLANT_ID:GROWTH_ID`` pair - instead of a material token. - -Corpses, body parts, and prepared meals cannot be created using this tool. - -To obtain the item and material tokens of an existing item, run -``createitem inspect``. Its output can be passed directly as arguments to -``createitem`` to create new matching items, as long as the item type is -supported. - -Examples: - -* Create 2 pairs of steel gauntlets:: - - createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2 - -* Create tower-cap logs:: - - createitem WOOD PLANT_MAT:TOWER_CAP:WOOD - -* Create bilberries:: - - createitem PLANT_GROWTH BILBERRY:FRUIT - -For more examples, :wiki:`see this wiki page `. - -To change where new items are placed, first run the command with a -destination type while an appropriate destination is selected. - -Options: - -:floor: Subsequent items will be placed on the floor beneath the selected unit's feet. -:item: Subsequent items will be stored inside the currently selected item. -:building: Subsequent items will become part of the currently selected building. - Good for loading traps; do not use with workshops (or deconstruct to use the item). - -.. _dig-now: - -dig-now -======= - -Instantly completes non-marker dig designations, modifying tile shapes and -creating boulders, ores, and gems as if a miner were doing the mining or -engraving. By default, the entire map is processed and boulder generation -follows standard game rules, but the behavior is configurable. - -Note that no units will get mining or engraving experience for the dug/engraved -tiles. - -Trees and roots are not currently handled by this plugin and will be skipped. -Requests for engravings are also skipped since they would depend on the skill -and creative choices of individual engravers. Other types of engraving (i.e. -smoothing and track carving) are handled. - -Usage:: - - dig-now [ []] [] - -Where the optional ```` pair can be used to specify the coordinate bounds -within which ``dig-now`` will operate. If they are not specified, ``dig-now`` -will scan the entire map. If only one ```` is specified, only the tile at -that coordinate is processed. - -Any ```` parameters can either be an ``,,`` triple (e.g. -``35,12,150``) or the string ``here``, which means the position of the active -game cursor should be used. - -Examples: - -``dig-now`` - Dig designated tiles according to standard game rules. - -``dig-now --clean`` - Dig designated tiles, but don't generate any boulders, ores, or gems. - -``dig-now --dump here`` - Dig tiles and dump all generated boulders, ores, and gems at the tile under - the game cursor. - -Options: - -:``-c``, ``--clean``: - Don't generate any boulders, ores, or gems. Equivalent to - ``--percentages 0,0,0,0``. -:``-d``, ``--dump ``: - Dump any generated items at the specified coordinates. If the tile at those - coordinates is open space or is a wall, items will be generated on the - closest walkable tile below. -:``-e``, ``--everywhere``: - Generate a boulder, ore, or gem for every tile that can produce one. - Equivalent to ``--percentages 100,100,100,100``. -:``-h``, ``--help``: - Show quick usage help text. -:``-p``, ``--percentages ,,,``: - Set item generation percentages for each of the tile categories. The - ``vein`` category includes both the large oval clusters and the long stringy - mineral veins. Default is ``25,33,100,100``. -:``-z``, ``--cur-zlevel``: - Restricts the bounds to the currently visible z-level. - -.. _diggingInvaders: - -diggingInvaders -=============== -Makes invaders dig or destroy constructions to get to your dwarves. - -To enable/disable the pluging, use: ``diggingInvaders (1|enable)|(0|disable)`` - -Basic usage: - -:add GOBLIN: registers the race GOBLIN as a digging invader. Case-sensitive. -:remove GOBLIN: unregisters the race GOBLIN as a digging invader. Case-sensitive. -:now: makes invaders try to dig now, if plugin is enabled -:clear: clears all digging invader races -:edgesPerTick n: makes the pathfinding algorithm work on at most n edges per tick. - Set to 0 or lower to make it unlimited. - -You can also use ``diggingInvaders setCost (race) (action) n`` to set the -pathing cost of particular action, or ``setDelay`` to set how long it takes. -Costs and delays are per-tile, and the table shows default values. - -============================== ======= ====== ================================= -Action Cost Delay Notes -============================== ======= ====== ================================= -``walk`` 1 0 base cost in the path algorithm -``destroyBuilding`` 2 1,000 delay adds to the job_completion_timer of destroy building jobs that are assigned to invaders -``dig`` 10,000 1,000 digging soil or natural stone -``destroyRoughConstruction`` 1,000 1,000 constructions made from boulders -``destroySmoothConstruction`` 100 100 constructions made from blocks or bars -============================== ======= ====== ================================= - - -.. _fastdwarf: - -fastdwarf -========= -Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly -and perform tasks quickly. Teledwarf makes dwarves move instantaneously, -but do jobs at the same speed. - -:fastdwarf 0: disables both (also ``0 0``) -:fastdwarf 1: enables speedydwarf and disables teledwarf (also ``1 0``) -:fastdwarf 2: sets a native debug flag in the game memory that implements an - even more aggressive version of speedydwarf. -:fastdwarf 0 1: disables speedydwarf and enables teledwarf -:fastdwarf 1 1: enables both - -See `superdwarf` for a per-creature version. - -.. _forceequip: - -forceequip -========== -Forceequip moves local items into a unit's inventory. It is typically used to -equip specific clothing/armor items onto a dwarf, but can also be used to put -armor onto a war animal or to add unusual items (such as crowns) to any unit. - -For more information run ``forceequip help``. See also `modtools/equip-item`. - -.. _generated-creature-renamer: - -generated-creature-renamer -========================== -Automatically renames generated creatures, such as forgotten beasts, titans, -etc, to have raw token names that match the description given in-game. - -The ``list-generated`` command can be used to list the token names of all -generated creatures in a given save, with an optional ``detailed`` argument -to show the accompanying description. - -The ``save-generated-raws`` command will save a sample creature graphics file in -the Dwarf Fortress root directory, to use as a start for making a graphics set -for generated creatures using the new names that they get with this plugin. - -The new names are saved with the save, and the plugin, when enabled, only runs once -per save, unless there's an update. - -.. _lair: - -lair -==== -This command allows you to mark the map as a monster lair, preventing item -scatter on abandon. When invoked as ``lair reset``, it does the opposite. - -Unlike `reveal`, this command doesn't save the information about tiles - you -won't be able to restore state of real monster lairs using ``lair reset``. - -Options: - -:lair: Mark the map as monster lair -:lair reset: Mark the map as ordinary (not lair) - -.. _misery: - -misery -====== -When enabled, fake bad thoughts will be added to all dwarves. - -Usage: - -:misery enable n: enable misery with optional magnitude n. If specified, n must - be positive. -:misery n: same as "misery enable n" -:misery enable: same as "misery enable 1" -:misery disable: stop adding new negative thoughts. This will not remove - existing negative thoughts. Equivalent to "misery 0". -:misery clear: remove fake thoughts, even after saving and reloading. Does - not change factor. - -.. _mode: - -mode -==== -This command lets you see and change the game mode directly. - -.. warning:: - - Only use ``mode`` after making a backup of your save! - - Not all combinations are good for every situation and most of them will - produce undesirable results. There are a few good ones though. - -Examples: - - * You are in fort game mode, managing your fortress and paused. - * You switch to the arena game mode, *assume control of a creature* and then - * switch to adventure game mode(1). - You just lost a fortress and gained an adventurer. Alternatively: - - * You are in fort game mode, managing your fortress and paused at the esc menu. - * You switch to the adventure game mode, assume control of a creature, then save or retire. - * You just created a returnable mountain home and gained an adventurer. - -.. _power-meter: - -power-meter -=========== -The power-meter plugin implements a modified pressure plate that detects power being -supplied to gear boxes built in the four adjacent N/S/W/E tiles. - -The configuration front-end is implemented by `gui/power-meter`. - -.. _siege-engine: - -siege-engine -============ -Siege engines in DF haven't been updated since the game was 2D, and can -only aim in four directions. To make them useful above-ground, -this plugin allows you to: - -* link siege engines to stockpiles -* restrict operator skill levels (like workshops) -* load any object into a catapult, not just stones -* aim at a rectangular area in any direction, and across Z-levels - -The front-end is implemented by `gui/siege-engine`. - -.. _steam-engine: - -steam-engine -============ -The steam-engine plugin detects custom workshops with STEAM_ENGINE in -their token, and turns them into real steam engines. - -The vanilla game contains only water wheels and windmills as sources of -power, but windmills give relatively little power, and water wheels require -flowing water, which must either be a real river and thus immovable and -limited in supply, or actually flowing and thus laggy. - -Compared to the :wiki:`water reactor ` -exploit, steam engines make a lot of sense! - -Construction ------------- -The workshop needs water as its input, which it takes via a -passable floor tile below it, like usual magma workshops do. -The magma version also needs magma. - -Due to DFHack limits, the workshop will collapse over true open space. -However down stairs are passable but support machines, so you can use them. - -After constructing the building itself, machines can be connected -to the edge tiles that look like gear boxes. Their exact position -is extracted from the workshop raws. - -Like with collapse above, due to DFHack limits the workshop -can only immediately connect to machine components built AFTER it. -This also means that engines cannot be chained without intermediate -axles built after both engines. - -Operation ---------- -In order to operate the engine, queue the Stoke Boiler job (optionally -on repeat). A furnace operator will come, possibly bringing a bar of fuel, -and perform it. As a result, a "boiling water" item will appear -in the :kbd:`t` view of the workshop. - -.. note:: - - The completion of the job will actually consume one unit - of the appropriate liquids from below the workshop. This means - that you cannot just raise 7 units of magma with a piston and - have infinite power. However, liquid consumption should be slow - enough that water can be supplied by a pond zone bucket chain. - -Every such item gives 100 power, up to a limit of 300 for coal, -and 500 for a magma engine. The building can host twice that -amount of items to provide longer autonomous running. When the -boiler gets filled to capacity, all queued jobs are suspended; -once it drops back to 3+1 or 5+1 items, they are re-enabled. - -While the engine is providing power, steam is being consumed. -The consumption speed includes a fixed 10% waste rate, and -the remaining 90% are applied proportionally to the actual -load in the machine. With the engine at nominal 300 power with -150 load in the system, it will consume steam for actual -300*(10% + 90%*150/300) = 165 power. - -Masterpiece mechanism and chain will decrease the mechanical -power drawn by the engine itself from 10 to 5. Masterpiece -barrel decreases waste rate by 4%. Masterpiece piston and pipe -decrease it by further 4%, and also decrease the whole steam -use rate by 10%. - -Explosions ----------- -The engine must be constructed using barrel, pipe and piston -from fire-safe, or in the magma version magma-safe metals. - -During operation weak parts get gradually worn out, and -eventually the engine explodes. It should also explode if -toppled during operation by a building destroyer, or a -tantruming dwarf. - -Save files ----------- -It should be safe to load and view engine-using fortresses -from a DF version without DFHack installed, except that in such -case the engines won't work. However actually making modifications -to them, or machines they connect to (including by pulling levers), -can easily result in inconsistent state once this plugin is -available again. The effects may be as weird as negative power -being generated. - -.. _strangemood: - -strangemood -=========== -Creates a strange mood job the same way the game itself normally does it. - -Options: - -:-force: Ignore normal strange mood preconditions (no recent mood, minimum - moodable population, artifact limit not reached). -:-unit: Make the strange mood strike the selected unit instead of picking - one randomly. Unit eligibility is still enforced. -:-type : Force the mood to be of a particular type instead of choosing randomly based on happiness. - Valid values for T are "fey", "secretive", "possessed", "fell", and "macabre". -:-skill S: Force the mood to use a specific skill instead of choosing the highest moodable skill. - Valid values are "miner", "carpenter", "engraver", "mason", "tanner", "weaver", - "clothier", "weaponsmith", "armorsmith", "metalsmith", "gemcutter", "gemsetter", - "woodcrafter", "stonecrafter", "metalcrafter", "glassmaker", "leatherworker", - "bonecarver", "bowyer", and "mechanic". - -Known limitations: if the selected unit is currently performing a job, the mood will not be started. - -============== -Plugin Lua API -============== - -Some plugins consist solely of native libraries exposed to Lua. They are listed -in the `lua-api` file under `lua-plugins`: - -* `building-hacks` -* `cxxrandom` -* `eventful` -* `luasocket` -* `map-render` -* `pathable` -* `xlsxreader` - -=========== -UI Upgrades -=========== - -.. note:: - In order to avoid user confusion, as a matter of policy all GUI tools - display the word :guilabel:`DFHack` on the screen somewhere while active. - - When that is not appropriate because they merely add keybinding hints to - existing DF screens, they deliberately use red instead of green for the key. - -.. contents:: - :local: - - -.. _automaterial: - -automaterial -============ -This makes building constructions (walls, floors, fortifications, etc) a little bit -easier by saving you from having to trawl through long lists of materials each time -you place one. - -Firstly, it moves the last used material for a given construction type to the top of -the list, if there are any left. So if you build a wall with chalk blocks, the next -time you place a wall the chalk blocks will be at the top of the list, regardless of -distance (it only does this in "grouped" mode, as individual item lists could be huge). -This should mean you can place most constructions without having to search for your -preferred material type. - -.. image:: images/automaterial-mat.png - -Pressing :kbd:`a` while highlighting any material will enable that material for "auto select" -for this construction type. You can enable multiple materials as autoselect. Now the next -time you place this type of construction, the plugin will automatically choose materials -for you from the kinds you enabled. If there is enough to satisfy the whole placement, -you won't be prompted with the material screen - the construction will be placed and you -will be back in the construction menu as if you did it manually. - -When choosing the construction placement, you will see a couple of options: - -.. image:: images/automaterial-pos.png - -Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you need -to go to the material selection screen so you can toggle some materials on or off. - -The other option (auto type selection, off by default) can be toggled on with :kbd:`t`. If you -toggle this option on, instead of returning you to the main construction menu after selecting -materials, it returns you back to this screen. If you use this along with several autoselect -enabled materials, you should be able to place complex constructions more conveniently. - -.. _automelt: - -automelt -======== -When automelt is enabled for a stockpile, any meltable items placed -in it will be designated to be melted. -This plugin adds an option to the :kbd:`q` menu when `enabled `. - -.. _autotrade: - -autotrade -========= -When autotrade is enabled for a stockpile, any items placed in it will be -designated to be taken to the Trade Depot whenever merchants are on the map. -This plugin adds an option to the :kbd:`q` menu when `enabled `. - -.. _buildingplan: - -buildingplan -============ -When active (via ``enable buildingplan``), this plugin adds a planning mode for -building placement. You can then place furniture, constructions, and other buildings -before the required materials are available, and they will be created in a suspended -state. Buildingplan will periodically scan for appropriate items, and the jobs will -be unsuspended when the items are available. - -This is very useful when combined with `workflow` - you can set a constraint -to always have one or two doors/beds/tables/chairs/etc available, and place -as many as you like. The plugins then take over and fulfill the orders, -with minimal space dedicated to stockpiles. - -.. _buildingplan-filters: - -Item filtering --------------- - -While placing a building, you can set filters for what materials you want the building made -out of, what quality you want the component items to be, and whether you want the items to -be decorated. - -If a building type takes more than one item to construct, use :kbd:`Ctrl`:kbd:`Left` and -:kbd:`Ctrl`:kbd:`Right` to select the item that you want to set filters for. Any filters that -you set will be used for all buildings of the selected type placed from that point onward -(until you set a new filter or clear the current one). Buildings placed before the filters -were changed will keep the filter values that were set when the building was placed. - -For example, you can be sure that all your constructed walls are the same color by setting -a filter to accept only certain types of stone. - -Quickfort mode --------------- - -If you use the external Python Quickfort to apply building blueprints instead of the native -DFHack `quickfort` script, you must enable Quickfort mode. This temporarily enables -buildingplan for all building types and adds an extra blank screen after every building -placement. This "dummy" screen is needed for Python Quickfort to interact successfully with -Dwarf Fortress. - -Note that Quickfort mode is only for compatibility with the legacy Python Quickfort. The -DFHack `quickfort` script does not need Quickfort mode to be enabled. The `quickfort` script -will successfully integrate with buildingplan as long as the buildingplan plugin is enabled. - -.. _buildingplan-settings: - -Global settings ---------------- - -The buildingplan plugin has several global settings that can be set from the UI (:kbd:`G` -from any building placement screen, for example: :kbd:`b`:kbd:`a`:kbd:`G`). These settings -can also be set from the ``DFHack#`` prompt once a map is loaded (or from your -``onMapLoad.init`` file) with the syntax:: - - buildingplan set - -and displayed with:: - - buildingplan set - -The available settings are: - -+----------------+---------+-----------+---------------------------------------+ -| Setting | Default | Persisted | Description | -+================+=========+===========+=======================================+ -| all_enabled | false | no | Enable planning mode for all building | -| | | | types. | -+----------------+---------+-----------+---------------------------------------+ -| blocks | true | yes | Allow blocks, boulders, logs, or bars | -+----------------+---------+ | to be matched for generic "building | -| boulders | true | | material" items | -+----------------+---------+ | | -| logs | true | | | -+----------------+---------+ | | -| bars | false | | | -+----------------+---------+-----------+---------------------------------------+ -| 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 -could add this to your ``onMapLoad.init`` file:: - - on-new-fortress buildingplan set boulders false; buildingplan set logs false - -Persisted settings (i.e. ``blocks``, ``boulders``, ``logs``, and ``bars``) are saved with -your game, so you only need to set them to the values you want once. - -.. _command-prompt: - -command-prompt -============== -An in-game DFHack terminal, where you can enter other commands. - -:dfhack-keybind:`command-prompt` - -Usage: ``command-prompt [entry]`` - -If called with an entry, it starts with that text filled in. -Most useful for developers, who can set a keybinding to open -a laungage interpreter for lua or Ruby by starting with the -`:lua ` or `:rb ` commands. - -Otherwise somewhat similar to `gui/quickcmd`. - -.. image:: images/command-prompt.png - -.. _confirm: - -confirm -======= -Implements several confirmation dialogs for potentially destructive actions -(for example, seizing goods from traders or deleting hauling routes). - -Usage: - -:enable confirm: Enable all confirmations; alias ``confirm enable all``. - Replace with ``disable`` to disable. -:confirm help: List available confirmation dialogues. -:confirm enable option1 [option2...]: - Enable (or disable) specific confirmation dialogues. - -.. _debug: - -debug -===== -Manager for DFHack runtime debug prints. Debug prints are grouped by plugin name, -category name and print level. Levels are ``trace``, ``debug``, ``info``, -``warning`` and ``error``. - -The runtime message printing is controlled using filters. Filters set the -visible messages of all matching categories. Matching uses regular expression syntax, -which allows listing multiple alternative matches or partial name matches. -This syntax is a C++ version of the ECMA-262 grammar (Javascript regular expressions). -Details of differences can be found at -https://en.cppreference.com/w/cpp/regex/ecmascript - -Persistent filters are stored in ``dfhack-config/runtime-debug.json``. -Oldest filters are applied first. That means a newer filter can override the -older printing level selection. - -Usage: ``debugfilter [subcommand] [parameters...]`` - -The following subcommands are supported: - -help ----- -Give overall help or a detailed help for a subcommand. - -Usage: ``debugfilter help [subcommand]`` - -category --------- -List available debug plugin and category names. - -Usage: ``debugfilter category [plugin regex] [category regex]`` - -The list can be filtered using optional regex parameters. If filters aren't -given then the it uses ``"."`` regex which matches any character. The regex -parameters are good way to test regex before passing them to ``set``. - -filter ------- -List active and passive debug print level changes. - -Usage: ``debugfilter filter [id]`` - -Optional ``id`` parameter is the id listed as first column in the filter list. -If id is given then the command shows information for the given filter only in -multi line format that is better format if filter has long regex. - -set ---- -Creates a new debug filter to set category printing levels. - -Usage: ``debugfilter set [level] [plugin regex] [category regex]`` - -Adds a filter that will be deleted when DF process exists or plugin is unloaded. - -Usage: ``debugfilter set persistent [level] [plugin regex] [category regex]`` - -Stores the filter in the configuration file to until ``unset`` is used to remove -it. - -Level is the minimum debug printing level to show in log. - -* ``trace``: Possibly very noisy messages which can be printed many times per second - -* ``debug``: Messages that happen often but they should happen only a couple of times per second - -* ``info``: Important state changes that happen rarely during normal execution - -* ``warning``: Enabled by default. Shows warnings about unexpected events which code managed to handle correctly. - -* ``error``: Enabled by default. Shows errors which code can't handle without user intervention. - -unset ------ -Delete a space separated list of filters - -Usage: ``debugfilter unset [id...]`` - -disable -------- -Disable a space separated list of filters but keep it in the filter list - -Usage: ``debugfilter disable [id...]`` - -enable ------- -Enable a space sperate list of filters - -Usage: ``debugfilter enable [id...]`` - -.. _embark-assistant: - -embark-assistant -================ - -This plugin provides embark site selection help. It has to be run with the -``embark-assistant`` command while the pre-embark screen is displayed and shows -extended (and correct(?)) resource information for the embark rectangle as well -as normally undisplayed sites in the current embark region. It also has a site -selection tool with more options than DF's vanilla search tool. For detailed -help invoke the in game info screen. - -.. _embark-tools: - -embark-tools -============ -A collection of embark-related tools. Usage and available tools:: - - embark-tools enable/disable tool [tool]... - -:anywhere: Allows embarking anywhere (including sites, mountain-only biomes, - and oceans). Use with caution. -:mouse: Implements mouse controls (currently in the local embark region only) -:sand: Displays an indicator when sand is present in the currently-selected - area, similar to the default clay/stone indicators. -:sticky: Maintains the selected local area while navigating the world map - -.. _follow: - -follow -====== -Makes the game view follow the currently highlighted unit after you exit from the -current menu or cursor mode. Handy for watching dwarves running around. Deactivated -by moving the view manually. - -.. _hotkeys: - -hotkeys -======= -Opens an in-game screen showing which DFHack keybindings are -active in the current context. See also `hotkey-notes`. - -.. image:: images/hotkeys.png - -:dfhack-keybind:`hotkeys` - -.. _manipulator: - -manipulator -=========== -An in-game equivalent to the popular program Dwarf Therapist. - -To activate, open the unit screen and press :kbd:`l`. - -.. image:: images/manipulator.png - -The far left column displays the unit's Happiness (color-coded based on its -value), Name, Profession/Squad, and the right half of the screen displays each -dwarf's labor settings and skill levels (0-9 for Dabbling through Professional, -A-E for Great through Grand Master, and U-Z for Legendary through Legendary+5). - -Cells with teal backgrounds denote skills not controlled by labors, e.g. -military and social skills. - -.. image:: images/manipulator2.png - -Press :kbd:`t` to toggle between Profession, Squad, and Job views. - -.. image:: images/manipulator3.png - -Use the arrow keys or number pad to move the cursor around, holding :kbd:`Shift` to -move 10 tiles at a time. - -Press the Z-Up (:kbd:`<`) and Z-Down (:kbd:`>`) keys to move quickly between labor/skill -categories. The numpad Z-Up and Z-Down keys seek to the first or last unit -in the list. :kbd:`Backspace` seeks to the top left corner. - -Press Enter to toggle the selected labor for the selected unit, or Shift+Enter -to toggle all labors within the selected category. - -Press the :kbd:`+`:kbd:`-` keys to sort the unit list according to the currently selected -skill/labor, and press the :kbd:`*`:kbd:`/` keys to sort the unit list by Name, Profession/Squad, -Happiness, or Arrival order (using :kbd:`Tab` to select which sort method to use here). - -With a unit selected, you can press the :kbd:`v` key to view its properties (and -possibly set a custom nickname or profession) or the :kbd:`c` key to exit -Manipulator and zoom to its position within your fortress. - -The following mouse shortcuts are also available: - -* Click on a column header to sort the unit list. Left-click to sort it in one - direction (descending for happiness or labors/skills, ascending for name, - profession or squad) and right-click to sort it in the opposite direction. -* Left-click on a labor cell to toggle that labor. Right-click to move the - cursor onto that cell instead of toggling it. -* Left-click on a unit's name, profession or squad to view its properties. -* Right-click on a unit's name, profession or squad to zoom to it. - -Pressing :kbd:`Esc` normally returns to the unit screen, but :kbd:`Shift`:kbd:`Esc` would exit -directly to the main dwarf mode screen. - -Professions ------------ - -The manipulator plugin supports saving professions: a named set of labors that can be -quickly applied to one or multiple dwarves. - -To save a profession, highlight a dwarf and press :kbd:`P`. The profession will be saved using -the custom profession name of the dwarf, or the default for that dwarf if no custom profession -name has been set. - -To apply a profession, either highlight a single dwarf or select multiple with -:kbd:`x`, and press :kbd:`p` to select the profession to apply. All labors for -the selected dwarves will be reset to the labors of the chosen profession. - -Professions are saved as human-readable text files in the -``dfhack-config/professions`` folder within the DF folder, and can be edited or -deleted there. - -The professions library -~~~~~~~~~~~~~~~~~~~~~~~ - -The manipulator plugin comes with a library of professions that you can assign -to your dwarves. - -If you'd rather use Dwarf Therapist to manage your labors, it is easy to import -these professions to DT and use them there. Simply assign the professions you -want to import to a dwarf. Once you have assigned a profession to at least one -dwarf, you can select "Import Professions from DF" in the DT "File" menu. The -professions will then be available for use in DT. - -In the charts below, the "At Start" and "Max" columns indicate the approximate -number of dwarves of each profession that you are likely to need at the start of -the game and how many you are likely to need in a mature fort. These are just -approximations. Your playstyle may demand more or fewer of each profession. - -============= ======== ===== ================================================= -Profession At Start Max Description -============= ======== ===== ================================================= -Chef 0 3 Buchery, Tanning, and Cooking. It is important to - focus just a few dwarves on cooking since - well-crafted meals make dwarves very happy. They - are also an excellent trade good. -Craftsdwarf 0 4-6 All labors used at Craftsdwarf's workshops, - Glassmaker's workshops, and kilns. -Doctor 0 2-4 The full suite of medical labors, plus Animal - Caretaking for those using the dwarfvet plugin. -Farmer 1 4 Food- and animal product-related labors. This - profession also has the ``Alchemist`` labor - enabled since they need to focus on food-related - jobs, though you might want to disable - ``Alchemist`` for your first farmer until there - are actual farming duties to perform. -Fisherdwarf 0 0-1 Fishing and fish cleaning. If you assign this - profession to any dwarf, be prepared to be - inundated with fish. Fisherdwarves *never stop - fishing*. Be sure to also run ``prioritize -a - PrepareRawFish ExtractFromRawFish`` or else - caught fish will just be left to rot. -Hauler 0 >20 All hauling labors plus Siege Operating, Mechanic - (so haulers can assist in reloading traps) and - Architecture (so haulers can help build massive - windmill farms and pump stacks). As you - accumulate enough Haulers, you can turn off - hauling labors for other dwarves so they can - focus on their skilled tasks. You may also want - to restrict your Mechanic's workshops to only - skilled mechanics so your haulers don't make - low-quality mechanisms. -Laborer 0 10-12 All labors that don't improve quality with skill, - such as Soapmaking and furnace labors. -Marksdwarf 0 10-30 Similar to Hauler. See the description for - Meleedwarf below for more details. -Mason 2 2-4 Masonry and Gem Cutting/Encrusting. In the early - game, you may need to run "`prioritize` - ConstructBuilding" to get your masons to build - wells and bridges if they are too busy crafting - stone furniture. -Meleedwarf 0 20-50 Similar to Hauler, but without most civilian - labors. This profession is separate from Hauler - so you can find your military dwarves easily. - Meleedwarves and Marksdwarves have Mechanics and - hauling labors enabled so you can temporarily - deactivate your military after sieges and allow - your military dwarves to help clean up. -Migrant 0 0 You can assign this profession to new migrants - temporarily while you sort them into professions. - Like Marksdwarf and Meleedwarf, the purpose of - this profession is so you can find your new - dwarves more easily. -Miner 2 2-10 Mining and Engraving. This profession also has - the ``Alchemist`` labor enabled, which disables - hauling for those using the `autohauler` plugin. - Once the need for Miners tapers off in the late - game, dwarves with this profession make good - military dwarves, wielding their picks as - weapons. -Outdoorsdwarf 1 2-4 Carpentry, Bowyery, Woodcutting, Animal Training, - Trapping, Plant Gathering, Beekeeping, and Siege - Engineering. -Smith 0 2-4 Smithing labors. You may want to specialize your - Smiths to focus on a single smithing skill to - maximize equipment quality. -StartManager 1 0 All skills not covered by the other starting - professions (Miner, Mason, Outdoorsdwarf, and - Farmer), plus a few overlapping skills to - assist in critical tasks at the beginning of the - game. Individual labors should be turned off as - migrants are assigned more specialized - professions that cover them, and the StartManager - dwarf can eventually convert to some other - profession. -Tailor 0 2 Textile industry labors: Dying, Leatherworking, - Weaving, and Clothesmaking. -============= ======== ===== ================================================= - -A note on autohauler -~~~~~~~~~~~~~~~~~~~~ - -These profession definitions are designed to work well with or without the -`autohauler` plugin (which helps to keep your dwarves focused on skilled labors -instead of constantly being distracted by hauling). If you do want to use -autohauler, adding the following lines to your ``onMapLoad.init`` file will -configure it to let the professions manage the "Feed water to civilians" and -"Recover wounded" labors instead of enabling those labors for all hauling -dwarves:: - - on-new-fortress enable autohauler - on-new-fortress autohauler FEED_WATER_CIVILIANS allow - on-new-fortress autohauler RECOVER_WOUNDED allow - -.. _mousequery: - -mousequery -========== -Adds mouse controls to the DF interface, e.g. click-and-drag designations. - -Options: - -:plugin: enable/disable the entire plugin -:rbutton: enable/disable right mouse button -:track: enable/disable moving cursor in build and designation mode -:edge: enable/disable active edge scrolling (when on, will also enable tracking) -:live: enable/disable query view when unpaused -:delay: Set delay when edge scrolling in tracking mode. Omit amount to display current setting. - -Usage:: - - mousequery [plugin] [rbutton] [track] [edge] [live] [enable|disable] - -.. _nopause: - -nopause -======= -Disables pausing (both manual and automatic) with the exception of pause forced -by `reveal` ``hell``. This is nice for digging under rivers. - -.. _rename: - -rename -====== -Allows renaming various things. Use `gui/rename` for an in-game interface. - -Options: - -``rename squad "name"`` - Rename squad by index to 'name'. -``rename hotkey \"name\"`` - Rename hotkey by index. This allows assigning - longer commands to the DF hotkeys. -``rename unit "nickname"`` - Rename a unit/creature highlighted in the DF user interface. -``rename unit-profession "custom profession"`` - Change proffession name of the highlighted unit/creature. -``rename building "name"`` - Set a custom name for the selected building. - The building must be one of stockpile, workshop, furnace, trap, - siege engine or an activity zone. - -.. _rendermax: - -rendermax -========= -A collection of renderer replacing/enhancing filters. For better effect try changing the -black color in palette to non totally black. See :forums:`128487` for more info. - -Options: - -:trippy: Randomizes the color of each tiles. Used for fun, or testing. -:light: Enable lighting engine. -:light reload: Reload the settings file. -:light sun |cycle: Set time to (in hours) or set it to df time cycle. -:occlusionON, occlusionOFF: Show debug occlusion info. -:disable: Disable any filter that is enabled. - -An image showing lava and dragon breath. Not pictured here: sunlight, shining items/plants, -materials that color the light etc... - -.. image:: images/rendermax.png - -.. _resume: - -resume -====== -Allows automatic resumption of suspended constructions, along with colored -UI hints for construction status. - -.. _rb: -.. _ruby: - -ruby -==== -Ruby language plugin, which evaluates the following arguments as a ruby string. -Best used as ``:rb [string]``, for the special parsing mode. Alias ``rb_eval``. - -.. comment - the link target "search" is reserved for the Sphinx search page -.. _search-plugin: - -search -====== -The search plugin adds search to the Stocks, Animals, Trading, Stockpile, -Noble (assignment candidates), Military (position candidates), Burrows -(unit list), Rooms, Announcements, Job List and Unit List screens. - -.. image:: images/search.png - -Searching works the same way as the search option in :guilabel:`Move to Depot`. -You will see the Search option displayed on screen with a hotkey (usually :kbd:`s`). -Pressing it lets you start typing a query and the relevant list will start -filtering automatically. - -Pressing :kbd:`Enter`, :kbd:`Esc` or the arrow keys will return you to browsing the now -filtered list, which still functions as normal. You can clear the filter -by either going back into search mode and backspacing to delete it, or -pressing the "shifted" version of the search hotkey while browsing the -list (e.g. if the hotkey is :kbd:`s`, then hitting :kbd:`Shift`:kbd:`s` will clear any -filter). - -Leaving any screen automatically clears the filter. - -In the Trade screen, the actual trade will always only act on items that -are actually visible in the list; the same effect applies to the Trade -Value numbers displayed by the screen. Because of this, the :kbd:`t` key is -blocked while search is active, so you have to reset the filters first. -Pressing :kbd:`Alt`:kbd:`C` will clear both search strings. - -In the stockpile screen the option only appears if the cursor is in the -rightmost list: - -.. image:: images/search-stockpile.png - -Note that the 'Permit XXX'/'Forbid XXX' keys conveniently operate only -on items actually shown in the rightmost list, so it is possible to select -only fat or tallow by forbidding fats, then searching for fat/tallow, and -using Permit Fats again while the list is filtered. - -.. _sort: -.. _sort-items: - -sort-items -========== -Sort the visible item list:: - - sort-items order [order...] - -Sort the item list using the given sequence of comparisons. -The ``<`` prefix for an order makes undefined values sort first. -The ``>`` prefix reverses the sort order for defined values. - -Item order examples:: - - description material wear type quality - -The orderings are defined in ``hack/lua/plugins/sort/*.lua`` - -.. _sort-units: - -sort-units -========== -Sort the visible unit list:: - - sort-units order [order...] - -Sort the unit list using the given sequence of comparisons. -The ``<`` prefix for an order makes undefined values sort first. -The ``>`` prefix reverses the sort order for defined values. - -Unit order examples:: - - name age arrival squad squad_position profession - -The orderings are defined in ``hack/lua/plugins/sort/*.lua`` - -:dfhack-keybind:`sort-units` - -.. _stocksettings: -.. _stockpiles: - -stockpiles -========== -Offers the following commands to save and load stockpile settings. -See `gui/stockpiles` for an in-game interface. - -:copystock: Copies the parameters of the currently highlighted stockpile to the custom - stockpile settings and switches to custom stockpile placement mode, effectively - allowing you to copy/paste stockpiles easily. - :dfhack-keybind:`copystock` - -:savestock: Saves the currently highlighted stockpile's settings to a file in your Dwarf - Fortress folder. This file can be used to copy settings between game saves or - players. e.g.: ``savestock food_settings.dfstock`` - -:loadstock: Loads a saved stockpile settings file and applies it to the currently selected - stockpile. e.g.: ``loadstock food_settings.dfstock`` - -To use savestock and loadstock, use the :kbd:`q` command to highlight a stockpile. -Then run savestock giving it a descriptive filename. Then, in a different (or -the same!) gameworld, you can highlight any stockpile with :kbd:`q` then execute the -``loadstock`` command passing it the name of that file. The settings will be -applied to that stockpile. - -Note that files are relative to the DF folder, so put your files there or in a -subfolder for easy access. Filenames should not have spaces. Generated materials, -divine metals, etc are not saved as they are different in every world. - -.. _stocks: - -stocks -====== -Replaces the DF stocks screen with an improved version. - -:dfhack-keybind:`stocks` - - -.. _title-folder: - -title-folder -============= -Displays the DF folder name in the window title bar when enabled. - -.. _title-version: - -title-version -============= -Displays the DFHack version on DF's title screen when enabled. - -.. _trackstop: - -trackstop -========= -Adds a :kbd:`q` menu for track stops, which is completely blank by default. -This allows you to view and/or change the track stop's friction and dump -direction settings, using the keybindings from the track stop building interface. diff --git a/docs/Removed.rst b/docs/Removed.rst index e94b04cf8..3dbe26820 100644 --- a/docs/Removed.rst +++ b/docs/Removed.rst @@ -59,6 +59,14 @@ script instead. You can use your existing .csv files. Just move them to the ``blueprints`` folder in your DF installation, and instead of ``fortplan file.csv`` run ``quickfort run file.csv``. +.. _gui/no-dfhack-init: + +gui/no-dfhack-init +================== +Tool that warned the user when the ``dfhack.init`` file did not exist. Now that +``dfhack.init`` is autogenerated in ``dfhack-config/init``, this warning is no +longer necessary. + .. _warn-stuck-trees: warn-stuck-trees diff --git a/docs/Scripts.rst b/docs/Scripts.rst deleted file mode 100644 index 419385582..000000000 --- a/docs/Scripts.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _scripts-index: - -############## -DFHack Scripts -############## - -Lua or ruby scripts placed in the :file:`hack/scripts/` directory -are considered for execution as if they were native DFHack commands. - -The following pages document all the scripts in the DFHack standard library. - -.. toctree:: - :maxdepth: 2 - - /docs/_auto/base - /docs/_auto/devel - /docs/_auto/fix - /docs/_auto/gui - /docs/_auto/modtools diff --git a/docs/Tools.rst b/docs/Tools.rst deleted file mode 100644 index 15c5f878e..000000000 --- a/docs/Tools.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _tools-index: - -############ -DFHack Tools -############ - -These are the DFHack commands you can run. - -.. toctree:: - :titlesonly: - :glob: - - /docs/tools/* diff --git a/docs/_auto/.gitignore b/docs/_auto/.gitignore deleted file mode 100644 index 30d85567b..000000000 --- a/docs/_auto/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.rst diff --git a/docs/build-pdf.sh b/docs/build-pdf.sh index 76908b49b..735ef2faa 100755 --- a/docs/build-pdf.sh +++ b/docs/build-pdf.sh @@ -11,13 +11,4 @@ cd $(dirname "$0") cd .. -sphinx=sphinx-build -if [ -n "$SPHINX" ]; then - sphinx=$SPHINX -fi - -if [ -z "$JOBS" ]; then - JOBS=2 -fi - -"$sphinx" -M latexpdf . ./docs/pdf -w ./docs/_sphinx-warnings.txt -j "$JOBS" "$@" +"${SPHINX:-sphinx-build}" -M latexpdf -d build/docs/pdf . docs/pdf -w build/docs/pdf/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" diff --git a/docs/build.sh b/docs/build.sh index 95a97e539..c696d5fbe 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -11,13 +11,5 @@ cd $(dirname "$0") cd .. -sphinx=sphinx-build -if [ -n "$SPHINX" ]; then - sphinx=$SPHINX -fi - -if [ -z "$JOBS" ]; then - JOBS=2 -fi - -"$sphinx" -a -b html . ./docs/html -w ./docs/_sphinx-warnings.txt -j "$JOBS" "$@" +"${SPHINX:-sphinx-build}" -b html -d build/docs/html . docs/html -w build/docs/html/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" +"${SPHINX:-sphinx-build}" -b text -d build/docs/text . docs/text -w build/docs/text/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" diff --git a/docs/_changelogs/.gitignore b/docs/changelogs/.gitignore similarity index 50% rename from docs/_changelogs/.gitignore rename to docs/changelogs/.gitignore index 2211df63d..90de5c70d 100644 --- a/docs/_changelogs/.gitignore +++ b/docs/changelogs/.gitignore @@ -1 +1,2 @@ *.txt +*.rst diff --git a/docs/guides/examples-guide.rst b/docs/guides/examples-guide.rst index b699b8204..f61ada4d7 100644 --- a/docs/guides/examples-guide.rst +++ b/docs/guides/examples-guide.rst @@ -28,8 +28,8 @@ it is useful (and customizable) for any fort. It includes the following config: - Calls `ban-cooking` for items that have important alternate uses and should not be cooked. This configuration is only set when a fortress is first started, so later manual changes will not be overridden. -- Automates calling of various fort maintenance and `scripts-fix`, like - `cleanowned` and `fix/stuckdoors`. +- Automates calling of various fort maintenance scripts, like `cleanowned` and + `fix/stuckdoors`. - Keeps your manager orders intelligently ordered with `orders` ``sort`` so no orders block other orders from ever getting completed. - Periodically enqueues orders to shear and milk shearable and milkable pets. diff --git a/docs/index-tools.rst b/docs/index-tools.rst new file mode 100644 index 000000000..dca14ae3b --- /dev/null +++ b/docs/index-tools.rst @@ -0,0 +1,24 @@ +.. _tools-index: + +============ +DFHack Tools +============ + +These pages contain information about the plugins, scripts, and built-in +commands distributed with DFHack. + +.. note:: + In order to avoid user confusion, as a matter of policy all GUI tools + display the word :guilabel:`DFHack` on the screen somewhere while active. + + When that is not appropriate because they merely add keybinding hints to + existing DF screens, they deliberately use red instead of green for the key. + +.. toctree:: + :titlesonly: + :glob: + + /docs/Tags + /docs/Builtin + /docs/tools/* + /docs/tools/*/* diff --git a/docs/tools/cromulate.rst b/docs/plugins/cromulate.rst similarity index 100% rename from docs/tools/cromulate.rst rename to docs/plugins/cromulate.rst diff --git a/docs/sphinx_extensions/dfhack/changelog.py b/docs/sphinx_extensions/dfhack/changelog.py index f7405d8b1..629d6b6de 100644 --- a/docs/sphinx_extensions/dfhack/changelog.py +++ b/docs/sphinx_extensions/dfhack/changelog.py @@ -238,8 +238,8 @@ def generate_changelog(all=False): consolidate_changelog(stable_entries) consolidate_changelog(dev_entries) - print_changelog(versions, stable_entries, os.path.join(DOCS_ROOT, '_auto/news.rst')) - print_changelog(versions, dev_entries, os.path.join(DOCS_ROOT, '_auto/news-dev.rst')) + print_changelog(versions, stable_entries, os.path.join(DOCS_ROOT, 'changelogs/news.rst')) + print_changelog(versions, dev_entries, os.path.join(DOCS_ROOT, 'changelogs/news-dev.rst')) if all: for version in versions: @@ -251,10 +251,10 @@ def generate_changelog(all=False): else: version_entries = {version: dev_entries[version]} print_changelog([version], version_entries, - os.path.join(DOCS_ROOT, '_changelogs/%s-github.txt' % version), + os.path.join(DOCS_ROOT, 'changelogs/%s-github.txt' % version), replace=False) print_changelog([version], version_entries, - os.path.join(DOCS_ROOT, '_changelogs/%s-reddit.txt' % version), + os.path.join(DOCS_ROOT, 'changelogs/%s-reddit.txt' % version), replace=False, prefix='> ') @@ -264,7 +264,7 @@ def cli_entrypoint(): import argparse parser = argparse.ArgumentParser() parser.add_argument('-a', '--all', action='store_true', - help='Print changelogs for all versions to docs/_changelogs') + help='Print changelogs for all versions to docs/changelogs') parser.add_argument('-c', '--check', action='store_true', help='Check that all entries are printed') args = parser.parse_args() @@ -272,9 +272,9 @@ def cli_entrypoint(): entries = generate_changelog(all=args.all) if args.check: - with open(os.path.join(DOCS_ROOT, '_auto/news.rst')) as f: + with open(os.path.join(DOCS_ROOT, 'changelogs/news.rst')) as f: content_stable = f.read() - with open(os.path.join(DOCS_ROOT, '_auto/news-dev.rst')) as f: + with open(os.path.join(DOCS_ROOT, 'changelogs/news-dev.rst')) as f: content_dev = f.read() for entry in entries: for description in entry.children: diff --git a/index.rst b/index.rst index 3b46699a7..29ac3c129 100644 --- a/index.rst +++ b/index.rst @@ -30,11 +30,7 @@ User Manual /docs/Installing /docs/Support /docs/Core - /docs/Builtin - /docs/Plugins - /docs/Scripts - /docs/Tags - /docs/Tools + /docs/index-tools /docs/guides/index /docs/index-about /docs/index-dev From 3e2320aa607154642cbefad116f92a5ef35c4974 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 9 Jul 2022 23:01:46 -0700 Subject: [PATCH 009/334] split the plugin docs into individual files --- docs/plugins/3dveins.rst | 14 ++ docs/plugins/add-spatter.rst | 6 + docs/plugins/adv-bodyswap.rst | 12 ++ docs/plugins/alltraffic.rst | 11 ++ docs/plugins/autobutcher.rst | 91 ++++++++++ docs/plugins/autochop.rst | 16 ++ docs/plugins/autoclothing.rst | 14 ++ docs/plugins/autodump.rst | 29 ++++ docs/plugins/autofarm.rst | 21 +++ docs/plugins/autogems.rst | 7 + docs/plugins/autohauler.rst | 20 +++ docs/plugins/autolabor.rst | 70 ++++++++ docs/plugins/automaterial.rst | 33 ++++ docs/plugins/automelt.rst | 5 + docs/plugins/autonestbox.rst | 19 ++ docs/plugins/autotrade.rst | 5 + docs/plugins/blueprint.rst | 112 ++++++++++++ docs/plugins/buildingplan.rst | 88 ++++++++++ docs/plugins/burrows.rst | 38 ++++ docs/plugins/changeitem.rst | 26 +++ docs/plugins/changelayer.rst | 60 +++++++ docs/plugins/changevein.rst | 10 ++ docs/plugins/clean.rst | 16 ++ docs/plugins/cleanconst.rst | 7 + docs/plugins/cleanowned.rst | 19 ++ docs/plugins/command-prompt.rst | 16 ++ docs/plugins/confirm.rst | 12 ++ docs/plugins/createitem.rst | 48 ++++++ docs/plugins/cursecheck.rst | 42 +++++ docs/plugins/debug.rst | 89 ++++++++++ docs/plugins/deramp.rst | 6 + docs/plugins/dig-now.rst | 61 +++++++ docs/plugins/dig.rst | 20 +++ docs/plugins/digFlood.rst | 18 ++ docs/plugins/digcircle.rst | 35 ++++ docs/plugins/digexp.rst | 31 ++++ docs/plugins/diggingInvaders.rst | 29 ++++ docs/plugins/digl.rst | 20 +++ docs/plugins/diglx.rst | 20 +++ docs/plugins/digtype.rst | 19 ++ docs/plugins/digv.rst | 20 +++ docs/plugins/digvx.rst | 20 +++ docs/plugins/dwarfmonitor.rst | 78 +++++++++ docs/plugins/dwarfvet.rst | 19 ++ docs/plugins/embark-assistant.rst | 9 + docs/plugins/embark-tools.rst | 12 ++ docs/plugins/fastdwarf.rst | 14 ++ docs/plugins/filltraffic.rst | 17 ++ docs/plugins/fix-armory.rst | 4 + docs/plugins/fix-job-postings.rst | 5 + docs/plugins/fix-unit-occupancy.rst | 12 ++ docs/plugins/fixveins.rst | 5 + docs/plugins/flows.rst | 5 + docs/plugins/follow.rst | 5 + docs/plugins/forceequip.rst | 7 + docs/plugins/generated-creature-renamer.rst | 15 ++ docs/plugins/getplants.rst | 37 ++++ docs/plugins/hotkeys.rst | 8 + docs/plugins/infiniteSky.rst | 16 ++ docs/plugins/isoworldremote.rst | 3 + docs/plugins/job-duplicate.rst | 6 + docs/plugins/job-material.rst | 15 ++ docs/plugins/job.rst | 15 ++ docs/plugins/labormanager.rst | 105 +++++++++++ docs/plugins/lair.rst | 12 ++ docs/plugins/liquids-here.rst | 6 + docs/plugins/liquids.rst | 65 +++++++ docs/plugins/manipulator.rst | 182 ++++++++++++++++++++ docs/plugins/misery.rst | 14 ++ docs/plugins/mode.rst | 21 +++ docs/plugins/mousequery.rst | 16 ++ docs/plugins/nestboxes.rst | 5 + docs/plugins/nopause.rst | 4 + docs/plugins/orders.rst | 116 +++++++++++++ docs/plugins/petcapRemover.rst | 19 ++ docs/plugins/plant.rst | 16 ++ docs/plugins/power-meter.rst | 6 + docs/plugins/probe.rst | 13 ++ docs/plugins/prospect.rst | 51 ++++++ docs/plugins/prospector.rst | 51 ++++++ docs/plugins/rb.rst | 4 + docs/plugins/regrass.rst | 3 + docs/plugins/remotefortressreader.rst | 4 + docs/plugins/rename.rst | 19 ++ docs/plugins/rendermax.rst | 18 ++ docs/plugins/restrictice.rst | 4 + docs/plugins/restrictliquids.rst | 4 + docs/plugins/resume.rst | 4 + docs/plugins/reveal.rst | 23 +++ docs/plugins/revflood.rst | 23 +++ docs/plugins/revforget.rst | 23 +++ docs/plugins/revtoggle.rst | 23 +++ docs/plugins/ruby.rst | 4 + docs/plugins/search.rst | 40 +++++ docs/plugins/seedwatch.rst | 27 +++ docs/plugins/showmood.rst | 3 + docs/plugins/siege-engine.rst | 12 ++ docs/plugins/sort-items.rst | 17 ++ docs/plugins/sort-units.rst | 17 ++ docs/plugins/spectate.rst | 5 + docs/plugins/spotclean.rst | 6 + docs/plugins/steam-engine.rst | 84 +++++++++ docs/plugins/stockflow.rst | 31 ++++ docs/plugins/stockpiles.rst | 28 +++ docs/plugins/stocks.rst | 6 + docs/plugins/stonesense.rst | 10 ++ docs/plugins/strangemood.rst | 19 ++ docs/plugins/tailor.rst | 11 ++ docs/plugins/tiletypes-command.rst | 10 ++ docs/plugins/tiletypes-here-point.rst | 6 + docs/plugins/tiletypes-here.rst | 14 ++ docs/plugins/tiletypes.rst | 82 +++++++++ docs/plugins/title-folder.rst | 3 + docs/plugins/title-version.rst | 3 + docs/plugins/trackstop.rst | 5 + docs/plugins/tubefill.rst | 11 ++ docs/plugins/tweak.rst | 91 ++++++++++ docs/plugins/unreveal.rst | 23 +++ docs/plugins/workNow.rst | 12 ++ docs/plugins/workflow.rst | 113 ++++++++++++ docs/plugins/zone.rst | 130 ++++++++++++++ 121 files changed, 3239 insertions(+) create mode 100644 docs/plugins/3dveins.rst create mode 100644 docs/plugins/add-spatter.rst create mode 100644 docs/plugins/adv-bodyswap.rst create mode 100644 docs/plugins/alltraffic.rst create mode 100644 docs/plugins/autobutcher.rst create mode 100644 docs/plugins/autochop.rst create mode 100644 docs/plugins/autoclothing.rst create mode 100644 docs/plugins/autodump.rst create mode 100644 docs/plugins/autofarm.rst create mode 100644 docs/plugins/autogems.rst create mode 100644 docs/plugins/autohauler.rst create mode 100644 docs/plugins/autolabor.rst create mode 100644 docs/plugins/automaterial.rst create mode 100644 docs/plugins/automelt.rst create mode 100644 docs/plugins/autonestbox.rst create mode 100644 docs/plugins/autotrade.rst create mode 100644 docs/plugins/blueprint.rst create mode 100644 docs/plugins/buildingplan.rst create mode 100644 docs/plugins/burrows.rst create mode 100644 docs/plugins/changeitem.rst create mode 100644 docs/plugins/changelayer.rst create mode 100644 docs/plugins/changevein.rst create mode 100644 docs/plugins/clean.rst create mode 100644 docs/plugins/cleanconst.rst create mode 100644 docs/plugins/cleanowned.rst create mode 100644 docs/plugins/command-prompt.rst create mode 100644 docs/plugins/confirm.rst create mode 100644 docs/plugins/createitem.rst create mode 100644 docs/plugins/cursecheck.rst create mode 100644 docs/plugins/debug.rst create mode 100644 docs/plugins/deramp.rst create mode 100644 docs/plugins/dig-now.rst create mode 100644 docs/plugins/dig.rst create mode 100644 docs/plugins/digFlood.rst create mode 100644 docs/plugins/digcircle.rst create mode 100644 docs/plugins/digexp.rst create mode 100644 docs/plugins/diggingInvaders.rst create mode 100644 docs/plugins/digl.rst create mode 100644 docs/plugins/diglx.rst create mode 100644 docs/plugins/digtype.rst create mode 100644 docs/plugins/digv.rst create mode 100644 docs/plugins/digvx.rst create mode 100644 docs/plugins/dwarfmonitor.rst create mode 100644 docs/plugins/dwarfvet.rst create mode 100644 docs/plugins/embark-assistant.rst create mode 100644 docs/plugins/embark-tools.rst create mode 100644 docs/plugins/fastdwarf.rst create mode 100644 docs/plugins/filltraffic.rst create mode 100644 docs/plugins/fix-armory.rst create mode 100644 docs/plugins/fix-job-postings.rst create mode 100644 docs/plugins/fix-unit-occupancy.rst create mode 100644 docs/plugins/fixveins.rst create mode 100644 docs/plugins/flows.rst create mode 100644 docs/plugins/follow.rst create mode 100644 docs/plugins/forceequip.rst create mode 100644 docs/plugins/generated-creature-renamer.rst create mode 100644 docs/plugins/getplants.rst create mode 100644 docs/plugins/hotkeys.rst create mode 100644 docs/plugins/infiniteSky.rst create mode 100644 docs/plugins/isoworldremote.rst create mode 100644 docs/plugins/job-duplicate.rst create mode 100644 docs/plugins/job-material.rst create mode 100644 docs/plugins/job.rst create mode 100644 docs/plugins/labormanager.rst create mode 100644 docs/plugins/lair.rst create mode 100644 docs/plugins/liquids-here.rst create mode 100644 docs/plugins/liquids.rst create mode 100644 docs/plugins/manipulator.rst create mode 100644 docs/plugins/misery.rst create mode 100644 docs/plugins/mode.rst create mode 100644 docs/plugins/mousequery.rst create mode 100644 docs/plugins/nestboxes.rst create mode 100644 docs/plugins/nopause.rst create mode 100644 docs/plugins/orders.rst create mode 100644 docs/plugins/petcapRemover.rst create mode 100644 docs/plugins/plant.rst create mode 100644 docs/plugins/power-meter.rst create mode 100644 docs/plugins/probe.rst create mode 100644 docs/plugins/prospect.rst create mode 100644 docs/plugins/prospector.rst create mode 100644 docs/plugins/rb.rst create mode 100644 docs/plugins/regrass.rst create mode 100644 docs/plugins/remotefortressreader.rst create mode 100644 docs/plugins/rename.rst create mode 100644 docs/plugins/rendermax.rst create mode 100644 docs/plugins/restrictice.rst create mode 100644 docs/plugins/restrictliquids.rst create mode 100644 docs/plugins/resume.rst create mode 100644 docs/plugins/reveal.rst create mode 100644 docs/plugins/revflood.rst create mode 100644 docs/plugins/revforget.rst create mode 100644 docs/plugins/revtoggle.rst create mode 100644 docs/plugins/ruby.rst create mode 100644 docs/plugins/search.rst create mode 100644 docs/plugins/seedwatch.rst create mode 100644 docs/plugins/showmood.rst create mode 100644 docs/plugins/siege-engine.rst create mode 100644 docs/plugins/sort-items.rst create mode 100644 docs/plugins/sort-units.rst create mode 100644 docs/plugins/spectate.rst create mode 100644 docs/plugins/spotclean.rst create mode 100644 docs/plugins/steam-engine.rst create mode 100644 docs/plugins/stockflow.rst create mode 100644 docs/plugins/stockpiles.rst create mode 100644 docs/plugins/stocks.rst create mode 100644 docs/plugins/stonesense.rst create mode 100644 docs/plugins/strangemood.rst create mode 100644 docs/plugins/tailor.rst create mode 100644 docs/plugins/tiletypes-command.rst create mode 100644 docs/plugins/tiletypes-here-point.rst create mode 100644 docs/plugins/tiletypes-here.rst create mode 100644 docs/plugins/tiletypes.rst create mode 100644 docs/plugins/title-folder.rst create mode 100644 docs/plugins/title-version.rst create mode 100644 docs/plugins/trackstop.rst create mode 100644 docs/plugins/tubefill.rst create mode 100644 docs/plugins/tweak.rst create mode 100644 docs/plugins/unreveal.rst create mode 100644 docs/plugins/workNow.rst create mode 100644 docs/plugins/workflow.rst create mode 100644 docs/plugins/zone.rst diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst new file mode 100644 index 000000000..0e6841792 --- /dev/null +++ b/docs/plugins/3dveins.rst @@ -0,0 +1,14 @@ +3dveins +======= +Removes all existing veins from the map and generates new ones using +3D Perlin noise, in order to produce a layout that smoothly flows between +Z levels. The vein distribution is based on the world seed, so running +the command for the second time should produce no change. It is best to +run it just once immediately after embark. + +This command is intended as only a cosmetic change, so it takes +care to exactly preserve the mineral counts reported by `prospect` ``all``. +The amounts of different layer stones may slightly change in some cases +if vein mass shifts between Z layers. + +The only undo option is to restore your save from backup. diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst new file mode 100644 index 000000000..15b99ff52 --- /dev/null +++ b/docs/plugins/add-spatter.rst @@ -0,0 +1,6 @@ +add-spatter +=========== +This plugin makes reactions with names starting with ``SPATTER_ADD_`` +produce contaminants on the items instead of improvements. The plugin is +intended to give some use to all those poisons that can be bought from caravans, +so they're immune to being washed away by water or destroyed by `clean`. diff --git a/docs/plugins/adv-bodyswap.rst b/docs/plugins/adv-bodyswap.rst new file mode 100644 index 000000000..46b961517 --- /dev/null +++ b/docs/plugins/adv-bodyswap.rst @@ -0,0 +1,12 @@ +adv-bodyswap +============ +This allows taking control over your followers and other creatures in adventure +mode. For example, you can make them pick up new arms and armor and equip them +properly. + +Usage: + +* When viewing unit details, body-swaps into that unit. +* In the main adventure mode screen, reverts transient swap. + +:dfhack-keybind:`adv-bodyswap` diff --git a/docs/plugins/alltraffic.rst b/docs/plugins/alltraffic.rst new file mode 100644 index 000000000..1e9116954 --- /dev/null +++ b/docs/plugins/alltraffic.rst @@ -0,0 +1,11 @@ +alltraffic +========== +Set traffic designations for every single tile of the map - useful for resetting +traffic designations. See also `filltraffic`, `restrictice`, and `restrictliquids`. + +Options: + +:H: High Traffic +:N: Normal Traffic +:L: Low Traffic +:R: Restricted Traffic diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst new file mode 100644 index 000000000..d761cbdbe --- /dev/null +++ b/docs/plugins/autobutcher.rst @@ -0,0 +1,91 @@ +autobutcher +=========== +Assigns lifestock for slaughter once it reaches a specific count. Requires that +you add the target race(s) to a watch list. Only tame units will be processed. + +Units will be ignored if they are: + +* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool + individually, or `zone` ``nick`` for groups) +* Caged, if and only if the cage is defined as a room (to protect zoos) +* Trained for war or hunting + +Creatures who will not reproduce (because they're not interested in the +opposite sex or have been gelded) will be butchered before those who will. +Older adults and younger children will be butchered first if the population +is above the target (default 1 male, 5 female kids and adults). Note that +you may need to set a target above 1 to have a reliable breeding population +due to asexuality etc. See `fix-ster` if this is a problem. + +Options: + +:example: Print some usage examples. +:start: Start running every X frames (df simulation ticks). + Default: X=6000, which would be every 60 seconds at 100fps. +:stop: Stop running automatically. +:sleep : Changes the timer to sleep X frames between runs. +:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, + BIRD_TURKEY, etc) or a list of ids seperated by spaces or + the keyword 'all' which affects all races on your current + watchlist. +:unwatch R: Stop watching race(s). The current target settings will be + remembered. R can be a list of ids or the keyword 'all'. +:forget R: Stop watching race(s) and forget it's/their target settings. + R can be a list of ids or the keyword 'all'. +:autowatch: Automatically adds all new races (animals you buy from merchants, + tame yourself or get from migrants) to the watch list using + default target count. +:noautowatch: Stop auto-adding new races to the watchlist. +:list: Print the current status and watchlist. +:list_export: Print the commands needed to set up status and watchlist, + which can be used to import them to another save (see notes). +:target : + Set target count for specified race(s). The first four arguments + are the number of female and male kids, and female and male adults. + R can be a list of spceies ids, or the keyword ``all`` or ``new``. + ``R = 'all'``: change target count for all races on watchlist + and set the new default for the future. ``R = 'new'``: don't touch + current settings on the watchlist, only set the new default + for future entries. +:list_export: Print the commands required to rebuild your current settings. + +.. note:: + + Settings and watchlist are stored in the savegame, so that you can have + different settings for each save. If you want to copy your watchlist to + another savegame you must export the commands required to recreate your settings. + + To export, open an external terminal in the DF directory, and run + ``dfhack-run autobutcher list_export > filename.txt``. To import, load your + new save and run ``script filename.txt`` in the DFHack terminal. + + +Examples: + +You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, +1 male) of the race alpaca. Once the kids grow up the oldest adults will get +slaughtered. Excess kids will get slaughtered starting with the youngest +to allow that the older ones grow into adults. Any unnamed cats will +be slaughtered as soon as possible. :: + + autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY + autobutcher target 0 0 0 0 CAT + autobutcher watch ALPACA BIRD_TURKEY CAT + autobutcher start + +Automatically put all new races onto the watchlist and mark unnamed tame units +for slaughter as soon as they arrive in your fort. Settings already made +for specific races will be left untouched. :: + + autobutcher target 0 0 0 0 new + autobutcher autowatch + autobutcher start + +Stop watching the races alpaca and cat, but remember the target count +settings so that you can use 'unwatch' without the need to enter the +values again. Note: 'autobutcher unwatch all' works, but only makes sense +if you want to keep the plugin running with the 'autowatch' feature or manually +add some new races with 'watch'. If you simply want to stop it completely use +'autobutcher stop' instead. :: + + autobutcher unwatch ALPACA CAT diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst new file mode 100644 index 000000000..4a669480f --- /dev/null +++ b/docs/plugins/autochop.rst @@ -0,0 +1,16 @@ +autochop +======== +Automatically manage tree cutting designation to keep available logs withing given +quotas. + +Open the dashboard by running:: + + enable autochop + +The plugin must be activated (with :kbd:`d`-:kbd:`t`-:kbd:`c`-:kbd:`a`) before +it can be used. You can then set logging quotas and restrict designations to +specific burrows (with 'Enter') if desired. The plugin's activity cycle runs +once every in game day. + +If you add ``enable autochop`` to your dfhack.init there will be a hotkey to +open the dashboard from the chop designation menu. diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst new file mode 100644 index 000000000..7169844dd --- /dev/null +++ b/docs/plugins/autoclothing.rst @@ -0,0 +1,14 @@ +autoclothing +============ + +Automatically manage clothing work orders, allowing the user to set how many of +each clothing type every citizen should have. Usage:: + + autoclothing [number] + +Examples: + +* ``autoclothing cloth "short skirt" 10``: + Sets the desired number of cloth short skirts available per citizen to 10. +* ``autoclothing cloth dress``: + Displays the currently set number of cloth dresses chosen per citizen. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst new file mode 100644 index 000000000..9bd74cff7 --- /dev/null +++ b/docs/plugins/autodump.rst @@ -0,0 +1,29 @@ +autodump +======== +This plugin adds an option to the :kbd:`q` menu for stckpiles when `enabled `. +When autodump is enabled for a stockpile, any items placed in the stockpile will +automatically be designated to be dumped. + +Alternatively, you can use it to quickly move all items designated to be dumped. +Items are instantly moved to the cursor position, the dump flag is unset, +and the forbid flag is set, as if it had been dumped normally. +Be aware that any active dump item tasks still point at the item. + +Cursor must be placed on a floor tile so the items can be dumped there. + +Options: + +:destroy: Destroy instead of dumping. Doesn't require a cursor. + If called again before the game is resumed, cancels destroy. +:destroy-here: As ``destroy``, but only the selected item in the :kbd:`k` list, + or inside a container. + Alias ``autodump-destroy-here``, for keybindings. + :dfhack-keybind:`autodump-destroy-here` +:visible: Only process items that are not hidden. +:hidden: Only process hidden items. +:forbidden: Only process forbidden items (default: only unforbidden). + +``autodump-destroy-item`` destroys the selected item, which may be selected +in the :kbd:`k` list, or inside a container. If called again before the game +is resumed, cancels destruction of the item. +:dfhack-keybind:`autodump-destroy-item` diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst new file mode 100644 index 000000000..b330517d0 --- /dev/null +++ b/docs/plugins/autofarm.rst @@ -0,0 +1,21 @@ +autofarm +======== + +Automatically handles crop selection in farm plots based on current plant +stocks, and selects crops for planting if current stock is below a threshold. +Selected crops are dispatched on all farmplots. (Note that this plugin replaces +an older Ruby script of the same name.) + +Use the `enable` or `disable ` commands to change whether this plugin is +enabled. + +Usage: + +* ``autofarm runonce``: + Updates all farm plots once, without enabling the plugin +* ``autofarm status``: + Prints status information, including any applied limits +* ``autofarm default 30``: + Sets the default threshold +* ``autofarm threshold 150 helmet_plump tail_pig``: + Sets thresholds of individual plants diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst new file mode 100644 index 000000000..59800e1b9 --- /dev/null +++ b/docs/plugins/autogems.rst @@ -0,0 +1,7 @@ +autogems +======== +Creates a new Workshop Order setting, automatically cutting rough gems +when `enabled `. + +See `gui/autogems` for a configuration UI. If necessary, the ``autogems-reload`` +command reloads the configuration file produced by that script. diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst new file mode 100644 index 000000000..289874f94 --- /dev/null +++ b/docs/plugins/autohauler.rst @@ -0,0 +1,20 @@ +autohauler +========== +Autohauler is an autolabor fork. + +Rather than the all-of-the-above means of autolabor, autohauler will instead +only manage hauling labors and leave skilled labors entirely to the user, who +will probably use Dwarf Therapist to do so. + +Idle dwarves will be assigned the hauling labors; everyone else (including +those currently hauling) will have the hauling labors removed. This is to +encourage every dwarf to do their assigned skilled labors whenever possible, +but resort to hauling when those jobs are not available. This also implies +that the user will have a very tight skill assignment, with most skilled +labors only being assigned to just one dwarf, no dwarf having more than two +active skilled labors, and almost every non-military dwarf having at least +one skilled labor assigned. + +Autohauler allows skills to be flagged as to prevent hauling labors from +being assigned when the skill is present. By default this is the unused +ALCHEMIST labor but can be changed by the user. diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst new file mode 100644 index 000000000..b63b9102b --- /dev/null +++ b/docs/plugins/autolabor.rst @@ -0,0 +1,70 @@ +autolabor +========= +Automatically manage dwarf labors to efficiently complete jobs. +Autolabor tries to keep as many dwarves as possible busy but +also tries to have dwarves specialize in specific skills. + +The key is that, for almost all labors, once a dwarf begins a job it will finish that +job even if the associated labor is removed. Autolabor therefore frequently checks +which dwarf or dwarves should take new jobs for that labor, and sets labors accordingly. +Labors with equipment (mining, hunting, and woodcutting), which are abandoned +if labors change mid-job, are handled slightly differently to minimise churn. + +.. warning:: + + *autolabor will override any manual changes you make to labors while + it is enabled, including through other tools such as Dwarf Therapist* + +Simple usage: + +:enable autolabor: Enables the plugin with default settings. (Persistent per fortress) +:disable autolabor: Disables the plugin. + +Anything beyond this is optional - autolabor works well on the default settings. + +By default, each labor is assigned to between 1 and 200 dwarves (2-200 for mining). +By default 33% of the workforce become haulers, who handle all hauling jobs as well +as cleaning, pulling levers, recovering wounded, removing constructions, and filling ponds. +Other jobs are automatically assigned as described above. Each of these settings can be adjusted. + +Jobs are rarely assigned to nobles with responsibilities for meeting diplomats or merchants, +never to the chief medical dwarf, and less often to the bookeeper and manager. + +Hunting is never assigned without a butchery, and fishing is never assigned without a fishery. + +For each labor a preference order is calculated based on skill, biased against masters of other +trades and excluding those who can't do the job. The labor is then added to the best +dwarves for that labor. We assign at least the minimum number of dwarfs, in order of preference, +and then assign additional dwarfs that meet any of these conditions: + +* The dwarf is idle and there are no idle dwarves assigned to this labor +* The dwarf has non-zero skill associated with the labor +* The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. + +We stop assigning dwarfs when we reach the maximum allowed. + +Advanced usage: + +:autolabor []: + Set number of dwarves assigned to a labor. +:autolabor haulers: Set a labor to be handled by hauler dwarves. +:autolabor disable: Turn off autolabor for a specific labor. +:autolabor reset: Return a labor to the default handling. +:autolabor reset-all: Return all labors to the default handling. +:autolabor list: List current status of all labors. +:autolabor status: Show basic status information. + +See `autolabor-artisans` for a differently-tuned setup. + +Examples: + +``autolabor MINE`` + Keep at least 5 dwarves with mining enabled. +``autolabor CUT_GEM 1 1`` + Keep exactly 1 dwarf with gemcutting enabled. +``autolabor COOK 1 1 3`` + Keep 1 dwarf with cooking enabled, selected only from the top 3. +``autolabor FEED_WATER_CIVILIANS haulers`` + Have haulers feed and water wounded dwarves. +``autolabor CUTWOOD disable`` + Turn off autolabor for wood cutting. diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst new file mode 100644 index 000000000..81de0cb3d --- /dev/null +++ b/docs/plugins/automaterial.rst @@ -0,0 +1,33 @@ +automaterial +============ +This makes building constructions (walls, floors, fortifications, etc) a little bit +easier by saving you from having to trawl through long lists of materials each time +you place one. + +Firstly, it moves the last used material for a given construction type to the top of +the list, if there are any left. So if you build a wall with chalk blocks, the next +time you place a wall the chalk blocks will be at the top of the list, regardless of +distance (it only does this in "grouped" mode, as individual item lists could be huge). +This should mean you can place most constructions without having to search for your +preferred material type. + +.. image:: ../images/automaterial-mat.png + +Pressing :kbd:`a` while highlighting any material will enable that material for "auto select" +for this construction type. You can enable multiple materials as autoselect. Now the next +time you place this type of construction, the plugin will automatically choose materials +for you from the kinds you enabled. If there is enough to satisfy the whole placement, +you won't be prompted with the material screen - the construction will be placed and you +will be back in the construction menu as if you did it manually. + +When choosing the construction placement, you will see a couple of options: + +.. image:: ../images/automaterial-pos.png + +Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you need +to go to the material selection screen so you can toggle some materials on or off. + +The other option (auto type selection, off by default) can be toggled on with :kbd:`t`. If you +toggle this option on, instead of returning you to the main construction menu after selecting +materials, it returns you back to this screen. If you use this along with several autoselect +enabled materials, you should be able to place complex constructions more conveniently. diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst new file mode 100644 index 000000000..73f8ba502 --- /dev/null +++ b/docs/plugins/automelt.rst @@ -0,0 +1,5 @@ +automelt +======== +When automelt is enabled for a stockpile, any meltable items placed +in it will be designated to be melted. +This plugin adds an option to the :kbd:`q` menu when `enabled `. diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst new file mode 100644 index 000000000..f19cad42c --- /dev/null +++ b/docs/plugins/autonestbox.rst @@ -0,0 +1,19 @@ +autonestbox +=========== +Assigns unpastured female egg-layers to nestbox zones. Requires that you create +pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox +must be in the top left corner. Only 1 unit will be assigned per pen, regardless +of the size. The age of the units is currently not checked, most birds grow up +quite fast. Egglayers who are also grazers will be ignored, since confining them +to a 1x1 pasture is not a good idea. Only tame and domesticated own units are +processed since pasturing half-trained wild egglayers could destroy your neat +nestbox zones when they revert to wild. When called without options autonestbox +will instantly run once. + +Options: + +:start: Start running every X frames (df simulation ticks). + Default: X=6000, which would be every 60 seconds at 100fps. +:stop: Stop running automatically. +:sleep: Must be followed by number X. Changes the timer to sleep X + frames between runs. diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst new file mode 100644 index 000000000..4f0d95efd --- /dev/null +++ b/docs/plugins/autotrade.rst @@ -0,0 +1,5 @@ +autotrade +========= +When autotrade is enabled for a stockpile, any items placed in it will be +designated to be taken to the Trade Depot whenever merchants are on the map. +This plugin adds an option to the :kbd:`q` menu when `enabled `. diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst new file mode 100644 index 000000000..6d5e306d1 --- /dev/null +++ b/docs/plugins/blueprint.rst @@ -0,0 +1,112 @@ +blueprint +========= +The ``blueprint`` command exports the structure of a portion of your fortress in +a blueprint file that you (or anyone else) can later play back with `quickfort`. + +Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` +subdirectory of your DF folder. The map area to turn into a blueprint is either +selected interactively with the ``blueprint gui`` command or, if the GUI is not +used, starts at the active cursor location and extends right and down for the +requested width and height. + +**Usage:** + + ``blueprint [] [ []] []`` + + ``blueprint gui [ []] []`` + +**Examples:** + +``blueprint gui`` + Runs `gui/blueprint`, the interactive frontend, where all configuration for + a ``blueprint`` command can be set visually and interactively. + +``blueprint 30 40 bedrooms`` + Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting + from the active cursor on the current z-level. Blueprints are written + sequentially to ``bedrooms.csv`` in the ``blueprints`` directory. + +``blueprint 30 40 bedrooms dig --cursor 108,100,150`` + Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and + the start of the blueprint area is set to a specific value instead of using + the in-game cursor position. + +**Positional Parameters:** + +:``width``: Width of the area (in tiles) to translate. +:``height``: Height of the area (in tiles) to translate. +:``depth``: Number of z-levels to translate. Positive numbers go *up* from the + cursor and negative numbers go *down*. Defaults to 1 if not specified, + indicating that the blueprint should only include the current z-level. +:``name``: Base name for blueprint files created in the ``blueprints`` + directory. If no name is specified, "blueprint" is used by default. The + string must contain some characters other than numbers so the name won't be + confused with the optional ``depth`` parameter. + +**Phases:** + +If you want to generate blueprints only for specific phases, add their names to +the commandline, anywhere after the blueprint base name. You can list multiple +phases; just separate them with a space. + +:``dig``: Generate quickfort ``#dig`` blueprints for digging natural stone. +:``carve``: Generate quickfort ``#dig`` blueprints for smoothing and carving. +:``build``: Generate quickfort ``#build`` blueprints for constructions and + buildings. +:``place``: Generate quickfort ``#place`` blueprints for placing stockpiles. +:``zone``: Generate quickfort ``#zone`` blueprints for designating zones. +:``query``: Generate quickfort ``#query`` blueprints for configuring rooms. + +If no phases are specified, phases are autodetected. For example, a ``#place`` +blueprint will be created only if there are stockpiles in the blueprint area. + +**Options:** + +``-c``, ``--cursor ,,``: + Use the specified map coordinates instead of the current cursor position for + the upper left corner of the blueprint range. If this option is specified, + then an active game map cursor is not necessary. +``-e``, ``--engrave``: + Record engravings in the ``carve`` phase. If this option is not specified, + engravings are ignored. +``-f``, ``--format ``: + Select the output format of the generated files. See the ``Output formats`` + section below for options. If not specified, the output format defaults to + "minimal", which will produce a small, fast ``.csv`` file. +``-h``, ``--help``: + Show command help text. +``-s``, ``--playback-start ,,``: + Specify the column and row offsets (relative to the upper-left corner of the + blueprint, which is ``1,1``) where the player should put the cursor when the + blueprint is played back with `quickfort`, in + `quickfort start marker ` format, for example: + ``10,10,central stairs``. If there is a space in the comment, you will need + to surround the parameter string in double quotes: ``"-s10,10,central stairs"`` or + ``--playback-start "10,10,central stairs"`` or + ``"--playback-start=10,10,central stairs"``. +``-t``, ``--splitby ``: + Split blueprints into multiple files. See the ``Splitting output into + multiple files`` section below for details. If not specified, defaults to + "none", which will create a standard quickfort + `multi-blueprint ` file. + +**Output formats:** + +Here are the values that can be passed to the ``--format`` flag: + +:``minimal``: + Creates ``.csv`` files with minimal file size that are fast to read and + write. This is the default. +:``pretty``: + Makes the blueprints in the ``.csv`` files easier to read and edit with a text + editor by adding extra spacing and alignment markers. + +**Splitting output into multiple files:** + +The ``--splitby`` flag can take any of the following values: + +:``none``: + Writes all blueprints into a single file. This is the standard format for + quickfort fortress blueprint bundles and is the default. +:``phase``: + Creates a separate file for each phase. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst new file mode 100644 index 000000000..ca6273d10 --- /dev/null +++ b/docs/plugins/buildingplan.rst @@ -0,0 +1,88 @@ +buildingplan +============ +When active (via ``enable buildingplan``), this plugin adds a planning mode for +building placement. You can then place furniture, constructions, and other buildings +before the required materials are available, and they will be created in a suspended +state. Buildingplan will periodically scan for appropriate items, and the jobs will +be unsuspended when the items are available. + +This is very useful when combined with `workflow` - you can set a constraint +to always have one or two doors/beds/tables/chairs/etc available, and place +as many as you like. The plugins then take over and fulfill the orders, +with minimal space dedicated to stockpiles. + +.. _buildingplan-filters: + +Item filtering +-------------- + +While placing a building, you can set filters for what materials you want the building made +out of, what quality you want the component items to be, and whether you want the items to +be decorated. + +If a building type takes more than one item to construct, use :kbd:`Ctrl`:kbd:`Left` and +:kbd:`Ctrl`:kbd:`Right` to select the item that you want to set filters for. Any filters that +you set will be used for all buildings of the selected type placed from that point onward +(until you set a new filter or clear the current one). Buildings placed before the filters +were changed will keep the filter values that were set when the building was placed. + +For example, you can be sure that all your constructed walls are the same color by setting +a filter to accept only certain types of stone. + +Quickfort mode +-------------- + +If you use the external Python Quickfort to apply building blueprints instead of the native +DFHack `quickfort` script, you must enable Quickfort mode. This temporarily enables +buildingplan for all building types and adds an extra blank screen after every building +placement. This "dummy" screen is needed for Python Quickfort to interact successfully with +Dwarf Fortress. + +Note that Quickfort mode is only for compatibility with the legacy Python Quickfort. The +DFHack `quickfort` script does not need Quickfort mode to be enabled. The `quickfort` script +will successfully integrate with buildingplan as long as the buildingplan plugin is enabled. + +.. _buildingplan-settings: + +Global settings +--------------- + +The buildingplan plugin has several global settings that can be set from the UI (:kbd:`G` +from any building placement screen, for example: :kbd:`b`:kbd:`a`:kbd:`G`). These settings +can also be set from the ``DFHack#`` prompt once a map is loaded (or from your +``onMapLoad.init`` file) with the syntax:: + + buildingplan set + +and displayed with:: + + buildingplan set + +The available settings are: + ++----------------+---------+-----------+---------------------------------------+ +| Setting | Default | Persisted | Description | ++================+=========+===========+=======================================+ +| all_enabled | false | no | Enable planning mode for all building | +| | | | types. | ++----------------+---------+-----------+---------------------------------------+ +| blocks | true | yes | Allow blocks, boulders, logs, or bars | ++----------------+---------+ | to be matched for generic "building | +| boulders | true | | material" items | ++----------------+---------+ | | +| logs | true | | | ++----------------+---------+ | | +| bars | false | | | ++----------------+---------+-----------+---------------------------------------+ +| 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 +could add this to your ``onMapLoad.init`` file:: + + on-new-fortress buildingplan set boulders false; buildingplan set logs false + +Persisted settings (i.e. ``blocks``, ``boulders``, ``logs``, and ``bars``) are saved with +your game, so you only need to set them to the values you want once. diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst new file mode 100644 index 000000000..787b3ff37 --- /dev/null +++ b/docs/plugins/burrows.rst @@ -0,0 +1,38 @@ +burrows +======= +Miscellaneous burrow control. Allows manipulating burrows and automated burrow +expansion while digging. + +Options: + +:enable feature ...: + Enable features of the plugin. +:disable feature ...: + Disable features of the plugin. +:clear-unit burrow burrow ...: + Remove all units from the burrows. +:clear-tiles burrow burrow ...: + Remove all tiles from the burrows. +:set-units target-burrow src-burrow ...: + Clear target, and adds units from source burrows. +:add-units target-burrow src-burrow ...: + Add units from the source burrows to the target. +:remove-units target-burrow src-burrow ...: + Remove units in source burrows from the target. +:set-tiles target-burrow src-burrow ...: + Clear target and adds tiles from the source burrows. +:add-tiles target-burrow src-burrow ...: + Add tiles from the source burrows to the target. +:remove-tiles target-burrow src-burrow ...: + Remove tiles in source burrows from the target. + + For these three options, in place of a source burrow it is + possible to use one of the following keywords: ABOVE_GROUND, + SUBTERRANEAN, INSIDE, OUTSIDE, LIGHT, DARK, HIDDEN, REVEALED + +Features: + +:auto-grow: When a wall inside a burrow with a name ending in '+' is dug + out, the burrow is extended to newly-revealed adjacent walls. + This final '+' may be omitted in burrow name args of commands above. + Digging 1-wide corridors with the miner inside the burrow is SLOW. diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst new file mode 100644 index 000000000..c6da2516f --- /dev/null +++ b/docs/plugins/changeitem.rst @@ -0,0 +1,26 @@ +changeitem +========== +Allows changing item material and base quality. By default the item currently +selected in the UI will be changed (you can select items in the 'k' list +or inside containers/inventory). By default change is only allowed if materials +is of the same subtype (for example wood<->wood, stone<->stone etc). But since +some transformations work pretty well and may be desired you can override this +with 'force'. Note that some attributes will not be touched, possibly resulting +in weirdness. To get an idea how the RAW id should look like, check some items +with 'info'. Using 'force' might create items which are not touched by +crafters/haulers. + +Options: + +:info: Don't change anything, print some info instead. +:here: Change all items at the cursor position. Requires in-game cursor. +:material, m: Change material. Must be followed by valid material RAW id. +:quality, q: Change base quality. Must be followed by number (0-5). +:force: Ignore subtypes, force change to new material. + +Examples: + +``changeitem m INORGANIC:GRANITE here`` + Change material of all items under the cursor to granite. +``changeitem q 5`` + Change currently selected item to masterpiece quality. diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst new file mode 100644 index 000000000..87c27921c --- /dev/null +++ b/docs/plugins/changelayer.rst @@ -0,0 +1,60 @@ +changelayer +=========== +Changes material of the geology layer under cursor to the specified inorganic +RAW material. Can have impact on all surrounding regions, not only your embark! +By default changing stone to soil and vice versa is not allowed. By default +changes only the layer at the cursor position. Note that one layer can stretch +across lots of z levels. By default changes only the geology which is linked +to the biome under the cursor. That geology might be linked to other biomes +as well, though. Mineral veins and gem clusters will stay on the map. Use +`changevein` for them. + +tl;dr: You will end up with changing quite big areas in one go, especially if +you use it in lower z levels. Use with care. + +Options: + +:all_biomes: Change selected layer for all biomes on your map. + Result may be undesirable since the same layer can AND WILL + be on different z-levels for different biomes. Use the tool + 'probe' to get an idea how layers and biomes are distributed + on your map. +:all_layers: Change all layers on your map (only for the selected biome + unless 'all_biomes' is added). + Candy mountain, anyone? Will make your map quite boring, + but tidy. +:force: Allow changing stone to soil and vice versa. !!THIS CAN HAVE + WEIRD EFFECTS, USE WITH CARE!! + Note that soil will not be magically replaced with stone. + You will, however, get a stone floor after digging so it + will allow the floor to be engraved. + Note that stone will not be magically replaced with soil. + You will, however, get a soil floor after digging so it + could be helpful for creating farm plots on maps with no + soil. +:verbose: Give some details about what is being changed. +:trouble: Give some advice about known problems. + +Examples: + +``changelayer GRANITE`` + Convert layer at cursor position into granite. +``changelayer SILTY_CLAY force`` + Convert layer at cursor position into clay even if it's stone. +``changelayer MARBLE all_biomes all_layers`` + Convert all layers of all biomes which are not soil into marble. + +.. note:: + + * If you use changelayer and nothing happens, try to pause/unpause the game + for a while and try to move the cursor to another tile. Then try again. + If that doesn't help try temporarily changing some other layer, undo your + changes and try again for the layer you want to change. Saving + and reloading your map might also help. + * You should be fine if you only change single layers without the use + of 'force'. Still it's advisable to save your game before messing with + the map. + * When you force changelayer to convert soil to stone you might experience + weird stuff (flashing tiles, tiles changed all over place etc). + Try reverting the changes manually or even better use an older savegame. + You did save your game, right? diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst new file mode 100644 index 000000000..ad7ab3d54 --- /dev/null +++ b/docs/plugins/changevein.rst @@ -0,0 +1,10 @@ +changevein +========== +Changes material of the vein under cursor to the specified inorganic RAW +material. Only affects tiles within the current 16x16 block - for veins and +large clusters, you will need to use this command multiple times. + +Example: + +``changevein NATIVE_PLATINUM`` + Convert vein at cursor position into platinum ore. diff --git a/docs/plugins/clean.rst b/docs/plugins/clean.rst new file mode 100644 index 000000000..f2d0c361d --- /dev/null +++ b/docs/plugins/clean.rst @@ -0,0 +1,16 @@ +clean +===== +Cleans all the splatter that get scattered all over the map, items and +creatures. In an old fortress, this can significantly reduce FPS lag. It can +also spoil your !!FUN!!, so think before you use it. + +Options: + +:map: Clean the map tiles. By default, it leaves mud and snow alone. +:units: Clean the creatures. Will also clean hostiles. +:items: Clean all the items. Even a poisoned blade. + +Extra options for ``map``: + +:mud: Remove mud in addition to the normal stuff. +:snow: Also remove snow coverings. diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst new file mode 100644 index 000000000..a8169d83b --- /dev/null +++ b/docs/plugins/cleanconst.rst @@ -0,0 +1,7 @@ +cleanconst +========== +Cleans up construction materials. + +This utility alters all constructions on the map so that they spawn their +building component when they are disassembled, allowing their actual +build items to be safely deleted. This can improve FPS in extreme situations. diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst new file mode 100644 index 000000000..7f80e35cf --- /dev/null +++ b/docs/plugins/cleanowned.rst @@ -0,0 +1,19 @@ +cleanowned +========== +Confiscates items owned by dwarfs. By default, owned food on the floor +and rotten items are confistacted and dumped. + +Options: + +:all: confiscate all owned items +:scattered: confiscated and dump all items scattered on the floor +:x: confiscate/dump items with wear level 'x' and more +:X: confiscate/dump items with wear level 'X' and more +:dryrun: a dry run. combine with other options to see what will happen + without it actually happening. + +Example: + +``cleanowned scattered X`` + This will confiscate rotten and dropped food, garbage on the floors and any + worn items with 'X' damage and above. diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst new file mode 100644 index 000000000..061b405ef --- /dev/null +++ b/docs/plugins/command-prompt.rst @@ -0,0 +1,16 @@ +command-prompt +============== +An in-game DFHack terminal, where you can enter other commands. + +:dfhack-keybind:`command-prompt` + +Usage: ``command-prompt [entry]`` + +If called with an entry, it starts with that text filled in. +Most useful for developers, who can set a keybinding to open +a laungage interpreter for lua or Ruby by starting with the +`:lua ` or `:rb ` commands. + +Otherwise somewhat similar to `gui/quickcmd`. + +.. image:: ../images/command-prompt.png diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst new file mode 100644 index 000000000..c7fb89bb0 --- /dev/null +++ b/docs/plugins/confirm.rst @@ -0,0 +1,12 @@ +confirm +======= +Implements several confirmation dialogs for potentially destructive actions +(for example, seizing goods from traders or deleting hauling routes). + +Usage: + +:enable confirm: Enable all confirmations; alias ``confirm enable all``. + Replace with ``disable`` to disable. +:confirm help: List available confirmation dialogues. +:confirm enable option1 [option2...]: + Enable (or disable) specific confirmation dialogues. diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst new file mode 100644 index 000000000..f37154f80 --- /dev/null +++ b/docs/plugins/createitem.rst @@ -0,0 +1,48 @@ +createitem +========== +Allows creating new items of arbitrary types and made of arbitrary materials. A +unit must be selected in-game to use this command. By default, items created are +spawned at the feet of the selected unit. + +Specify the item and material information as you would indicate them in +custom reaction raws, with the following differences: + +* Separate the item and material with a space rather than a colon +* If the item has no subtype, the ``:NONE`` can be omitted +* If the item is ``REMAINS``, ``FISH``, ``FISH_RAW``, ``VERMIN``, ``PET``, or ``EGG``, + specify a ``CREATURE:CASTE`` pair instead of a material token. +* If the item is a ``PLANT_GROWTH``, specify a ``PLANT_ID:GROWTH_ID`` pair + instead of a material token. + +Corpses, body parts, and prepared meals cannot be created using this tool. + +To obtain the item and material tokens of an existing item, run +``createitem inspect``. Its output can be passed directly as arguments to +``createitem`` to create new matching items, as long as the item type is +supported. + +Examples: + +* Create 2 pairs of steel gauntlets:: + + createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2 + +* Create tower-cap logs:: + + createitem WOOD PLANT_MAT:TOWER_CAP:WOOD + +* Create bilberries:: + + createitem PLANT_GROWTH BILBERRY:FRUIT + +For more examples, :wiki:`see this wiki page `. + +To change where new items are placed, first run the command with a +destination type while an appropriate destination is selected. + +Options: + +:floor: Subsequent items will be placed on the floor beneath the selected unit's feet. +:item: Subsequent items will be stored inside the currently selected item. +:building: Subsequent items will become part of the currently selected building. + Good for loading traps; do not use with workshops (or deconstruct to use the item). diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst new file mode 100644 index 000000000..1bf1803fa --- /dev/null +++ b/docs/plugins/cursecheck.rst @@ -0,0 +1,42 @@ +cursecheck +========== +Checks a single map tile or the whole map/world for cursed creatures (ghosts, +vampires, necromancers, werebeasts, zombies). + +With an active in-game cursor only the selected tile will be observed. +Without a cursor the whole map will be checked. + +By default cursed creatures will be only counted in case you just want to find +out if you have any of them running around in your fort. Dead and passive +creatures (ghosts who were put to rest, killed vampires, ...) are ignored. +Undead skeletons, corpses, bodyparts and the like are all thrown into the curse +category "zombie". Anonymous zombies and resurrected body parts will show +as "unnamed creature". + +Options: + +:detail: Print full name, date of birth, date of curse and some status + info (some vampires might use fake identities in-game, though). +:ids: Print the creature and race IDs. +:nick: Set the type of curse as nickname (does not always show up + in-game, some vamps don't like nicknames). +:all: Include dead and passive cursed creatures (can result in a quite + long list after having FUN with necromancers). +:verbose: Print all curse tags (if you really want to know it all). + +Examples: + +``cursecheck detail all`` + Give detailed info about all cursed creatures including deceased ones (no + in-game cursor). +``cursecheck nick`` + Give a nickname all living/active cursed creatures on the map(no in-game + cursor). + +.. note:: + + If you do a full search (with the option "all") former ghosts will show up + with the cursetype "unknown" because their ghostly flag is not set. + + Please report any living/active creatures with cursetype "unknown" - + this is most likely with mods which introduce new types of curses. diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst new file mode 100644 index 000000000..2bef7d28a --- /dev/null +++ b/docs/plugins/debug.rst @@ -0,0 +1,89 @@ +debug +===== +Manager for DFHack runtime debug prints. Debug prints are grouped by plugin name, +category name and print level. Levels are ``trace``, ``debug``, ``info``, +``warning`` and ``error``. + +The runtime message printing is controlled using filters. Filters set the +visible messages of all matching categories. Matching uses regular expression syntax, +which allows listing multiple alternative matches or partial name matches. +This syntax is a C++ version of the ECMA-262 grammar (Javascript regular expressions). +Details of differences can be found at +https://en.cppreference.com/w/cpp/regex/ecmascript + +Persistent filters are stored in ``dfhack-config/runtime-debug.json``. +Oldest filters are applied first. That means a newer filter can override the +older printing level selection. + +Usage: ``debugfilter [subcommand] [parameters...]`` + +The following subcommands are supported: + +help +---- +Give overall help or a detailed help for a subcommand. + +Usage: ``debugfilter help [subcommand]`` + +category +-------- +List available debug plugin and category names. + +Usage: ``debugfilter category [plugin regex] [category regex]`` + +The list can be filtered using optional regex parameters. If filters aren't +given then the it uses ``"."`` regex which matches any character. The regex +parameters are good way to test regex before passing them to ``set``. + +filter +------ +List active and passive debug print level changes. + +Usage: ``debugfilter filter [id]`` + +Optional ``id`` parameter is the id listed as first column in the filter list. +If id is given then the command shows information for the given filter only in +multi line format that is better format if filter has long regex. + +set +--- +Creates a new debug filter to set category printing levels. + +Usage: ``debugfilter set [level] [plugin regex] [category regex]`` + +Adds a filter that will be deleted when DF process exists or plugin is unloaded. + +Usage: ``debugfilter set persistent [level] [plugin regex] [category regex]`` + +Stores the filter in the configuration file to until ``unset`` is used to remove +it. + +Level is the minimum debug printing level to show in log. + +* ``trace``: Possibly very noisy messages which can be printed many times per second + +* ``debug``: Messages that happen often but they should happen only a couple of times per second + +* ``info``: Important state changes that happen rarely during normal execution + +* ``warning``: Enabled by default. Shows warnings about unexpected events which code managed to handle correctly. + +* ``error``: Enabled by default. Shows errors which code can't handle without user intervention. + +unset +----- +Delete a space separated list of filters + +Usage: ``debugfilter unset [id...]`` + +disable +------- +Disable a space separated list of filters but keep it in the filter list + +Usage: ``debugfilter disable [id...]`` + +enable +------ +Enable a space sperate list of filters + +Usage: ``debugfilter enable [id...]`` diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst new file mode 100644 index 000000000..a6b944381 --- /dev/null +++ b/docs/plugins/deramp.rst @@ -0,0 +1,6 @@ +deramp +====== +Removes all ramps designated for removal from the map. This is useful for +replicating the old channel digging designation. It also removes any and +all 'down ramps' that can remain after a cave-in (you don't have to designate +anything for that to happen). diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst new file mode 100644 index 000000000..b6f4d64f7 --- /dev/null +++ b/docs/plugins/dig-now.rst @@ -0,0 +1,61 @@ +dig-now +======= + +Instantly completes non-marker dig designations, modifying tile shapes and +creating boulders, ores, and gems as if a miner were doing the mining or +engraving. By default, the entire map is processed and boulder generation +follows standard game rules, but the behavior is configurable. + +Note that no units will get mining or engraving experience for the dug/engraved +tiles. + +Trees and roots are not currently handled by this plugin and will be skipped. +Requests for engravings are also skipped since they would depend on the skill +and creative choices of individual engravers. Other types of engraving (i.e. +smoothing and track carving) are handled. + +Usage:: + + dig-now [ []] [] + +Where the optional ```` pair can be used to specify the coordinate bounds +within which ``dig-now`` will operate. If they are not specified, ``dig-now`` +will scan the entire map. If only one ```` is specified, only the tile at +that coordinate is processed. + +Any ```` parameters can either be an ``,,`` triple (e.g. +``35,12,150``) or the string ``here``, which means the position of the active +game cursor should be used. + +Examples: + +``dig-now`` + Dig designated tiles according to standard game rules. + +``dig-now --clean`` + Dig designated tiles, but don't generate any boulders, ores, or gems. + +``dig-now --dump here`` + Dig tiles and dump all generated boulders, ores, and gems at the tile under + the game cursor. + +Options: + +:``-c``, ``--clean``: + Don't generate any boulders, ores, or gems. Equivalent to + ``--percentages 0,0,0,0``. +:``-d``, ``--dump ``: + Dump any generated items at the specified coordinates. If the tile at those + coordinates is open space or is a wall, items will be generated on the + closest walkable tile below. +:``-e``, ``--everywhere``: + Generate a boulder, ore, or gem for every tile that can produce one. + Equivalent to ``--percentages 100,100,100,100``. +:``-h``, ``--help``: + Show quick usage help text. +:``-p``, ``--percentages ,,,``: + Set item generation percentages for each of the tile categories. The + ``vein`` category includes both the large oval clusters and the long stringy + mineral veins. Default is ``25,33,100,100``. +:``-z``, ``--cur-zlevel``: + Restricts the bounds to the currently visible z-level. diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst new file mode 100644 index 000000000..f09626d0c --- /dev/null +++ b/docs/plugins/dig.rst @@ -0,0 +1,20 @@ +dig +=== +This plugin makes many automated or complicated dig patterns easy. + +Basic commands: + +:digv: Designate all of the selected vein for digging. +:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option + to remove designations, for if you accidentally set 50 levels at once. +:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + +:dfhack-keybind:`digv` + +.. note:: + + All commands implemented by the `dig` plugin (listed by ``ls dig``) support + specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, + where ``#`` is a number from 1 to 7. If a priority is not specified, the + priority selected in-game is used as the default. diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst new file mode 100644 index 000000000..dd957feb2 --- /dev/null +++ b/docs/plugins/digFlood.rst @@ -0,0 +1,18 @@ +digFlood +======== +Automatically digs out specified veins as they are discovered. It runs once +every time a dwarf finishes a dig job. It will only dig out appropriate tiles +that are adjacent to the finished dig job. To add a vein type, use ``digFlood 1 [type]``. +This will also enable the plugin. To remove a vein type, use ``digFlood 0 [type] 1`` +to disable, then remove, then re-enable. + +Usage: + +:help digflood: detailed help message +:digFlood 0: disable the plugin +:digFlood 1: enable the plugin +:digFlood 0 MICROCLINE COAL_BITUMINOUS 1: + disable plugin, remove microcline and bituminous coal from monitoring, then re-enable the plugin +:digFlood CLEAR: remove all inorganics from monitoring +:digFlood digAll1: ignore the monitor list and dig any vein +:digFlood digAll0: disable digAll mode diff --git a/docs/plugins/digcircle.rst b/docs/plugins/digcircle.rst new file mode 100644 index 000000000..fc31b65df --- /dev/null +++ b/docs/plugins/digcircle.rst @@ -0,0 +1,35 @@ +digcircle +========= +A command for easy designation of filled and hollow circles. +It has several types of options. + +Shape: + +:hollow: Set the circle to hollow (default) +:filled: Set the circle to filled +:#: Diameter in tiles (default = 0, does nothing) + +Action: + +:set: Set designation (default) +:unset: Unset current designation +:invert: Invert designations already present + +Designation types: + +:dig: Normal digging designation (default) +:ramp: Ramp digging +:ustair: Staircase up +:dstair: Staircase down +:xstair: Staircase up/down +:chan: Dig channel + +After you have set the options, the command called with no options +repeats with the last selected parameters. + +Examples: + +``digcircle filled 3`` + Dig a filled circle with diameter = 3. +``digcircle`` + Do it again. diff --git a/docs/plugins/digexp.rst b/docs/plugins/digexp.rst new file mode 100644 index 000000000..e4412d051 --- /dev/null +++ b/docs/plugins/digexp.rst @@ -0,0 +1,31 @@ +digexp +====== +This command is for :wiki:`exploratory mining `. + +There are two variables that can be set: pattern and filter. + +Patterns: + +:diag5: diagonals separated by 5 tiles +:diag5r: diag5 rotated 90 degrees +:ladder: A 'ladder' pattern +:ladderr: ladder rotated 90 degrees +:clear: Just remove all dig designations +:cross: A cross, exactly in the middle of the map. + +Filters: + +:all: designate whole z-level +:hidden: designate only hidden tiles of z-level (default) +:designated: Take current designation and apply pattern to it. + +After you have a pattern set, you can use ``expdig`` to apply it again. + +Examples: + +``expdig diag5 hidden`` + Designate the diagonal 5 patter over all hidden tiles +``expdig`` + Apply last used pattern and filter +``expdig ladder designated`` + Take current designations and replace them with the ladder pattern diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst new file mode 100644 index 000000000..0557225fb --- /dev/null +++ b/docs/plugins/diggingInvaders.rst @@ -0,0 +1,29 @@ +diggingInvaders +=============== +Makes invaders dig or destroy constructions to get to your dwarves. + +To enable/disable the pluging, use: ``diggingInvaders (1|enable)|(0|disable)`` + +Basic usage: + +:add GOBLIN: registers the race GOBLIN as a digging invader. Case-sensitive. +:remove GOBLIN: unregisters the race GOBLIN as a digging invader. Case-sensitive. +:now: makes invaders try to dig now, if plugin is enabled +:clear: clears all digging invader races +:edgesPerTick n: makes the pathfinding algorithm work on at most n edges per tick. + Set to 0 or lower to make it unlimited. + +You can also use ``diggingInvaders setCost (race) (action) n`` to set the +pathing cost of particular action, or ``setDelay`` to set how long it takes. +Costs and delays are per-tile, and the table shows default values. + +============================== ======= ====== ================================= +Action Cost Delay Notes +============================== ======= ====== ================================= +``walk`` 1 0 base cost in the path algorithm +``destroyBuilding`` 2 1,000 delay adds to the job_completion_timer of destroy building jobs that are assigned to invaders +``dig`` 10,000 1,000 digging soil or natural stone +``destroyRoughConstruction`` 1,000 1,000 constructions made from boulders +``destroySmoothConstruction`` 100 100 constructions made from blocks or bars +============================== ======= ====== ================================= + diff --git a/docs/plugins/digl.rst b/docs/plugins/digl.rst new file mode 100644 index 000000000..f09626d0c --- /dev/null +++ b/docs/plugins/digl.rst @@ -0,0 +1,20 @@ +dig +=== +This plugin makes many automated or complicated dig patterns easy. + +Basic commands: + +:digv: Designate all of the selected vein for digging. +:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option + to remove designations, for if you accidentally set 50 levels at once. +:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + +:dfhack-keybind:`digv` + +.. note:: + + All commands implemented by the `dig` plugin (listed by ``ls dig``) support + specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, + where ``#`` is a number from 1 to 7. If a priority is not specified, the + priority selected in-game is used as the default. diff --git a/docs/plugins/diglx.rst b/docs/plugins/diglx.rst new file mode 100644 index 000000000..f09626d0c --- /dev/null +++ b/docs/plugins/diglx.rst @@ -0,0 +1,20 @@ +dig +=== +This plugin makes many automated or complicated dig patterns easy. + +Basic commands: + +:digv: Designate all of the selected vein for digging. +:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option + to remove designations, for if you accidentally set 50 levels at once. +:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + +:dfhack-keybind:`digv` + +.. note:: + + All commands implemented by the `dig` plugin (listed by ``ls dig``) support + specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, + where ``#`` is a number from 1 to 7. If a priority is not specified, the + priority selected in-game is used as the default. diff --git a/docs/plugins/digtype.rst b/docs/plugins/digtype.rst new file mode 100644 index 000000000..1c2d49eaa --- /dev/null +++ b/docs/plugins/digtype.rst @@ -0,0 +1,19 @@ +digtype +======= +For every tile on the map of the same vein type as the selected tile, +this command designates it to have the same designation as the +selected tile. If the selected tile has no designation, they will be +dig designated. +If an argument is given, the designation of the selected tile is +ignored, and all appropriate tiles are set to the specified +designation. + +Options: + +:dig: +:channel: +:ramp: +:updown: up/down stairs +:up: up stairs +:down: down stairs +:clear: clear designation diff --git a/docs/plugins/digv.rst b/docs/plugins/digv.rst new file mode 100644 index 000000000..f09626d0c --- /dev/null +++ b/docs/plugins/digv.rst @@ -0,0 +1,20 @@ +dig +=== +This plugin makes many automated or complicated dig patterns easy. + +Basic commands: + +:digv: Designate all of the selected vein for digging. +:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option + to remove designations, for if you accidentally set 50 levels at once. +:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + +:dfhack-keybind:`digv` + +.. note:: + + All commands implemented by the `dig` plugin (listed by ``ls dig``) support + specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, + where ``#`` is a number from 1 to 7. If a priority is not specified, the + priority selected in-game is used as the default. diff --git a/docs/plugins/digvx.rst b/docs/plugins/digvx.rst new file mode 100644 index 000000000..f09626d0c --- /dev/null +++ b/docs/plugins/digvx.rst @@ -0,0 +1,20 @@ +dig +=== +This plugin makes many automated or complicated dig patterns easy. + +Basic commands: + +:digv: Designate all of the selected vein for digging. +:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option + to remove designations, for if you accidentally set 50 levels at once. +:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + +:dfhack-keybind:`digv` + +.. note:: + + All commands implemented by the `dig` plugin (listed by ``ls dig``) support + specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, + where ``#`` is a number from 1 to 7. If a priority is not specified, the + priority selected in-game is used as the default. diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst new file mode 100644 index 000000000..cbe3e8bdc --- /dev/null +++ b/docs/plugins/dwarfmonitor.rst @@ -0,0 +1,78 @@ +dwarfmonitor +============ +Records dwarf activity to measure fort efficiency. + +Options: + +:enable : Start monitoring ``mode``. ``mode`` can be "work", "misery", + "weather", or "all". This will enable all corresponding widgets, + if applicable. +:disable : Stop monitoring ``mode``, and disable corresponding widgets, if applicable. +:stats: Show statistics summary +:prefs: Show dwarf preferences summary +:reload: Reload configuration file (``dfhack-config/dwarfmonitor.json``) + +:dfhack-keybind:`dwarfmonitor` + +Widget configuration: + +The following types of widgets (defined in :file:`hack/lua/plugins/dwarfmonitor.lua`) +can be displayed on the main fortress mode screen: + +:date: Show the in-game date +:misery: Show overall happiness levels of all dwarves +:weather: Show current weather (rain/snow) +:cursor: Show the current mouse cursor position + +The file :file:`dfhack-config/dwarfmonitor.json` can be edited to control the +positions and settings of all widgets displayed. This file should contain a +JSON object with the key ``widgets`` containing an array of objects - see the +included file in the ``dfhack-config`` folder for an example: + +.. code-block:: lua + + { + "widgets": [ + { + "type": "widget type (weather, misery, etc.)", + "x": X coordinate, + "y": Y coordinate + <...additional options...> + } + ] + } + +X and Y coordinates begin at zero (in the upper left corner of the screen). +Negative coordinates will be treated as distances from the lower right corner, +beginning at 1 - e.g. an x coordinate of 0 is the leftmost column, while an x +coordinate of 1 is the rightmost column. + +By default, the x and y coordinates given correspond to the leftmost tile of +the widget. Including an ``anchor`` option set to ``right`` will cause the +rightmost tile of the widget to be located at this position instead. + +Some widgets support additional options: + +* ``date`` widget: + + * ``format``: specifies the format of the date. The following characters + are replaced (all others, such as punctuation, are not modified) + + * ``Y`` or ``y``: The current year + * ``M``: The current month, zero-padded if necessary + * ``m``: The current month, *not* zero-padded + * ``D``: The current day, zero-padded if necessary + * ``d``: The current day, *not* zero-padded + + The default date format is ``Y-M-D``, per the ISO8601_ standard. + + .. _ISO8601: https://en.wikipedia.org/wiki/ISO_8601 + +* ``cursor`` widget: + + * ``format``: Specifies the format. ``X``, ``x``, ``Y``, and ``y`` are + replaced with the corresponding cursor cordinates, while all other + characters are unmodified. + * ``show_invalid``: If set to ``true``, the mouse coordinates will both be + displayed as ``-1`` when the cursor is outside of the DF window; otherwise, + nothing will be displayed. diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst new file mode 100644 index 000000000..1ceb1bc2f --- /dev/null +++ b/docs/plugins/dwarfvet.rst @@ -0,0 +1,19 @@ +dwarfvet +======== +Enables Animal Caretaker functionality + +Always annoyed your dragons become useless after a minor injury? Well, with +dwarfvet, your animals become first rate members of your fort. It can also +be used to train medical skills. + +Animals need to be treated in an animal hospital, which is simply a hospital +that is also an animal training zone. The console will print out a list on game +load, and whenever one is added or removed. Dwarfs must have the Animal Caretaker +labor to treat animals. Normal medical skills are used (and no experience is given +to the Animal Caretaker skill). + +Options: + +:enable: Enables Animal Caretakers to treat and manage animals +:disable: Turns off the plguin +:report: Reports all zones that the game considers animal hospitals diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst new file mode 100644 index 000000000..4500a919e --- /dev/null +++ b/docs/plugins/embark-assistant.rst @@ -0,0 +1,9 @@ +embark-assistant +================ + +This plugin provides embark site selection help. It has to be run with the +``embark-assistant`` command while the pre-embark screen is displayed and shows +extended (and correct(?)) resource information for the embark rectangle as well +as normally undisplayed sites in the current embark region. It also has a site +selection tool with more options than DF's vanilla search tool. For detailed +help invoke the in game info screen. diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst new file mode 100644 index 000000000..fcab18fc7 --- /dev/null +++ b/docs/plugins/embark-tools.rst @@ -0,0 +1,12 @@ +embark-tools +============ +A collection of embark-related tools. Usage and available tools:: + + embark-tools enable/disable tool [tool]... + +:anywhere: Allows embarking anywhere (including sites, mountain-only biomes, + and oceans). Use with caution. +:mouse: Implements mouse controls (currently in the local embark region only) +:sand: Displays an indicator when sand is present in the currently-selected + area, similar to the default clay/stone indicators. +:sticky: Maintains the selected local area while navigating the world map diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst new file mode 100644 index 000000000..65d2a3d45 --- /dev/null +++ b/docs/plugins/fastdwarf.rst @@ -0,0 +1,14 @@ +fastdwarf +========= +Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly +and perform tasks quickly. Teledwarf makes dwarves move instantaneously, +but do jobs at the same speed. + +:fastdwarf 0: disables both (also ``0 0``) +:fastdwarf 1: enables speedydwarf and disables teledwarf (also ``1 0``) +:fastdwarf 2: sets a native debug flag in the game memory that implements an + even more aggressive version of speedydwarf. +:fastdwarf 0 1: disables speedydwarf and enables teledwarf +:fastdwarf 1 1: enables both + +See `superdwarf` for a per-creature version. diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst new file mode 100644 index 000000000..6a9c57c9f --- /dev/null +++ b/docs/plugins/filltraffic.rst @@ -0,0 +1,17 @@ +filltraffic +=========== +Set traffic designations using flood-fill starting at the cursor. +See also `alltraffic`, `restrictice`, and `restrictliquids`. Options: + +:H: High Traffic +:N: Normal Traffic +:L: Low Traffic +:R: Restricted Traffic +:X: Fill across z-levels. +:B: Include buildings and stockpiles. +:P: Include empty space. + +Example: + +``filltraffic H`` + When used in a room with doors, it will set traffic to HIGH in just that room. diff --git a/docs/plugins/fix-armory.rst b/docs/plugins/fix-armory.rst new file mode 100644 index 000000000..f4b988420 --- /dev/null +++ b/docs/plugins/fix-armory.rst @@ -0,0 +1,4 @@ +fix-armory +========== +`This plugin requires a binpatch `, which has not +been available since DF 0.34.11 diff --git a/docs/plugins/fix-job-postings.rst b/docs/plugins/fix-job-postings.rst new file mode 100644 index 000000000..198dcd61d --- /dev/null +++ b/docs/plugins/fix-job-postings.rst @@ -0,0 +1,5 @@ +fix-job-postings +---------------- +This command fixes crashes caused by previous versions of workflow, mostly in +DFHack 0.40.24-r4, and should be run automatically when loading a world (but can +also be run manually if desired). diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst new file mode 100644 index 000000000..bf999c8a3 --- /dev/null +++ b/docs/plugins/fix-unit-occupancy.rst @@ -0,0 +1,12 @@ +fix-unit-occupancy +================== +This plugin fixes issues with unit occupancy, notably phantom +"unit blocking tile" messages (:bug:`3499`). It can be run manually, or +periodically when enabled with the built-in enable/disable commands: + +:(no argument): Run the plugin once immediately, for the whole map. +:-h, here, cursor: Run immediately, only operate on the tile at the cursor +:-n, dry, dry-run: Run immediately, do not write changes to map +:interval : Run the plugin every ``X`` ticks (when enabled). + The default is 1200 ticks, or 1 day. + Ticks are only counted when the game is unpaused. diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst new file mode 100644 index 000000000..58678d154 --- /dev/null +++ b/docs/plugins/fixveins.rst @@ -0,0 +1,5 @@ +fixveins +======== +Removes invalid references to mineral inclusions and restores missing ones. +Use this if you broke your embark with tools like `tiletypes`, or if you +accidentally placed a construction on top of a valuable mineral floor. diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst new file mode 100644 index 000000000..3df451195 --- /dev/null +++ b/docs/plugins/flows.rst @@ -0,0 +1,5 @@ +flows +===== +A tool for checking how many tiles contain flowing liquids. If you suspect that +your magma sea leaks into HFS, you can use this tool to be sure without +revealing the map. diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst new file mode 100644 index 000000000..a91c14f73 --- /dev/null +++ b/docs/plugins/follow.rst @@ -0,0 +1,5 @@ +follow +====== +Makes the game view follow the currently highlighted unit after you exit from the +current menu or cursor mode. Handy for watching dwarves running around. Deactivated +by moving the view manually. diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst new file mode 100644 index 000000000..2278bca22 --- /dev/null +++ b/docs/plugins/forceequip.rst @@ -0,0 +1,7 @@ +forceequip +========== +Forceequip moves local items into a unit's inventory. It is typically used to +equip specific clothing/armor items onto a dwarf, but can also be used to put +armor onto a war animal or to add unusual items (such as crowns) to any unit. + +For more information run ``forceequip help``. See also `modtools/equip-item`. diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst new file mode 100644 index 000000000..bb486a4ee --- /dev/null +++ b/docs/plugins/generated-creature-renamer.rst @@ -0,0 +1,15 @@ +generated-creature-renamer +========================== +Automatically renames generated creatures, such as forgotten beasts, titans, +etc, to have raw token names that match the description given in-game. + +The ``list-generated`` command can be used to list the token names of all +generated creatures in a given save, with an optional ``detailed`` argument +to show the accompanying description. + +The ``save-generated-raws`` command will save a sample creature graphics file in +the Dwarf Fortress root directory, to use as a start for making a graphics set +for generated creatures using the new names that they get with this plugin. + +The new names are saved with the save, and the plugin, when enabled, only runs once +per save, unless there's an update. diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst new file mode 100644 index 000000000..5a719b3ab --- /dev/null +++ b/docs/plugins/getplants.rst @@ -0,0 +1,37 @@ +getplants +========= +This tool allows plant gathering and tree cutting by RAW ID. Specify the types +of trees to cut down and/or shrubs to gather by their plant names, separated +by spaces. + +Options: + +:``-t``: Tree: Select trees only (exclude shrubs) +:``-s``: Shrub: Select shrubs only (exclude trees) +:``-f``: Farming: Designate only shrubs that yield seeds for farming. Implies -s +:``-c``: Clear: Clear designations instead of setting them +:``-x``: eXcept: Apply selected action to all plants except those specified (invert + selection) +:``-a``: All: Select every type of plant (obeys ``-t``/``-s``/``-f``) +:``-v``: Verbose: Lists the number of (un)designations per plant +:``-n *``: Number: Designate up to * (an integer number) plants of each species + +Specifying both ``-t`` and ``-s`` or ``-f`` will have no effect. If no plant IDs are +specified, all valid plant IDs will be listed, with ``-t``, ``-s``, and ``-f`` +restricting the list to trees, shrubs, and farmable shrubs, respectively. + +.. note:: + + DF is capable of determining that a shrub has already been picked, leaving + an unusable structure part behind. This plugin does not perform such a check + (as the location of the required information has not yet been identified). + This leads to some shrubs being designated when they shouldn't be, causing a + plant gatherer to walk there and do nothing (except clearing the + designation). See :issue:`1479` for details. + + The implementation another known deficiency: it's incapable of detecting that + raw definitions that specify a seed extraction reaction for the structural part + but has no other use for it cannot actually yield any seeds, as the part is + never used (parts of :bug:`6940`, e.g. Red Spinach), even though DF + collects it, unless there's a workshop reaction to do it (which there isn't + in vanilla). diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst new file mode 100644 index 000000000..57c3b3af5 --- /dev/null +++ b/docs/plugins/hotkeys.rst @@ -0,0 +1,8 @@ +hotkeys +======= +Opens an in-game screen showing which DFHack keybindings are +active in the current context. See also `hotkey-notes`. + +.. image:: ../images/hotkeys.png + +:dfhack-keybind:`hotkeys` diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst new file mode 100644 index 000000000..328ee83bd --- /dev/null +++ b/docs/plugins/infiniteSky.rst @@ -0,0 +1,16 @@ +infiniteSky +=========== +Automatically allocates new z-levels of sky at the top of the map as you build up, +or on request allocates many levels all at once. + +Usage: + +``infiniteSky n`` + Raise the sky by n z-levels. +``infiniteSky enable/disable`` + Enables/disables monitoring of constructions. If you build anything in the second to highest z-level, it will allocate one more sky level. This is so you can continue to build stairs upward. + +.. warning:: + + :issue:`Sometimes <254>` new z-levels disappear and cause cave-ins. + Saving and loading after creating new z-levels should fix the problem. diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst new file mode 100644 index 000000000..7bacc2b57 --- /dev/null +++ b/docs/plugins/isoworldremote.rst @@ -0,0 +1,3 @@ +isoworldremote +============== +A plugin that implements a `remote API ` used by Isoworld. diff --git a/docs/plugins/job-duplicate.rst b/docs/plugins/job-duplicate.rst new file mode 100644 index 000000000..a18d73df3 --- /dev/null +++ b/docs/plugins/job-duplicate.rst @@ -0,0 +1,6 @@ +job-duplicate +============= +In :kbd:`q` mode, when a job is highlighted within a workshop or furnace +building, calling ``job-duplicate`` instantly duplicates the job. + +:dfhack-keybind:`job-duplicate` diff --git a/docs/plugins/job-material.rst b/docs/plugins/job-material.rst new file mode 100644 index 000000000..f1b37fc1d --- /dev/null +++ b/docs/plugins/job-material.rst @@ -0,0 +1,15 @@ +job-material +============ +Alter the material of the selected job. Similar to ``job item-material ...`` + +Invoked as:: + + job-material + +:dfhack-keybind:`job-material` + +* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, + changes the material of the job. Only inorganic materials can be used + in this mode. +* In :kbd:`b` mode, during selection of building components positions the cursor + over the first available choice with the matching material. diff --git a/docs/plugins/job.rst b/docs/plugins/job.rst new file mode 100644 index 000000000..26f163864 --- /dev/null +++ b/docs/plugins/job.rst @@ -0,0 +1,15 @@ +job +=== +Command for general job query and manipulation. + +Options: + +*no extra options* + Print details of the current job. The job can be selected + in a workshop, or the unit/jobs screen. +**list** + Print details of all jobs in the selected workshop. +**item-material ** + Replace the exact material id in the job item. +**item-type ** + Replace the exact item type id in the job item. diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst new file mode 100644 index 000000000..d16a10e33 --- /dev/null +++ b/docs/plugins/labormanager.rst @@ -0,0 +1,105 @@ +labormanager +============ +Automatically manage dwarf labors to efficiently complete jobs. +Labormanager is derived from autolabor (above) but uses a completely +different approach to assigning jobs to dwarves. While autolabor tries +to keep as many dwarves busy as possible, labormanager instead strives +to get jobs done as quickly as possible. + +Labormanager frequently scans the current job list, current list of +dwarfs, and the map to determine how many dwarves need to be assigned to +what labors in order to meet all current labor needs without starving +any particular type of job. + +.. warning:: + + *As with autolabor, labormanager will override any manual changes you + make to labors while it is enabled, including through other tools such + as Dwarf Therapist* + +Simple usage: + +:enable labormanager: Enables the plugin with default settings. + (Persistent per fortress) + +:disable labormanager: Disables the plugin. + +Anything beyond this is optional - labormanager works fairly well on the +default settings. + +The default priorities for each labor vary (some labors are higher +priority by default than others). The way the plugin works is that, once +it determines how many of each labor is needed, it then sorts them by +adjusted priority. (Labors other than hauling have a bias added to them +based on how long it's been since they were last used, to prevent job +starvation.) The labor with the highest priority is selected, the "best +fit" dwarf for that labor is assigned to that labor, and then its +priority is *halved*. This process is repeated until either dwarfs or +labors run out. + +Because there is no easy way to detect how many haulers are actually +needed at any moment, the plugin always ensures that at least one dwarf +is assigned to each of the hauling labors, even if no hauling jobs are +detected. At least one dwarf is always assigned to construction removing +and cleaning because these jobs also cannot be easily detected. Lever +pulling is always assigned to everyone. Any dwarfs for which there are +no jobs will be assigned hauling, lever pulling, and cleaning labors. If +you use animal trainers, note that labormanager will misbehave if you +assign specific trainers to specific animals; results are only guaranteed +if you use "any trainer", and animal trainers will probably be +overallocated in any case. + +Labormanager also sometimes assigns extra labors to currently busy +dwarfs so that when they finish their current job, they will go off and +do something useful instead of standing around waiting for a job. + +There is special handling to ensure that at least one dwarf is assigned +to haul food whenever food is detected left in a place where it will rot +if not stored. This will cause a dwarf to go idle if you have no +storepiles to haul food to. + +Dwarfs who are unable to work (child, in the military, wounded, +handless, asleep, in a meeting) are entirely excluded from labor +assignment. Any dwarf explicitly assigned to a burrow will also be +completely ignored by labormanager. + +The fitness algorithm for assigning jobs to dwarfs generally attempts to +favor dwarfs who are more skilled over those who are less skilled. It +also tries to avoid assigning female dwarfs with children to jobs that +are "outside", favors assigning "outside" jobs to dwarfs who are +carrying a tool that could be used as a weapon, and tries to minimize +how often dwarfs have to reequip. + +Labormanager automatically determines medical needs and reserves health +care providers as needed. Note that this may cause idling if you have +injured dwarfs but no or inadequate hospital facilities. + +Hunting is never assigned without a butchery, and fishing is never +assigned without a fishery, and neither of these labors is assigned +unless specifically enabled. + +The method by which labormanager determines what labor is needed for a +particular job is complicated and, in places, incomplete. In some +situations, labormanager will detect that it cannot determine what labor +is required. It will, by default, pause and print an error message on +the dfhack console, followed by the message "LABORMANAGER: Game paused +so you can investigate the above message.". If this happens, please open +an issue on github, reporting the lines that immediately preceded this +message. You can tell labormanager to ignore this error and carry on by +typing ``labormanager pause-on-error no``, but be warned that some job may go +undone in this situation. + +Advanced usage: + +:labormanager enable: Turn plugin on. +:labormanager disable: Turn plugin off. +:labormanager priority : Set the priority value (see above) for labor to . +:labormanager reset : Reset the priority value of labor to its default. +:labormanager reset-all: Reset all priority values to their defaults. +:labormanager allow-fishing: Allow dwarfs to fish. *Warning* This tends to result in most of the fort going fishing. +:labormanager forbid-fishing: Forbid dwarfs from fishing. Default behavior. +:labormanager allow-hunting: Allow dwarfs to hunt. *Warning* This tends to result in as many dwarfs going hunting as you have crossbows. +:labormanager forbid-hunting: Forbid dwarfs from hunting. Default behavior. +:labormanager list: Show current priorities and current allocation stats. +:labormanager pause-on-error yes: Make labormanager pause if the labor inference engine fails. See above. +:labormanager pause-on-error no: Allow labormanager to continue past a labor inference engine failure. diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst new file mode 100644 index 000000000..b80e71e3a --- /dev/null +++ b/docs/plugins/lair.rst @@ -0,0 +1,12 @@ +lair +==== +This command allows you to mark the map as a monster lair, preventing item +scatter on abandon. When invoked as ``lair reset``, it does the opposite. + +Unlike `reveal`, this command doesn't save the information about tiles - you +won't be able to restore state of real monster lairs using ``lair reset``. + +Options: + +:lair: Mark the map as monster lair +:lair reset: Mark the map as ordinary (not lair) diff --git a/docs/plugins/liquids-here.rst b/docs/plugins/liquids-here.rst new file mode 100644 index 000000000..d838170a2 --- /dev/null +++ b/docs/plugins/liquids-here.rst @@ -0,0 +1,6 @@ +liquids-here +------------ +Run the liquid spawner with the current/last settings made in liquids (if no +settings in liquids were made it paints a point of 7/7 magma by default). + +Intended to be used as keybinding. Requires an active in-game cursor. diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst new file mode 100644 index 000000000..d55962ad5 --- /dev/null +++ b/docs/plugins/liquids.rst @@ -0,0 +1,65 @@ +liquids +======= +Allows adding magma, water and obsidian to the game. It replaces the normal +dfhack command line and can't be used from a hotkey. Settings will be remembered +as long as dfhack runs. Intended for use in combination with the command +``liquids-here`` (which can be bound to a hotkey). See also :issue:`80`. + +.. warning:: + + Spawning and deleting liquids can mess up pathing data and + temperatures (creating heat traps). You've been warned. + +.. note:: + + `gui/liquids` is an in-game UI for this script. + +Settings will be remembered until you quit DF. You can call `liquids-here` to execute +the last configured action, which is useful in combination with keybindings. + +Usage: point the DF cursor at a tile you want to modify and use the commands. + +If you only want to add or remove water or magma from one tile, +`source` may be easier to use. + +Commands +-------- +Misc commands: + +:q: quit +:help, ?: print this list of commands +:: put liquid + +Modes: + +:m: switch to magma +:w: switch to water +:o: make obsidian wall instead +:of: make obsidian floors +:rs: make a river source +:f: flow bits only +:wclean: remove salt and stagnant flags from tiles + +Set-Modes and flow properties (only for magma/water): + +:s+: only add mode +:s.: set mode +:s-: only remove mode +:f+: make the spawned liquid flow +:f.: don't change flow state (read state in flow mode) +:f-: make the spawned liquid static + +Permaflow (only for water): + +:pf.: don't change permaflow state +:pf-: make the spawned liquid static +:pf[NS][EW]: make the spawned liquid permanently flow +:0-7: set liquid amount + +Brush size and shape: + +:p, point: Single tile +:r, range: Block with cursor at bottom north-west (any place, any size) +:block: DF map block with cursor in it (regular spaced 16x16x1 blocks) +:column: Column from cursor, up through free space +:flood: Flood-fill water tiles from cursor (only makes sense with wclean) diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst new file mode 100644 index 000000000..024438d85 --- /dev/null +++ b/docs/plugins/manipulator.rst @@ -0,0 +1,182 @@ +manipulator +=========== +An in-game equivalent to the popular program Dwarf Therapist. + +To activate, open the unit screen and press :kbd:`l`. + +.. image:: ../images/manipulator.png + +The far left column displays the unit's Happiness (color-coded based on its +value), Name, Profession/Squad, and the right half of the screen displays each +dwarf's labor settings and skill levels (0-9 for Dabbling through Professional, +A-E for Great through Grand Master, and U-Z for Legendary through Legendary+5). + +Cells with teal backgrounds denote skills not controlled by labors, e.g. +military and social skills. + +.. image:: ../images/manipulator2.png + +Press :kbd:`t` to toggle between Profession, Squad, and Job views. + +.. image:: ../images/manipulator3.png + +Use the arrow keys or number pad to move the cursor around, holding :kbd:`Shift` to +move 10 tiles at a time. + +Press the Z-Up (:kbd:`<`) and Z-Down (:kbd:`>`) keys to move quickly between labor/skill +categories. The numpad Z-Up and Z-Down keys seek to the first or last unit +in the list. :kbd:`Backspace` seeks to the top left corner. + +Press Enter to toggle the selected labor for the selected unit, or Shift+Enter +to toggle all labors within the selected category. + +Press the :kbd:`+`:kbd:`-` keys to sort the unit list according to the currently selected +skill/labor, and press the :kbd:`*`:kbd:`/` keys to sort the unit list by Name, Profession/Squad, +Happiness, or Arrival order (using :kbd:`Tab` to select which sort method to use here). + +With a unit selected, you can press the :kbd:`v` key to view its properties (and +possibly set a custom nickname or profession) or the :kbd:`c` key to exit +Manipulator and zoom to its position within your fortress. + +The following mouse shortcuts are also available: + +* Click on a column header to sort the unit list. Left-click to sort it in one + direction (descending for happiness or labors/skills, ascending for name, + profession or squad) and right-click to sort it in the opposite direction. +* Left-click on a labor cell to toggle that labor. Right-click to move the + cursor onto that cell instead of toggling it. +* Left-click on a unit's name, profession or squad to view its properties. +* Right-click on a unit's name, profession or squad to zoom to it. + +Pressing :kbd:`Esc` normally returns to the unit screen, but :kbd:`Shift`:kbd:`Esc` would exit +directly to the main dwarf mode screen. + +Professions +----------- + +The manipulator plugin supports saving professions: a named set of labors that can be +quickly applied to one or multiple dwarves. + +To save a profession, highlight a dwarf and press :kbd:`P`. The profession will be saved using +the custom profession name of the dwarf, or the default for that dwarf if no custom profession +name has been set. + +To apply a profession, either highlight a single dwarf or select multiple with +:kbd:`x`, and press :kbd:`p` to select the profession to apply. All labors for +the selected dwarves will be reset to the labors of the chosen profession. + +Professions are saved as human-readable text files in the +``dfhack-config/professions`` folder within the DF folder, and can be edited or +deleted there. + +The professions library +~~~~~~~~~~~~~~~~~~~~~~~ + +The manipulator plugin comes with a library of professions that you can assign +to your dwarves. + +If you'd rather use Dwarf Therapist to manage your labors, it is easy to import +these professions to DT and use them there. Simply assign the professions you +want to import to a dwarf. Once you have assigned a profession to at least one +dwarf, you can select "Import Professions from DF" in the DT "File" menu. The +professions will then be available for use in DT. + +In the charts below, the "At Start" and "Max" columns indicate the approximate +number of dwarves of each profession that you are likely to need at the start of +the game and how many you are likely to need in a mature fort. These are just +approximations. Your playstyle may demand more or fewer of each profession. + +============= ======== ===== ================================================= +Profession At Start Max Description +============= ======== ===== ================================================= +Chef 0 3 Buchery, Tanning, and Cooking. It is important to + focus just a few dwarves on cooking since + well-crafted meals make dwarves very happy. They + are also an excellent trade good. +Craftsdwarf 0 4-6 All labors used at Craftsdwarf's workshops, + Glassmaker's workshops, and kilns. +Doctor 0 2-4 The full suite of medical labors, plus Animal + Caretaking for those using the dwarfvet plugin. +Farmer 1 4 Food- and animal product-related labors. This + profession also has the ``Alchemist`` labor + enabled since they need to focus on food-related + jobs, though you might want to disable + ``Alchemist`` for your first farmer until there + are actual farming duties to perform. +Fisherdwarf 0 0-1 Fishing and fish cleaning. If you assign this + profession to any dwarf, be prepared to be + inundated with fish. Fisherdwarves *never stop + fishing*. Be sure to also run ``prioritize -a + PrepareRawFish ExtractFromRawFish`` or else + caught fish will just be left to rot. +Hauler 0 >20 All hauling labors plus Siege Operating, Mechanic + (so haulers can assist in reloading traps) and + Architecture (so haulers can help build massive + windmill farms and pump stacks). As you + accumulate enough Haulers, you can turn off + hauling labors for other dwarves so they can + focus on their skilled tasks. You may also want + to restrict your Mechanic's workshops to only + skilled mechanics so your haulers don't make + low-quality mechanisms. +Laborer 0 10-12 All labors that don't improve quality with skill, + such as Soapmaking and furnace labors. +Marksdwarf 0 10-30 Similar to Hauler. See the description for + Meleedwarf below for more details. +Mason 2 2-4 Masonry and Gem Cutting/Encrusting. In the early + game, you may need to run "`prioritize` + ConstructBuilding" to get your masons to build + wells and bridges if they are too busy crafting + stone furniture. +Meleedwarf 0 20-50 Similar to Hauler, but without most civilian + labors. This profession is separate from Hauler + so you can find your military dwarves easily. + Meleedwarves and Marksdwarves have Mechanics and + hauling labors enabled so you can temporarily + deactivate your military after sieges and allow + your military dwarves to help clean up. +Migrant 0 0 You can assign this profession to new migrants + temporarily while you sort them into professions. + Like Marksdwarf and Meleedwarf, the purpose of + this profession is so you can find your new + dwarves more easily. +Miner 2 2-10 Mining and Engraving. This profession also has + the ``Alchemist`` labor enabled, which disables + hauling for those using the `autohauler` plugin. + Once the need for Miners tapers off in the late + game, dwarves with this profession make good + military dwarves, wielding their picks as + weapons. +Outdoorsdwarf 1 2-4 Carpentry, Bowyery, Woodcutting, Animal Training, + Trapping, Plant Gathering, Beekeeping, and Siege + Engineering. +Smith 0 2-4 Smithing labors. You may want to specialize your + Smiths to focus on a single smithing skill to + maximize equipment quality. +StartManager 1 0 All skills not covered by the other starting + professions (Miner, Mason, Outdoorsdwarf, and + Farmer), plus a few overlapping skills to + assist in critical tasks at the beginning of the + game. Individual labors should be turned off as + migrants are assigned more specialized + professions that cover them, and the StartManager + dwarf can eventually convert to some other + profession. +Tailor 0 2 Textile industry labors: Dying, Leatherworking, + Weaving, and Clothesmaking. +============= ======== ===== ================================================= + +A note on autohauler +~~~~~~~~~~~~~~~~~~~~ + +These profession definitions are designed to work well with or without the +`autohauler` plugin (which helps to keep your dwarves focused on skilled labors +instead of constantly being distracted by hauling). If you do want to use +autohauler, adding the following lines to your ``onMapLoad.init`` file will +configure it to let the professions manage the "Feed water to civilians" and +"Recover wounded" labors instead of enabling those labors for all hauling +dwarves:: + + on-new-fortress enable autohauler + on-new-fortress autohauler FEED_WATER_CIVILIANS allow + on-new-fortress autohauler RECOVER_WOUNDED allow diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst new file mode 100644 index 000000000..1c146a10f --- /dev/null +++ b/docs/plugins/misery.rst @@ -0,0 +1,14 @@ +misery +====== +When enabled, fake bad thoughts will be added to all dwarves. + +Usage: + +:misery enable n: enable misery with optional magnitude n. If specified, n must + be positive. +:misery n: same as "misery enable n" +:misery enable: same as "misery enable 1" +:misery disable: stop adding new negative thoughts. This will not remove + existing negative thoughts. Equivalent to "misery 0". +:misery clear: remove fake thoughts, even after saving and reloading. Does + not change factor. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst new file mode 100644 index 000000000..9b3fc6984 --- /dev/null +++ b/docs/plugins/mode.rst @@ -0,0 +1,21 @@ +mode +==== +This command lets you see and change the game mode directly. + +.. warning:: + + Only use ``mode`` after making a backup of your save! + + Not all combinations are good for every situation and most of them will + produce undesirable results. There are a few good ones though. + +Examples: + + * You are in fort game mode, managing your fortress and paused. + * You switch to the arena game mode, *assume control of a creature* and then + * switch to adventure game mode(1). + You just lost a fortress and gained an adventurer. Alternatively: + + * You are in fort game mode, managing your fortress and paused at the esc menu. + * You switch to the adventure game mode, assume control of a creature, then save or retire. + * You just created a returnable mountain home and gained an adventurer. diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst new file mode 100644 index 000000000..2f613acaa --- /dev/null +++ b/docs/plugins/mousequery.rst @@ -0,0 +1,16 @@ +mousequery +========== +Adds mouse controls to the DF interface, e.g. click-and-drag designations. + +Options: + +:plugin: enable/disable the entire plugin +:rbutton: enable/disable right mouse button +:track: enable/disable moving cursor in build and designation mode +:edge: enable/disable active edge scrolling (when on, will also enable tracking) +:live: enable/disable query view when unpaused +:delay: Set delay when edge scrolling in tracking mode. Omit amount to display current setting. + +Usage:: + + mousequery [plugin] [rbutton] [track] [edge] [live] [enable|disable] diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst new file mode 100644 index 000000000..2b633f31b --- /dev/null +++ b/docs/plugins/nestboxes.rst @@ -0,0 +1,5 @@ +nestboxes +========= + +Automatically scan for and forbid fertile eggs incubating in a nestbox. +Toggle status with `enable` or `disable `. diff --git a/docs/plugins/nopause.rst b/docs/plugins/nopause.rst new file mode 100644 index 000000000..c30543187 --- /dev/null +++ b/docs/plugins/nopause.rst @@ -0,0 +1,4 @@ +nopause +======= +Disables pausing (both manual and automatic) with the exception of pause forced +by `reveal` ``hell``. This is nice for digging under rivers. diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst new file mode 100644 index 000000000..eb0e8a7ca --- /dev/null +++ b/docs/plugins/orders.rst @@ -0,0 +1,116 @@ +orders +====== + +A plugin for manipulating manager orders. + +Subcommands: + +:list: Shows the list of previously exported orders, including the orders library. +:export NAME: Exports the current list of manager orders to a file named ``dfhack-config/orders/NAME.json``. +:import NAME: Imports manager orders from a file named ``dfhack-config/orders/NAME.json``. +:clear: Deletes all manager orders in the current embark. +:sort: Sorts current manager orders by repeat frequency so daily orders don't + prevent other orders from ever being completed: one-time orders first, then + yearly, seasonally, monthly, then finally daily. + +You can keep your orders automatically sorted by adding the following command to +your ``onMapLoad.init`` file:: + + repeat -name orders-sort -time 1 -timeUnits days -command [ orders sort ] + + +The orders library +------------------ + +DFHack comes with a library of useful manager orders that are ready for import: + +:source:`basic.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This collection of orders handles basic fort necessities: + +- prepared meals and food products (and by-products like oil) +- booze/mead +- thread/cloth/dye +- pots/jugs/buckets/mugs +- bags of leather, cloth, silk, and yarn +- crafts and totems from otherwise unusable by-products +- mechanisms/cages +- splints/crutches +- lye/soap +- ash/potash +- beds/wheelbarrows/minecarts +- scrolls + +You should import it as soon as you have enough dwarves to perform the tasks. +Right after the first migration wave is usually a good time. + +:source:`furnace.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This collection creates basic items that require heat. It is separated out from +``basic.json`` to give players the opportunity to set up magma furnaces first in +order to save resources. It handles: + +- charcoal (including smelting of bituminous coal and lignite) +- pearlash +- sand +- green/clear/crystal glass +- adamantine processing +- item melting + +Orders are missing for plaster powder until DF :bug:`11803` is fixed. + +:source:`military.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This collection adds high-volume smelting jobs for military-grade metal ores and +produces weapons and armor: + +- leather backpacks/waterskins/cloaks/quivers/armor +- bone/wooden bolts +- smelting for platinum, silver, steel, bronze, bismuth bronze, and copper (and + their dependencies) +- bronze/bismuth bronze/copper bolts +- platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, + with checks to ensure only the best available materials are being used + +If you set a stockpile to take weapons and armor of less than masterwork quality +and turn on `automelt` (like what `dreamfort` provides on its industry level), +these orders will automatically upgrade your military equipment to masterwork. +Make sure you have a lot of fuel (or magma forges and furnaces) before you turn +``automelt`` on, though! + +This file should only be imported, of course, if you need to equip a military. + +:source:`smelting.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This collection adds smelting jobs for all ores. It includes handling the ores +already managed by ``military.json``, but has lower limits. This ensures all +ores will be covered if a player imports ``smelting`` but not ``military``, but +the higher-volume ``military`` orders will take priority if both are imported. + +:source:`rockstock.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This collection of orders keeps a small stock of all types of rock furniture. +This allows you to do ad-hoc furnishings of guildhalls, libraries, temples, or +other rooms with `buildingplan` and your masons will make sure there is always +stock on hand to fulfill the plans. + +:source:`glassstock.json ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to ``rockstock`` above, this collection keeps a small stock of all types +of glass furniture. If you have a functioning glass industry, this is more +sustainable than ``rockstock`` since you can never run out of sand. If you have +plenty of rock and just want the variety, you can import both ``rockstock`` and +``glassstock`` to get a mixture of rock and glass furnishings in your fort. + +There are a few items that ``glassstock`` produces that ``rockstock`` does not, +since there are some items that can not be made out of rock, for example: + +- tubes and corkscrews for building magma-safe screw pumps +- windows +- terrariums (as an alternative to wooden cages) diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst new file mode 100644 index 000000000..65ebaeeab --- /dev/null +++ b/docs/plugins/petcapRemover.rst @@ -0,0 +1,19 @@ +petcapRemover +============= +Allows you to remove or raise the pet population cap. In vanilla +DF, pets will not reproduce unless the population is below 50 and the number of +children of that species is below a certain percentage. This plugin allows +removing the second restriction and removing or raising the first. Pets still +require PET or PET_EXOTIC tags in order to reproduce. Type ``help petcapRemover`` +for exact usage. In order to make population more stable and avoid sudden +population booms as you go below the raised population cap, this plugin counts +pregnancies toward the new population cap. It can still go over, but only in the +case of multiple births. + +Usage: + +:petcapRemover: cause pregnancies now and schedule the next check +:petcapRemover every n: set how often in ticks the plugin checks for possible pregnancies +:petcapRemover cap n: set the new cap to n. if n = 0, no cap +:petcapRemover pregtime n: sets the pregnancy duration to n ticks. natural pregnancies are + 300000 ticks for the current race and 200000 for everyone else diff --git a/docs/plugins/plant.rst b/docs/plugins/plant.rst new file mode 100644 index 000000000..b621b890d --- /dev/null +++ b/docs/plugins/plant.rst @@ -0,0 +1,16 @@ +plant +===== +A tool for creating shrubs, growing, or getting rid of them. + +Subcommands: + +:create: Creates a new sapling under the cursor. Takes a raw ID as argument + (e.g. TOWER_CAP). The cursor must be located on a dirt or grass floor tile. +:grow: Turns saplings into trees; under the cursor if a sapling is selected, + or every sapling on the map if the cursor is hidden. + +For mass effects, use one of the additional options: + +:shrubs: affect all shrubs on the map +:trees: affect all trees on the map +:all: affect every plant! diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst new file mode 100644 index 000000000..163ef91be --- /dev/null +++ b/docs/plugins/power-meter.rst @@ -0,0 +1,6 @@ +power-meter +=========== +The power-meter plugin implements a modified pressure plate that detects power being +supplied to gear boxes built in the four adjacent N/S/W/E tiles. + +The configuration front-end is implemented by `gui/power-meter`. diff --git a/docs/plugins/probe.rst b/docs/plugins/probe.rst new file mode 100644 index 000000000..1dcc28336 --- /dev/null +++ b/docs/plugins/probe.rst @@ -0,0 +1,13 @@ +probe +===== + +This plugin provides multiple commands that print low-level properties of the +selected objects. + +* ``probe``: prints some properties of the tile selected with :kbd:`k`. Some of + these properties can be passed into `tiletypes`. +* ``cprobe``: prints some properties of the unit selected with :kbd:`v`, as well + as the IDs of any worn items. `gui/gm-unit` and `gui/gm-editor` are more + complete in-game alternatives. +* ``bprobe``: prints some properties of the building selected with :kbd:`q` or + :kbd:`t`. `gui/gm-editor` is a more complete in-game alternative. diff --git a/docs/plugins/prospect.rst b/docs/plugins/prospect.rst new file mode 100644 index 000000000..a8979f1cc --- /dev/null +++ b/docs/plugins/prospect.rst @@ -0,0 +1,51 @@ +prospect +======== + +**Usage:** + + ``prospect [all|hell] []`` + +Shows a summary of resources that exist on the map. By default, only the visible +part of the map is scanned. Include the ``all`` keyword if you want ``prospect`` +to scan the whole map as if it were revealed. Use ``hell`` instead of ``all`` if +you also want to see the Z range of HFS tubes in the 'features' report section. + +**Options:** + +:``-h``, ``--help``: + Shows this help text. +:``-s``, ``--show ``: + Shows only the named comma-separated list of report sections. Report section + names are: summary, liquids, layers, features, ores, gems, veins, shrubs, + and trees. If run during pre-embark, only the layers, ores, gems, and veins + report sections are available. +:``-v``, ``--values``: + Includes material value in the output. Most useful for the 'gems' report + section. + +**Examples:** + +``prospect all`` + Shows the entire report for the entire map. + +``prospect hell --show layers,ores,veins`` + Shows only the layers, ores, and other vein stone report sections, and + includes information on HFS tubes when a fort is loaded. + +``prospect all -sores`` + Show only information about ores for the pre-embark or fortress map report. + +**Pre-embark estimate:** + +If prospect is called during the embark selection screen, it displays an +estimate of layer stone availability. If the ``all`` keyword is specified, it +also estimates ores, gems, and vein material. The estimate covers all tiles of +the embark rectangle. + +.. note:: + + The results of pre-embark prospect are an *estimate*, and can at best be + expected to be somewhere within +/- 30% of the true amount; sometimes it + does a lot worse. Especially, it is not clear how to precisely compute how + many soil layers there will be in a given embark tile, so it can report a + whole extra layer, or omit one that is actually present. diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst new file mode 100644 index 000000000..a8979f1cc --- /dev/null +++ b/docs/plugins/prospector.rst @@ -0,0 +1,51 @@ +prospect +======== + +**Usage:** + + ``prospect [all|hell] []`` + +Shows a summary of resources that exist on the map. By default, only the visible +part of the map is scanned. Include the ``all`` keyword if you want ``prospect`` +to scan the whole map as if it were revealed. Use ``hell`` instead of ``all`` if +you also want to see the Z range of HFS tubes in the 'features' report section. + +**Options:** + +:``-h``, ``--help``: + Shows this help text. +:``-s``, ``--show ``: + Shows only the named comma-separated list of report sections. Report section + names are: summary, liquids, layers, features, ores, gems, veins, shrubs, + and trees. If run during pre-embark, only the layers, ores, gems, and veins + report sections are available. +:``-v``, ``--values``: + Includes material value in the output. Most useful for the 'gems' report + section. + +**Examples:** + +``prospect all`` + Shows the entire report for the entire map. + +``prospect hell --show layers,ores,veins`` + Shows only the layers, ores, and other vein stone report sections, and + includes information on HFS tubes when a fort is loaded. + +``prospect all -sores`` + Show only information about ores for the pre-embark or fortress map report. + +**Pre-embark estimate:** + +If prospect is called during the embark selection screen, it displays an +estimate of layer stone availability. If the ``all`` keyword is specified, it +also estimates ores, gems, and vein material. The estimate covers all tiles of +the embark rectangle. + +.. note:: + + The results of pre-embark prospect are an *estimate*, and can at best be + expected to be somewhere within +/- 30% of the true amount; sometimes it + does a lot worse. Especially, it is not clear how to precisely compute how + many soil layers there will be in a given embark tile, so it can report a + whole extra layer, or omit one that is actually present. diff --git a/docs/plugins/rb.rst b/docs/plugins/rb.rst new file mode 100644 index 000000000..359d6149b --- /dev/null +++ b/docs/plugins/rb.rst @@ -0,0 +1,4 @@ +ruby +==== +Ruby language plugin, which evaluates the following arguments as a ruby string. +Best used as ``:rb [string]``, for the special parsing mode. Alias ``rb_eval``. diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst new file mode 100644 index 000000000..f3d00d8e1 --- /dev/null +++ b/docs/plugins/regrass.rst @@ -0,0 +1,3 @@ +regrass +======= +Regrows all the grass. Not much to it ;) diff --git a/docs/plugins/remotefortressreader.rst b/docs/plugins/remotefortressreader.rst new file mode 100644 index 000000000..287debca8 --- /dev/null +++ b/docs/plugins/remotefortressreader.rst @@ -0,0 +1,4 @@ +remotefortressreader +==================== +An in-development plugin for realtime fortress visualisation. +See :forums:`Armok Vision <146473>`. diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst new file mode 100644 index 000000000..55534e636 --- /dev/null +++ b/docs/plugins/rename.rst @@ -0,0 +1,19 @@ +rename +====== +Allows renaming various things. Use `gui/rename` for an in-game interface. + +Options: + +``rename squad "name"`` + Rename squad by index to 'name'. +``rename hotkey \"name\"`` + Rename hotkey by index. This allows assigning + longer commands to the DF hotkeys. +``rename unit "nickname"`` + Rename a unit/creature highlighted in the DF user interface. +``rename unit-profession "custom profession"`` + Change proffession name of the highlighted unit/creature. +``rename building "name"`` + Set a custom name for the selected building. + The building must be one of stockpile, workshop, furnace, trap, + siege engine or an activity zone. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst new file mode 100644 index 000000000..a173a75db --- /dev/null +++ b/docs/plugins/rendermax.rst @@ -0,0 +1,18 @@ +rendermax +========= +A collection of renderer replacing/enhancing filters. For better effect try changing the +black color in palette to non totally black. See :forums:`128487` for more info. + +Options: + +:trippy: Randomizes the color of each tiles. Used for fun, or testing. +:light: Enable lighting engine. +:light reload: Reload the settings file. +:light sun |cycle: Set time to (in hours) or set it to df time cycle. +:occlusionON, occlusionOFF: Show debug occlusion info. +:disable: Disable any filter that is enabled. + +An image showing lava and dragon breath. Not pictured here: sunlight, shining items/plants, +materials that color the light etc... + +.. image:: ../images/rendermax.png diff --git a/docs/plugins/restrictice.rst b/docs/plugins/restrictice.rst new file mode 100644 index 000000000..558fb5679 --- /dev/null +++ b/docs/plugins/restrictice.rst @@ -0,0 +1,4 @@ +restrictice +=========== +Restrict traffic on all tiles on top of visible ice. +See also `alltraffic`, `filltraffic`, and `restrictliquids`. diff --git a/docs/plugins/restrictliquids.rst b/docs/plugins/restrictliquids.rst new file mode 100644 index 000000000..4f6cd9550 --- /dev/null +++ b/docs/plugins/restrictliquids.rst @@ -0,0 +1,4 @@ +restrictliquids +=============== +Restrict traffic on all visible tiles with liquid. +See also `alltraffic`, `filltraffic`, and `restrictice`. diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst new file mode 100644 index 000000000..ccca39e28 --- /dev/null +++ b/docs/plugins/resume.rst @@ -0,0 +1,4 @@ +resume +====== +Allows automatic resumption of suspended constructions, along with colored +UI hints for construction status. diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst new file mode 100644 index 000000000..da7be8277 --- /dev/null +++ b/docs/plugins/reveal.rst @@ -0,0 +1,23 @@ +reveal +====== +This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use ``reveal demons``. + +Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run ``unreveal``. + +Usage and related commands: + +:reveal: Reveal the whole map, except for HFS to avoid demons spawning +:reveal hell: Also show hell, but requires ``unreveal`` before unpausing +:reveal demon: Reveals everything and allows unpausing - good luck! +:unreveal: Reverts the effects of ``reveal`` +:revtoggle: Switches between ``reveal`` and ``unreveal`` +:revflood: Hide everything, then reveal tiles with a path to the cursor. + Note that tiles behind constructed walls are also revealed as a + workaround for :bug:`1871`. +:revforget: Discard info about what was visible before revealing the map. + Only useful where (e.g.) you abandoned with the fort revealed + and no longer want the data. diff --git a/docs/plugins/revflood.rst b/docs/plugins/revflood.rst new file mode 100644 index 000000000..da7be8277 --- /dev/null +++ b/docs/plugins/revflood.rst @@ -0,0 +1,23 @@ +reveal +====== +This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use ``reveal demons``. + +Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run ``unreveal``. + +Usage and related commands: + +:reveal: Reveal the whole map, except for HFS to avoid demons spawning +:reveal hell: Also show hell, but requires ``unreveal`` before unpausing +:reveal demon: Reveals everything and allows unpausing - good luck! +:unreveal: Reverts the effects of ``reveal`` +:revtoggle: Switches between ``reveal`` and ``unreveal`` +:revflood: Hide everything, then reveal tiles with a path to the cursor. + Note that tiles behind constructed walls are also revealed as a + workaround for :bug:`1871`. +:revforget: Discard info about what was visible before revealing the map. + Only useful where (e.g.) you abandoned with the fort revealed + and no longer want the data. diff --git a/docs/plugins/revforget.rst b/docs/plugins/revforget.rst new file mode 100644 index 000000000..da7be8277 --- /dev/null +++ b/docs/plugins/revforget.rst @@ -0,0 +1,23 @@ +reveal +====== +This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use ``reveal demons``. + +Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run ``unreveal``. + +Usage and related commands: + +:reveal: Reveal the whole map, except for HFS to avoid demons spawning +:reveal hell: Also show hell, but requires ``unreveal`` before unpausing +:reveal demon: Reveals everything and allows unpausing - good luck! +:unreveal: Reverts the effects of ``reveal`` +:revtoggle: Switches between ``reveal`` and ``unreveal`` +:revflood: Hide everything, then reveal tiles with a path to the cursor. + Note that tiles behind constructed walls are also revealed as a + workaround for :bug:`1871`. +:revforget: Discard info about what was visible before revealing the map. + Only useful where (e.g.) you abandoned with the fort revealed + and no longer want the data. diff --git a/docs/plugins/revtoggle.rst b/docs/plugins/revtoggle.rst new file mode 100644 index 000000000..da7be8277 --- /dev/null +++ b/docs/plugins/revtoggle.rst @@ -0,0 +1,23 @@ +reveal +====== +This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use ``reveal demons``. + +Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run ``unreveal``. + +Usage and related commands: + +:reveal: Reveal the whole map, except for HFS to avoid demons spawning +:reveal hell: Also show hell, but requires ``unreveal`` before unpausing +:reveal demon: Reveals everything and allows unpausing - good luck! +:unreveal: Reverts the effects of ``reveal`` +:revtoggle: Switches between ``reveal`` and ``unreveal`` +:revflood: Hide everything, then reveal tiles with a path to the cursor. + Note that tiles behind constructed walls are also revealed as a + workaround for :bug:`1871`. +:revforget: Discard info about what was visible before revealing the map. + Only useful where (e.g.) you abandoned with the fort revealed + and no longer want the data. diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst new file mode 100644 index 000000000..359d6149b --- /dev/null +++ b/docs/plugins/ruby.rst @@ -0,0 +1,4 @@ +ruby +==== +Ruby language plugin, which evaluates the following arguments as a ruby string. +Best used as ``:rb [string]``, for the special parsing mode. Alias ``rb_eval``. diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst new file mode 100644 index 000000000..57b9e5df4 --- /dev/null +++ b/docs/plugins/search.rst @@ -0,0 +1,40 @@ + +.. _search-plugin: + +search +====== +The search plugin adds search to the Stocks, Animals, Trading, Stockpile, +Noble (assignment candidates), Military (position candidates), Burrows +(unit list), Rooms, Announcements, Job List and Unit List screens. + +.. image:: ../images/search.png + +Searching works the same way as the search option in :guilabel:`Move to Depot`. +You will see the Search option displayed on screen with a hotkey (usually :kbd:`s`). +Pressing it lets you start typing a query and the relevant list will start +filtering automatically. + +Pressing :kbd:`Enter`, :kbd:`Esc` or the arrow keys will return you to browsing the now +filtered list, which still functions as normal. You can clear the filter +by either going back into search mode and backspacing to delete it, or +pressing the "shifted" version of the search hotkey while browsing the +list (e.g. if the hotkey is :kbd:`s`, then hitting :kbd:`Shift`:kbd:`s` will clear any +filter). + +Leaving any screen automatically clears the filter. + +In the Trade screen, the actual trade will always only act on items that +are actually visible in the list; the same effect applies to the Trade +Value numbers displayed by the screen. Because of this, the :kbd:`t` key is +blocked while search is active, so you have to reset the filters first. +Pressing :kbd:`Alt`:kbd:`C` will clear both search strings. + +In the stockpile screen the option only appears if the cursor is in the +rightmost list: + +.. image:: ../images/search-stockpile.png + +Note that the 'Permit XXX'/'Forbid XXX' keys conveniently operate only +on items actually shown in the rightmost list, so it is possible to select +only fat or tallow by forbidding fats, then searching for fat/tallow, and +using Permit Fats again while the list is filtered. diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst new file mode 100644 index 000000000..1d5ab9e62 --- /dev/null +++ b/docs/plugins/seedwatch.rst @@ -0,0 +1,27 @@ +seedwatch +========= +Watches the numbers of seeds available and enables/disables seed and plant cooking. + +Each plant type can be assigned a limit. If their number falls below that limit, +the plants and seeds of that type will be excluded from cookery. +If the number rises above the limit + 20, then cooking will be allowed. + +The plugin needs a fortress to be loaded and will deactivate automatically otherwise. +You have to reactivate with 'seedwatch start' after you load the game. + +Options: + +:all: Adds all plants from the abbreviation list to the watch list. +:start: Start watching. +:stop: Stop watching. +:info: Display whether seedwatch is watching, and the watch list. +:clear: Clears the watch list. + +Examples: + +``seedwatch MUSHROOM_HELMET_PLUMP 30`` + add ``MUSHROOM_HELMET_PLUMP`` to the watch list, limit = 30 +``seedwatch MUSHROOM_HELMET_PLUMP`` + removes ``MUSHROOM_HELMET_PLUMP`` from the watch list. +``seedwatch all 30`` + adds all plants from the abbreviation list to the watch list, the limit being 30. diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst new file mode 100644 index 000000000..9276766c4 --- /dev/null +++ b/docs/plugins/showmood.rst @@ -0,0 +1,3 @@ +showmood +======== +Shows all items needed for the currently active strange mood. diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst new file mode 100644 index 000000000..0e9511d3d --- /dev/null +++ b/docs/plugins/siege-engine.rst @@ -0,0 +1,12 @@ +siege-engine +============ +Siege engines in DF haven't been updated since the game was 2D, and can +only aim in four directions. To make them useful above-ground, +this plugin allows you to: + +* link siege engines to stockpiles +* restrict operator skill levels (like workshops) +* load any object into a catapult, not just stones +* aim at a rectangular area in any direction, and across Z-levels + +The front-end is implemented by `gui/siege-engine`. diff --git a/docs/plugins/sort-items.rst b/docs/plugins/sort-items.rst new file mode 100644 index 000000000..d18f33c4e --- /dev/null +++ b/docs/plugins/sort-items.rst @@ -0,0 +1,17 @@ +.. _sort: + +sort-items +========== +Sort the visible item list:: + + sort-items order [order...] + +Sort the item list using the given sequence of comparisons. +The ``<`` prefix for an order makes undefined values sort first. +The ``>`` prefix reverses the sort order for defined values. + +Item order examples:: + + description material wear type quality + +The orderings are defined in ``hack/lua/plugins/sort/*.lua`` diff --git a/docs/plugins/sort-units.rst b/docs/plugins/sort-units.rst new file mode 100644 index 000000000..1a88381ce --- /dev/null +++ b/docs/plugins/sort-units.rst @@ -0,0 +1,17 @@ +sort-units +========== +Sort the visible unit list:: + + sort-units order [order...] + +Sort the unit list using the given sequence of comparisons. +The ``<`` prefix for an order makes undefined values sort first. +The ``>`` prefix reverses the sort order for defined values. + +Unit order examples:: + + name age arrival squad squad_position profession + +The orderings are defined in ``hack/lua/plugins/sort/*.lua`` + +:dfhack-keybind:`sort-units` diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst new file mode 100644 index 000000000..6c2ca9e24 --- /dev/null +++ b/docs/plugins/spectate.rst @@ -0,0 +1,5 @@ +spectate +======== +Simple plugin to automate following random dwarves. Most of the time things will +be weighted towards z-levels with the highest job activity. Simply enter the +``spectate`` command to toggle the plugin's state. diff --git a/docs/plugins/spotclean.rst b/docs/plugins/spotclean.rst new file mode 100644 index 000000000..9ccdd2cc6 --- /dev/null +++ b/docs/plugins/spotclean.rst @@ -0,0 +1,6 @@ +spotclean +========= +Works like ``clean map snow mud``, but only for the tile under the cursor. Ideal +if you want to keep that bloody entrance ``clean map`` would clean up. + +:dfhack-keybind:`spotclean` diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst new file mode 100644 index 000000000..416ac9ad1 --- /dev/null +++ b/docs/plugins/steam-engine.rst @@ -0,0 +1,84 @@ +steam-engine +============ +The steam-engine plugin detects custom workshops with STEAM_ENGINE in +their token, and turns them into real steam engines. + +The vanilla game contains only water wheels and windmills as sources of +power, but windmills give relatively little power, and water wheels require +flowing water, which must either be a real river and thus immovable and +limited in supply, or actually flowing and thus laggy. + +Compared to the :wiki:`water reactor ` +exploit, steam engines make a lot of sense! + +Construction +------------ +The workshop needs water as its input, which it takes via a +passable floor tile below it, like usual magma workshops do. +The magma version also needs magma. + +Due to DFHack limits, the workshop will collapse over true open space. +However down stairs are passable but support machines, so you can use them. + +After constructing the building itself, machines can be connected +to the edge tiles that look like gear boxes. Their exact position +is extracted from the workshop raws. + +Like with collapse above, due to DFHack limits the workshop +can only immediately connect to machine components built AFTER it. +This also means that engines cannot be chained without intermediate +axles built after both engines. + +Operation +--------- +In order to operate the engine, queue the Stoke Boiler job (optionally +on repeat). A furnace operator will come, possibly bringing a bar of fuel, +and perform it. As a result, a "boiling water" item will appear +in the :kbd:`t` view of the workshop. + +.. note:: + + The completion of the job will actually consume one unit + of the appropriate liquids from below the workshop. This means + that you cannot just raise 7 units of magma with a piston and + have infinite power. However, liquid consumption should be slow + enough that water can be supplied by a pond zone bucket chain. + +Every such item gives 100 power, up to a limit of 300 for coal, +and 500 for a magma engine. The building can host twice that +amount of items to provide longer autonomous running. When the +boiler gets filled to capacity, all queued jobs are suspended; +once it drops back to 3+1 or 5+1 items, they are re-enabled. + +While the engine is providing power, steam is being consumed. +The consumption speed includes a fixed 10% waste rate, and +the remaining 90% are applied proportionally to the actual +load in the machine. With the engine at nominal 300 power with +150 load in the system, it will consume steam for actual +300*(10% + 90%*150/300) = 165 power. + +Masterpiece mechanism and chain will decrease the mechanical +power drawn by the engine itself from 10 to 5. Masterpiece +barrel decreases waste rate by 4%. Masterpiece piston and pipe +decrease it by further 4%, and also decrease the whole steam +use rate by 10%. + +Explosions +---------- +The engine must be constructed using barrel, pipe and piston +from fire-safe, or in the magma version magma-safe metals. + +During operation weak parts get gradually worn out, and +eventually the engine explodes. It should also explode if +toppled during operation by a building destroyer, or a +tantruming dwarf. + +Save files +---------- +It should be safe to load and view engine-using fortresses +from a DF version without DFHack installed, except that in such +case the engines won't work. However actually making modifications +to them, or machines they connect to (including by pulling levers), +can easily result in inconsistent state once this plugin is +available again. The effects may be as weird as negative power +being generated. diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst new file mode 100644 index 000000000..c3db7a673 --- /dev/null +++ b/docs/plugins/stockflow.rst @@ -0,0 +1,31 @@ +stockflow +========= +Allows the fortress bookkeeper to queue jobs through the manager, +based on space or items available in stockpiles. + +Inspired by `workflow`. + +Usage: + +``stockflow enable`` + Enable the plugin. +``stockflow disable`` + Disable the plugin. +``stockflow fast`` + Enable the plugin in fast mode. +``stockflow list`` + List any work order settings for your stockpiles. +``stockflow status`` + Display whether the plugin is enabled. + +While enabled, the :kbd:`q` menu of each stockpile will have two new options: + +* :kbd:`j`: Select a job to order, from an interface like the manager's screen. +* :kbd:`J`: Cycle between several options for how many such jobs to order. + +Whenever the bookkeeper updates stockpile records, new work orders will +be placed on the manager's queue for each such selection, reduced by the +number of identical orders already in the queue. + +In fast mode, new work orders will be enqueued once per day, instead of +waiting for the bookkeeper. diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst new file mode 100644 index 000000000..000527ce5 --- /dev/null +++ b/docs/plugins/stockpiles.rst @@ -0,0 +1,28 @@ +.. _stocksettings: + +stockpiles +========== +Offers the following commands to save and load stockpile settings. +See `gui/stockpiles` for an in-game interface. + +:copystock: Copies the parameters of the currently highlighted stockpile to the custom + stockpile settings and switches to custom stockpile placement mode, effectively + allowing you to copy/paste stockpiles easily. + :dfhack-keybind:`copystock` + +:savestock: Saves the currently highlighted stockpile's settings to a file in your Dwarf + Fortress folder. This file can be used to copy settings between game saves or + players. e.g.: ``savestock food_settings.dfstock`` + +:loadstock: Loads a saved stockpile settings file and applies it to the currently selected + stockpile. e.g.: ``loadstock food_settings.dfstock`` + +To use savestock and loadstock, use the :kbd:`q` command to highlight a stockpile. +Then run savestock giving it a descriptive filename. Then, in a different (or +the same!) gameworld, you can highlight any stockpile with :kbd:`q` then execute the +``loadstock`` command passing it the name of that file. The settings will be +applied to that stockpile. + +Note that files are relative to the DF folder, so put your files there or in a +subfolder for easy access. Filenames should not have spaces. Generated materials, +divine metals, etc are not saved as they are different in every world. diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst new file mode 100644 index 000000000..cbfbce978 --- /dev/null +++ b/docs/plugins/stocks.rst @@ -0,0 +1,6 @@ +stocks +====== +Replaces the DF stocks screen with an improved version. + +:dfhack-keybind:`stocks` + diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst new file mode 100644 index 000000000..b6c154e66 --- /dev/null +++ b/docs/plugins/stonesense.rst @@ -0,0 +1,10 @@ +.. _plugin-stonesense: + +stonesense +========== +An isometric visualizer that runs in a second window. Usage: + +:stonesense: Open the visualiser in a new window. Alias ``ssense``. +:ssense overlay: Overlay DF window, replacing the map area. + +For more information, see `the full Stonesense README `. diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst new file mode 100644 index 000000000..0d578c363 --- /dev/null +++ b/docs/plugins/strangemood.rst @@ -0,0 +1,19 @@ +strangemood +=========== +Creates a strange mood job the same way the game itself normally does it. + +Options: + +:-force: Ignore normal strange mood preconditions (no recent mood, minimum + moodable population, artifact limit not reached). +:-unit: Make the strange mood strike the selected unit instead of picking + one randomly. Unit eligibility is still enforced. +:-type : Force the mood to be of a particular type instead of choosing randomly based on happiness. + Valid values for T are "fey", "secretive", "possessed", "fell", and "macabre". +:-skill S: Force the mood to use a specific skill instead of choosing the highest moodable skill. + Valid values are "miner", "carpenter", "engraver", "mason", "tanner", "weaver", + "clothier", "weaponsmith", "armorsmith", "metalsmith", "gemcutter", "gemsetter", + "woodcrafter", "stonecrafter", "metalcrafter", "glassmaker", "leatherworker", + "bonecarver", "bowyer", and "mechanic". + +Known limitations: if the selected unit is currently performing a job, the mood will not be started. diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst new file mode 100644 index 000000000..ee1567fdb --- /dev/null +++ b/docs/plugins/tailor.rst @@ -0,0 +1,11 @@ +tailor +====== + +Whenever the bookkeeper updates stockpile records, this plugin will scan every unit in the fort, +count up the number that are worn, and then order enough more made to replace all worn items. +If there are enough replacement items in inventory to replace all worn items, the units wearing them +will have the worn items confiscated (in the same manner as the `cleanowned` plugin) so that they'll +reeequip with replacement items. + +Use the `enable` and `disable ` commands to toggle this plugin's status, or run +``tailor status`` to check its current status. diff --git a/docs/plugins/tiletypes-command.rst b/docs/plugins/tiletypes-command.rst new file mode 100644 index 000000000..0f75c633e --- /dev/null +++ b/docs/plugins/tiletypes-command.rst @@ -0,0 +1,10 @@ +tiletypes-command +----------------- +Runs tiletypes commands, separated by ``;``. This makes it possible to change +tiletypes modes from a hotkey or via dfhack-run. + +Example:: + + tiletypes-command p any ; p s wall ; p sp normal + +This resets the paint filter to unsmoothed walls. diff --git a/docs/plugins/tiletypes-here-point.rst b/docs/plugins/tiletypes-here-point.rst new file mode 100644 index 000000000..8951bdff8 --- /dev/null +++ b/docs/plugins/tiletypes-here-point.rst @@ -0,0 +1,6 @@ +tiletypes-here-point +-------------------- +Apply the current tiletypes options at the in-game cursor position to a single +tile. Can be used from a hotkey. + +This command supports the same options as `tiletypes-here` above. diff --git a/docs/plugins/tiletypes-here.rst b/docs/plugins/tiletypes-here.rst new file mode 100644 index 000000000..fe9ceefd9 --- /dev/null +++ b/docs/plugins/tiletypes-here.rst @@ -0,0 +1,14 @@ +tiletypes-here +-------------- +Apply the current tiletypes options at the in-game cursor position, including +the brush. Can be used from a hotkey. + +Options: + +:``-c``, ``--cursor ,,``: + Use the specified map coordinates instead of the current cursor position. If + this option is specified, then an active game map cursor is not necessary. +:``-h``, ``--help``: + Show command help text. +:``-q``, ``--quiet``: + Suppress non-error status output. diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst new file mode 100644 index 000000000..41f780521 --- /dev/null +++ b/docs/plugins/tiletypes.rst @@ -0,0 +1,82 @@ +tiletypes +========= +Can be used for painting map tiles and is an interactive command, much like +`liquids`. Some properties of existing tiles can be looked up with `probe`. If +something goes wrong, `fixveins` may help. + +The tool works with two set of options and a brush. The brush determines which +tiles will be processed. First set of options is the filter, which can exclude +some of the tiles from the brush by looking at the tile properties. The second +set of options is the paint - this determines how the selected tiles are +changed. + +Both paint and filter can have many different properties including things like +general shape (WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, +etc.), state of 'designated', 'hidden' and 'light' flags. + +The properties of filter and paint can be partially defined. This means that +you can for example turn all stone fortifications into floors, preserving the +material:: + + filter material STONE + filter shape FORTIFICATION + paint shape FLOOR + +Or turn mineral vein floors back into walls:: + + filter shape FLOOR + filter material MINERAL + paint shape WALL + +The tool also allows tweaking some tile flags:: + + paint hidden 1 + paint hidden 0 + +This will hide previously revealed tiles (or show hidden with the 0 option). + +More recently, the tool supports changing the base material of the tile to +an arbitrary stone from the raws, by creating new veins as required. Note +that this mode paints under ice and constructions, instead of overwriting +them. To enable, use:: + + paint stone MICROCLINE + +This mode is incompatible with the regular ``material`` setting, so changing +it cancels the specific stone selection:: + + paint material ANY + +Since different vein types have different drop rates, it is possible to choose +which one to use in painting:: + + paint veintype CLUSTER_SMALL + +When the chosen type is ``CLUSTER`` (the default), the tool may automatically +choose to use layer stone or lava stone instead of veins if its material matches +the desired one. + +Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:: + + paint hidden ANY + paint shape ANY + filter material any + filter shape any + filter any + +You can use several different brushes for painting tiles: + +:point: a single tile +:range: a rectangular range +:column: a column ranging from current cursor to the first solid tile above +:block: a DF map block - 16x16 tiles, in a regular grid + +Example:: + + range 10 10 1 + +This will change the brush to a rectangle spanning 10x10 tiles on one z-level. +The range starts at the position of the cursor and goes to the east, south and +up. + +For more details, use ``tiletypes help``. diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst new file mode 100644 index 000000000..4e1ef40ec --- /dev/null +++ b/docs/plugins/title-folder.rst @@ -0,0 +1,3 @@ +title-folder +============= +Displays the DF folder name in the window title bar when enabled. diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst new file mode 100644 index 000000000..aed7a02e0 --- /dev/null +++ b/docs/plugins/title-version.rst @@ -0,0 +1,3 @@ +title-version +============= +Displays the DFHack version on DF's title screen when enabled. diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst new file mode 100644 index 000000000..b012df34d --- /dev/null +++ b/docs/plugins/trackstop.rst @@ -0,0 +1,5 @@ +trackstop +========= +Adds a :kbd:`q` menu for track stops, which is completely blank by default. +This allows you to view and/or change the track stop's friction and dump +direction settings, using the keybindings from the track stop building interface. diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst new file mode 100644 index 000000000..99c0b76bd --- /dev/null +++ b/docs/plugins/tubefill.rst @@ -0,0 +1,11 @@ +tubefill +======== +Fills all the adamantine veins again. Veins that were hollow will be left +alone. + +Options: + +:hollow: fill in naturally hollow veins too + +Beware that filling in hollow veins will trigger a demon invasion on top of +your miner when you dig into the region that used to be hollow. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst new file mode 100644 index 000000000..4ce7b6d38 --- /dev/null +++ b/docs/plugins/tweak.rst @@ -0,0 +1,91 @@ +tweak +===== +Contains various tweaks for minor bugs. + +One-shot subcommands: + +:clear-missing: Remove the missing status from the selected unit. + This allows engraving slabs for ghostly, but not yet + found, creatures. +:clear-ghostly: Remove the ghostly status from the selected unit and mark + it as dead. This allows getting rid of bugged ghosts + which do not show up in the engraving slab menu at all, + even after using clear-missing. It works, but is + potentially very dangerous - so use with care. Probably + (almost certainly) it does not have the same effects like + a proper burial. You've been warned. +:fixmigrant: Remove the resident/merchant flag from the selected unit. + Intended to fix bugged migrants/traders who stay at the + map edge and don't enter your fort. Only works for + dwarves (or generally the player's race in modded games). + Do NOT abuse this for 'real' caravan merchants (if you + really want to kidnap them, use 'tweak makeown' instead, + otherwise they will have their clothes set to forbidden etc). +:makeown: Force selected unit to become a member of your fort. + Can be abused to grab caravan merchants and escorts, even if + they don't belong to the player's race. Foreign sentients + (humans, elves) can be put to work, but you can't assign rooms + to them and they don't show up in DwarfTherapist because the + game treats them like pets. Grabbing draft animals from + a caravan can result in weirdness (animals go insane or berserk + and are not flagged as tame), but you are allowed to mark them + for slaughter. Grabbing wagons results in some funny spam, then + they are scuttled. + +Subcommands that persist until disabled or DF quits: + +.. comment: sort these alphabetically + +:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (:bug:`6481`). +:advmode-contained: Works around :bug:`6202`, custom reactions with container inputs + in advmode. The issue is that the screen tries to force you to select + the contents separately from the container. This forcefully skips child + reagents. +:block-labors: Prevents labors that can't be used from being toggled +:burrow-name-cancel: Implements the "back" option when renaming a burrow, + which currently does nothing (:bug:`1518`) +:cage-butcher: Adds an option to butcher units when viewing cages with :kbd:`q` +:civ-view-agreement: Fixes overlapping text on the "view agreement" screen +:condition-material: Fixes a crash in the work order contition material list (:bug:`9905`). +:craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`). + With this tweak, items made from cloth and leather will gain a level of + wear every 20 years. +:do-job-now: Adds a job priority toggle to the jobs list +:embark-profile-name: Allows the use of lowercase letters when saving embark profiles +:eggs-fertile: Displays a fertility indicator on nestboxes +:farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus +:fast-heat: Further improves temperature update performance by ensuring that 1 degree + of item temperature is crossed in no more than specified number of frames + when updating from the environment temperature. This reduces the time it + takes for stable-temp to stop updates again when equilibrium is disturbed. +:fast-trade: Makes Shift-Down in the Move Goods to Depot and Trade screens select + the current item (fully, in case of a stack), and scroll down one line. +:fps-min: Fixes the in-game minimum FPS setting +:hide-priority: Adds an option to hide designation priority indicators +:hotkey-clear: Adds an option to clear currently-bound hotkeys (in the :kbd:`H` menu) +:import-priority-category: + Allows changing the priority of all goods in a + category when discussing an import agreement with the liaison +:kitchen-prefs-all: Adds an option to toggle cook/brew for all visible items in kitchen preferences +:kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences +:kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs (:bug:`9000`) +:max-wheelbarrow: Allows assigning more than 3 wheelbarrows to a stockpile +:military-color-assigned: + Color squad candidates already assigned to other squads in yellow/green + to make them stand out more in the list. + + .. image:: ../images/tweak-mil-color.png + +:military-stable-assign: + Preserve list order and cursor position when assigning to squad, + i.e. stop the rightmost list of the Positions page of the military + screen from constantly resetting to the top. +:nestbox-color: Fixes the color of built nestboxes +:partial-items: Displays percentages on partially-consumed items such as hospital cloth +:reaction-gloves: Fixes reactions to produce gloves in sets with correct handedness (:bug:`6273`) +:shift-8-scroll: Gives Shift-8 (or :kbd:`*`) priority when scrolling menus, instead of scrolling the map +:stable-cursor: Saves the exact cursor position between t/q/k/d/b/etc menus of fortress mode, if the + map view is near enough to its previous position. +:stone-status-all: Adds an option to toggle the economic status of all stones +:title-start-rename: Adds a safe rename option to the title screen "Start Playing" menu +:tradereq-pet-gender: Displays pet genders on the trade request screen diff --git a/docs/plugins/unreveal.rst b/docs/plugins/unreveal.rst new file mode 100644 index 000000000..da7be8277 --- /dev/null +++ b/docs/plugins/unreveal.rst @@ -0,0 +1,23 @@ +reveal +====== +This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use ``reveal demons``. + +Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run ``unreveal``. + +Usage and related commands: + +:reveal: Reveal the whole map, except for HFS to avoid demons spawning +:reveal hell: Also show hell, but requires ``unreveal`` before unpausing +:reveal demon: Reveals everything and allows unpausing - good luck! +:unreveal: Reverts the effects of ``reveal`` +:revtoggle: Switches between ``reveal`` and ``unreveal`` +:revflood: Hide everything, then reveal tiles with a path to the cursor. + Note that tiles behind constructed walls are also revealed as a + workaround for :bug:`1871`. +:revforget: Discard info about what was visible before revealing the map. + Only useful where (e.g.) you abandoned with the fort revealed + and no longer want the data. diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst new file mode 100644 index 000000000..bacecf612 --- /dev/null +++ b/docs/plugins/workNow.rst @@ -0,0 +1,12 @@ +workNow +======= +Don't allow dwarves to idle if any jobs are available. + +When workNow is active, every time the game pauses, DF will make dwarves +perform any appropriate available jobs. This includes when you one step +through the game using the pause menu. Usage: + +:workNow: print workNow status +:workNow 0: deactivate workNow +:workNow 1: activate workNow (look for jobs on pause, and only then) +:workNow 2: make dwarves look for jobs whenever a job completes diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst new file mode 100644 index 000000000..17db57e69 --- /dev/null +++ b/docs/plugins/workflow.rst @@ -0,0 +1,113 @@ +workflow +======== +Manage control of repeat jobs. `gui/workflow` provides a simple +front-end integrated in the game UI. + +Usage: + +``workflow enable [option...], workflow disable [option...]`` + If no options are specified, enables or disables the plugin. + Otherwise, enables or disables any of the following options: + + - drybuckets: Automatically empty abandoned water buckets. + - auto-melt: Resume melt jobs when there are objects to melt. +``workflow jobs`` + List workflow-controlled jobs (if in a workshop, filtered by it). +``workflow list`` + List active constraints, and their job counts. +``workflow list-commands`` + List active constraints as workflow commands that re-create them; + this list can be copied to a file, and then reloaded using the + ``script`` built-in command. +``workflow count [cnt-gap]`` + Set a constraint, counting every stack as 1 item. +``workflow amount [cnt-gap]`` + Set a constraint, counting all items within stacks. +``workflow unlimit `` + Delete a constraint. +``workflow unlimit-all`` + Delete all constraints. + +Function +-------- +When the plugin is enabled, it protects all repeat jobs from removal. +If they do disappear due to any cause, they are immediately re-added to their +workshop and suspended. + +In addition, when any constraints on item amounts are set, repeat jobs that +produce that kind of item are automatically suspended and resumed as the item +amount goes above or below the limit. The gap specifies how much below the limit +the amount has to drop before jobs are resumed; this is intended to reduce +the frequency of jobs being toggled. + +Constraint format +----------------- +The constraint spec consists of 4 parts, separated with ``/`` characters:: + + ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,] + +The first part is mandatory and specifies the item type and subtype, +using the raw tokens for items (the same syntax used custom reaction inputs). +For more information, see :wiki:`this wiki page `. + +The subsequent parts are optional: + +- A generic material spec constrains the item material to one of + the hard-coded generic classes, which currently include:: + + PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN + METAL STONE SAND GLASS CLAY MILK + +- A specific material spec chooses the material exactly, using the + raw syntax for reaction input materials, e.g. ``INORGANIC:IRON``, + although for convenience it also allows just ``IRON``, or ``ACACIA:WOOD`` etc. + See the link above for more details on the unabbreviated raw syntax. + +- A comma-separated list of miscellaneous flags, which currently can + be used to ignore imported items or items below a certain quality. + +Constraint examples +------------------- +Keep metal bolts within 900-1000, and wood/bone within 150-200:: + + workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100 + workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50 + +Keep the number of prepared food & drink stacks between 90 and 120:: + + workflow count FOOD 120 30 + workflow count DRINK 120 30 + +Make sure there are always 25-30 empty bins/barrels/bags:: + + workflow count BIN 30 + workflow count BARREL 30 + workflow count BOX/CLOTH,SILK,YARN 30 + +Make sure there are always 15-20 coal and 25-30 copper bars:: + + workflow count BAR//COAL 20 + workflow count BAR//COPPER 30 + +Produce 15-20 gold crafts:: + + workflow count CRAFTS//GOLD 20 + +Collect 15-20 sand bags and clay boulders:: + + workflow count POWDER_MISC/SAND 20 + workflow count BOULDER/CLAY 20 + +Make sure there are always 80-100 units of dimple dye:: + + workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 + +.. note:: + + In order for this to work, you have to set the material of the PLANT input + on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the `job item-material ` + command. Otherwise the plugin won't be able to deduce the output material. + +Maintain 10-100 locally-made crafts of exceptional quality:: + + workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90 diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst new file mode 100644 index 000000000..a3112c147 --- /dev/null +++ b/docs/plugins/zone.rst @@ -0,0 +1,130 @@ +zone +==== +Helps a bit with managing activity zones (pens, pastures and pits) and cages. + +:dfhack-keybind:`zone` + +Options: + +:set: Set zone or cage under cursor as default for future assigns. +:assign: Assign unit(s) to the pen or pit marked with the 'set' command. + If no filters are set a unit must be selected in the in-game ui. + Can also be followed by a valid zone id which will be set + instead. +:unassign: Unassign selected creature from it's zone. +:nick: Mass-assign nicknames, must be followed by the name you want + to set. +:remnick: Mass-remove nicknames. +:enumnick: Assign enumerated nicknames (e.g. "Hen 1", "Hen 2"...). Must be + followed by the prefix to use in nicknames. +:tocages: Assign unit(s) to cages inside a pasture. +:uinfo: Print info about unit(s). If no filters are set a unit must + be selected in the in-game ui. +:zinfo: Print info about zone(s). If no filters are set zones under + the cursor are listed. +:verbose: Print some more info. +:filters: Print list of valid filter options. +:examples: Print some usage examples. +:not: Negates the next filter keyword. + +Filters: + +:all: Process all units (to be used with additional filters). +:count: Must be followed by a number. Process only n units (to be used + with additional filters). +:unassigned: Not assigned to zone, chain or built cage. +:minage: Minimum age. Must be followed by number. +:maxage: Maximum age. Must be followed by number. +:race: Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, + etc). Negatable. +:caged: In a built cage. Negatable. +:own: From own civilization. Negatable. +:merchant: Is a merchant / belongs to a merchant. Should only be used for + pitting, not for stealing animals (slaughter should work). +:war: Trained war creature. Negatable. +:hunting: Trained hunting creature. Negatable. +:tamed: Creature is tame. Negatable. +:trained: Creature is trained. Finds war/hunting creatures as well as + creatures who have a training level greater than 'domesticated'. + If you want to specifically search for war/hunting creatures use + 'war' or 'hunting' Negatable. +:trainablewar: Creature can be trained for war (and is not already trained for + war/hunt). Negatable. +:trainablehunt: Creature can be trained for hunting (and is not already trained + for war/hunt). Negatable. +:male: Creature is male. Negatable. +:female: Creature is female. Negatable. +:egglayer: Race lays eggs. Negatable. +:grazer: Race is a grazer. Negatable. +:milkable: Race is milkable. Negatable. + +Usage with single units +----------------------- +One convenient way to use the zone tool is to bind the command 'zone assign' to +a hotkey, maybe also the command 'zone set'. Place the in-game cursor over +a pen/pasture or pit, use 'zone set' to mark it. Then you can select units +on the map (in 'v' or 'k' mode), in the unit list or from inside cages +and use 'zone assign' to assign them to their new home. Allows pitting your +own dwarves, by the way. + +Usage with filters +------------------ +All filters can be used together with the 'assign' command. + +Restrictions: It's not possible to assign units who are inside built cages +or chained because in most cases that won't be desirable anyways. +It's not possible to cage owned pets because in that case the owner +uncages them after a while which results in infinite hauling back and forth. + +Usually you should always use the filter 'own' (which implies tame) unless you +want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless +you specify 'race DWARF' (so it's safe to use 'assign all own' to one big +pasture if you want to have all your animals at the same place). 'egglayer' and +'milkable' should be used together with 'female' unless you have a mod with +egg-laying male elves who give milk or whatever. Merchants and their animals are +ignored unless you specify 'merchant' (pitting them should be no problem, +but stealing and pasturing their animals is not a good idea since currently they +are not properly added to your own stocks; slaughtering them should work). + +Most filters can be negated (e.g. 'not grazer' -> race is not a grazer). + +Mass-renaming +------------- +Using the 'nick' command you can set the same nickname for multiple units. +If used without 'assign', 'all' or 'count' it will rename all units in the +current default target zone. Combined with 'assign', 'all' or 'count' (and +further optional filters) it will rename units matching the filter conditions. + +Cage zones +---------- +Using the 'tocages' command you can assign units to a set of cages, for example +a room next to your butcher shop(s). They will be spread evenly among available +cages to optimize hauling to and butchering from them. For this to work you need +to build cages and then place one pen/pasture activity zone above them, covering +all cages you want to use. Then use 'zone set' (like with 'assign') and use +'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it +would make no sense, but can be used together with 'nick' or 'remnick' and all +the usual filters. + +Examples +-------- +``zone assign all own ALPACA minage 3 maxage 10`` + Assign all own alpacas who are between 3 and 10 years old to the selected + pasture. +``zone assign all own caged grazer nick ineedgrass`` + Assign all own grazers who are sitting in cages on stockpiles (e.g. after + buying them from merchants) to the selected pasture and give them + the nickname 'ineedgrass'. +``zone assign all own not grazer not race CAT`` + Assign all own animals who are not grazers, excluding cats. +``zone assign count 5 own female milkable`` + Assign up to 5 own female milkable creatures to the selected pasture. +``zone assign all own race DWARF maxage 2`` + Throw all useless kids into a pit :) +``zone nick donttouchme`` + Nicknames all units in the current default zone or cage to 'donttouchme'. + Mostly intended to be used for special pastures or cages which are not marked + as rooms you want to protect from autobutcher. +``zone tocages count 50 own tame male not grazer`` + Stuff up to 50 owned tame male animals who are not grazers into cages built + on the current default zone. From a58b56abc4a0f0197eb6378b201de9044c49b7f5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 9 Jul 2022 23:16:40 -0700 Subject: [PATCH 010/334] don't error out if files cannot be read --- library/lua/helpdb.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index e72f0804d..83a281884 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -206,8 +206,10 @@ local function make_script_entry(old_entry, entry_name, script_source_path) end local entry = make_default_entry(entry_name, {[ENTRY_TYPES.COMMAND]=true}, HELP_SOURCES.SCRIPT, source_timestamp, script_source_path) + local ok, lines = pcall(io.lines, script_source_path) + if not ok then return entry end local is_rb = script_source_path:endswith('.rb') - update_entry(entry, io.lines(script_source_path), + update_entry(entry, lines, {begin_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_BEGIN), end_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_END), first_line_is_short_help=true}) @@ -343,7 +345,9 @@ end -- to tag_index, initizlizing each entry with an empty list. local function initialize_tags() local tag, desc, in_desc = nil, nil, false - for line in io.lines(TAG_DEFINITIONS) do + local ok, lines = pcall(io.lines, TAG_DEFINITIONS) + if not ok then return end + for line in lines do if in_desc then line = line:trim() if #line == 0 then From 12557f8dc14b77ab86f42e448ab8c1c065bb9560 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 10 Jul 2022 06:47:15 +0000 Subject: [PATCH 011/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/plugins/diggingInvaders.rst | 1 - docs/plugins/stocks.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index 0557225fb..09a22c07a 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -26,4 +26,3 @@ Action Cost Delay Notes ``destroyRoughConstruction`` 1,000 1,000 constructions made from boulders ``destroySmoothConstruction`` 100 100 constructions made from blocks or bars ============================== ======= ====== ================================= - diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index cbfbce978..404c96a6d 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -3,4 +3,3 @@ stocks Replaces the DF stocks screen with an improved version. :dfhack-keybind:`stocks` - From fdd406b7220d6c2086a959a2c6cd69a5c7858644 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 9 Jul 2022 23:58:11 -0700 Subject: [PATCH 012/334] ensure all files are reread on every docs build this fixes the issue where the Stonesense docs were getting ignored --- CMakeLists.txt | 4 ++-- conf.py | 2 +- docs/build.sh | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46bd1917b..a076bbc76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -478,13 +478,13 @@ if(BUILD_DOCS) set_source_files_properties(${SPHINX_OUTPUT} PROPERTIES GENERATED TRUE) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND ${SPHINX_EXECUTABLE} - -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" + -E -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/html" -w "${CMAKE_BINARY_DIR}/docs/html/_sphinx-warnings.txt" -j auto COMMAND ${SPHINX_EXECUTABLE} - -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" + -E -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/text" -w "${CMAKE_BINARY_DIR}/docs/text/_sphinx-warnings.txt" diff --git a/conf.py b/conf.py index d7be99a15..530a7a312 100644 --- a/conf.py +++ b/conf.py @@ -140,7 +140,7 @@ def write_tool_docs(): os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), mode=0o755, exist_ok=True) with open('docs/tools/{}.rst'.format(k[0]), mode) as outfile: - if k[0] != 'search': + if k[0] != 'search' and k[0] != 'stonesense': outfile.write(label) outfile.write(include) diff --git a/docs/build.sh b/docs/build.sh index c696d5fbe..28182d9c0 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -11,5 +11,5 @@ cd $(dirname "$0") cd .. -"${SPHINX:-sphinx-build}" -b html -d build/docs/html . docs/html -w build/docs/html/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" -"${SPHINX:-sphinx-build}" -b text -d build/docs/text . docs/text -w build/docs/text/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" +"${SPHINX:-sphinx-build}" -E -b html -d build/docs/html . docs/html -w build/docs/html/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" +"${SPHINX:-sphinx-build}" -E -b text -d build/docs/text . docs/text -w build/docs/text/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" From 4ed15ffcc4032dec4b31def7d02ad8e100c91d5a Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 10 Jul 2022 20:48:24 -0700 Subject: [PATCH 013/334] fix parsing of first line as the short_help --- library/lua/helpdb.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 83a281884..a145ab5f7 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -92,9 +92,10 @@ end -- end_marker (string that marks the end of the help text; text will stop -- being parsed after this marker is seen) -- no_header (don't try to find the entity name at the top of the help text) --- first_line_is_short_help (read the short help text from the first commented --- line of the script instead of using the first --- sentence of the long help text) +-- first_line_is_short_help (if set, then read the short help text from the +-- first commented line of the script instead of +-- using the first sentence of the long help text. +-- value is the comment character.) local function update_entry(entry, iterator, opts) opts = opts or {} local lines = {} @@ -104,7 +105,7 @@ local function update_entry(entry, iterator, opts) for line in iterator do if not short_help_found and first_line_is_short_help then line = line:trim() - local _,_,text = line:find('^%-%-%s*(.*)') or line:find('^#%s*(.*)') + local _,_,text = line:find('^'..first_line_is_short_help..'%s*(.*)') if not text then -- if no first-line short help found, fall back to getting the -- first sentence of the help text. @@ -212,7 +213,7 @@ local function make_script_entry(old_entry, entry_name, script_source_path) update_entry(entry, lines, {begin_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_BEGIN), end_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_END), - first_line_is_short_help=true}) + first_line_is_short_help=(is_rb and '#' or '%-%-')}) return entry end From a5da3c18f9957b26e948e4db3ec87fd2c5dff003 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 11 Jul 2022 17:23:23 -0700 Subject: [PATCH 014/334] reset scroll position when the text is changed --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 1a1b4c6fb..d5d94ffd9 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -469,7 +469,6 @@ Label.ATTRS{ } function Label:init(args) - self.start_line_num = 1 -- use existing saved text if no explicit text was specified. this avoids -- overwriting pre-formatted text that subclasses may have already set self:setText(args.text or self.text) @@ -479,6 +478,7 @@ function Label:init(args) end function Label:setText(text) + self.start_line_num = 1 self.text = text parse_label_text(self) From 328d839f1938d2cf4623fc6e26db9fada346e354 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 11 Jul 2022 17:23:56 -0700 Subject: [PATCH 015/334] support backtick as a keybinding and bind it to gui/launcher --- data/init/dfhack.keybindings.init | 2 ++ docs/Builtin.rst | 4 ++-- library/Core.cpp | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index e32a7c58e..f38840e06 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -8,6 +8,8 @@ # Generic dwarfmode bindings # ############################## +keybinding add ` gui/launcher + # show all current key bindings keybinding add Ctrl-F1 hotkeys keybinding add Alt-F1 hotkeys diff --git a/docs/Builtin.rst b/docs/Builtin.rst index 2d938e30f..cf58337bc 100644 --- a/docs/Builtin.rst +++ b/docs/Builtin.rst @@ -107,8 +107,8 @@ To set keybindings, use the built-in ``keybinding`` command. Like any other command it can be used at any time from the console, but bindings are not remembered between runs of the game unless re-created in `dfhack.init`. -Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, or F1-F12 are -supported. +Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or ``\``` +are supported. Possible ways to call the command: diff --git a/library/Core.cpp b/library/Core.cpp index 62fe366f1..a4feab742 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2558,6 +2558,9 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') { *psym = SDL::K_a + (keyspec[0]-'A'); return true; + } else if (keyspec.size() == 1 && keyspec[0] == '`') { + *psym = SDL::K_BACKQUOTE; + return true; } else if (keyspec.size() == 1 && keyspec[0] >= '0' && keyspec[0] <= '9') { *psym = SDL::K_0 + (keyspec[0]-'0'); return true; From d68350c1f116cb569fab996f8743fc099b1d8907 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 11 Jul 2022 17:24:17 -0700 Subject: [PATCH 016/334] wrap text at 52 characters for in-game display --- conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf.py b/conf.py index 530a7a312..461fa789b 100644 --- a/conf.py +++ b/conf.py @@ -363,3 +363,9 @@ latex_documents = [ ] latex_toplevel_sectioning = 'part' + +# -- Options for text output --------------------------------------------- + +from sphinx.writers import text + +text.MAXWIDTH = 52 From 185f49976c51e0e689c06f6ae67c789a7c8feb92 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 11 Jul 2022 17:24:53 -0700 Subject: [PATCH 017/334] ensure scripts get their entry type set --- library/lua/helpdb.lua | 58 +++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index a145ab5f7..8856c7ad8 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -318,6 +318,7 @@ end -- scan for scripts and add their help to the db local function scan_scripts(old_db, db) + local entry_types = {[ENTRY_TYPES.COMMAND]=true} for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( script_path, nil, false) @@ -330,12 +331,13 @@ local function scan_scripts(old_db, db) goto continue end local dot_index = f.path:find('%.[^.]*$') + local entry_name = f.path:sub(1, dot_index - 1) local script_source = script_path .. '/' .. f.path update_db(old_db, db, - has_rendered_help(f.path) and + has_rendered_help(entry_name) and HELP_SOURCES.RENDERED or HELP_SOURCES.SCRIPT, - f.path:sub(1, dot_index - 1), - {script_source=script_source}) + entry_name, + {entry_types=entry_types, script_source=script_source}) ::continue:: end ::skip_path:: @@ -388,6 +390,33 @@ end -- get API --------------------------------------------------------------------------- +-- converts strings into single-element lists containing that string +local function normalize_string_list(l) + if not l or #l == 0 then return nil end + if type(l) == 'string' then + return {l} + end + return l +end + +local function has_keys(str, dict) + if not str or #str == 0 then + return false + end + ensure_db() + for _,s in ipairs(normalize_string_list(str)) do + if not dict[s] then + return false + end + end + return true +end + +-- returns whether the given string (or list of strings) is an entry in the db +function is_entry(str) + return has_keys(str, db) +end + local function get_db_property(entry_name, property) ensure_db() if not db[entry_name] then @@ -420,19 +449,9 @@ function get_entry_tags(entry) return set_to_sorted_list(get_db_property(entry, 'tags')) end --- returns whether the given string matches a tag name +-- returns whether the given string (or list of strings) matches a tag name function is_tag(str) - if not str or #str == 0 then - return false - end - ensure_db() - if type(str) == "string" then str = {str} end - for _,s in ipairs(str) do - if not tag_index[s] then - return false - end - end - return true + return has_keys(str, tag_index) end -- returns the defined tags in alphabetical order @@ -524,15 +543,6 @@ local function matches(entry_name, filter) return true end --- converts strings into single-element lists containing that string -local function normalize_string_list(l) - if not l or #l == 0 then return nil end - if type(l) == 'string' then - return {l} - end - return l -end - -- normalizes the lists in the filter and returns nil if no filter elements are -- populated local function normalize_filter(f) From e926e1116eb9490ccb6ce875a86dd33418f303c8 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 14 Jul 2022 13:19:30 -0700 Subject: [PATCH 018/334] replace more Core cpp code with calls to helpdb also document devel/dump-rpc builtin --- docs/Builtin.rst | 11 + library/Core.cpp | 1104 ++++++++++++++++-------------------- library/LuaApi.cpp | 10 + library/include/LuaTools.h | 2 + library/lua/helpdb.lua | 28 +- 5 files changed, 547 insertions(+), 608 deletions(-) diff --git a/docs/Builtin.rst b/docs/Builtin.rst index cf58337bc..8e59840b4 100644 --- a/docs/Builtin.rst +++ b/docs/Builtin.rst @@ -255,6 +255,17 @@ type ---- ``type command`` shows where ``command`` is implemented. +.. _devel/dump-rpc: + +devel/dump-rpc +-------------- + +Writes RPC endpoint information to the specified file. + +Usage:: + + devel/dump-rpc FILENAME + Other Commands -------------- The following commands are *not* built-in, but offer similarly useful functions. diff --git a/library/Core.cpp b/library/Core.cpp index a4feab742..30b87007d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -277,65 +277,6 @@ static string dfhack_version_desc() return s.str(); } -static std::string getScriptHelp(std::string path, std::string helpprefix) -{ - ifstream script(path.c_str()); - - if (script.good()) - { - std::string help; - if (getline(script, help) && - help.substr(0,helpprefix.length()) == helpprefix) - { - help = help.substr(helpprefix.length()); - while (help.size() && help[0] == ' ') - help = help.substr(1); - return help; - } - } - - return "No help available."; -} - -static void listScripts(PluginManager *plug_mgr, std::map &pset, std::string path, bool all, std::string prefix = "") -{ - std::vector files; - Filesystem::listdir(path, files); - - path += '/'; - for (size_t i = 0; i < files.size(); i++) - { - if (hasEnding(files[i], ".lua")) - { - string help = getScriptHelp(path + files[i], "--"); - string key = prefix + files[i].substr(0, files[i].size()-4); - if (pset.find(key) == pset.end()) { - pset[key] = help; - } - } - else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && hasEnding(files[i], ".rb")) - { - string help = getScriptHelp(path + files[i], "#"); - string key = prefix + files[i].substr(0, files[i].size()-3); - if (pset.find(key) == pset.end()) { - pset[key] = help; - } - } - else if (all && !files[i].empty() && files[i][0] != '.' && files[i] != "internal" && files[i] != "test") - { - listScripts(plug_mgr, pset, path+files[i]+"/", all, prefix+files[i]+"/"); - } - } -} - -static void listAllScripts(map &pset, bool all) -{ - vector paths; - Core::getInstance().getScriptPaths(&paths); - for (string path : paths) - listScripts(Core::getInstance().getPluginManager(), pset, path, all); -} - namespace { struct ScriptArgs { const string *pcmd; @@ -442,60 +383,52 @@ command_result Core::runCommand(color_ostream &out, const std::string &command) return CR_NOT_IMPLEMENTED; } -// List of built in commands -static const std::set built_in_commands = { - "ls" , - "help" , - "tags" , - "type" , - "load" , - "unload" , - "reload" , - "enable" , - "disable" , - "plug" , - "keybinding" , - "alias" , - "fpause" , - "cls" , - "die" , - "kill-lua" , - "script" , - "hide" , - "show" , - "sc-script" -}; +bool is_builtin(color_ostream &con, const string &command) { + CoreSuspender suspend; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); -static bool try_autocomplete(color_ostream &con, const std::string &first, std::string &completed) -{ - std::vector possible; + if (!lua_checkstack(L, 1) || + !Lua::PushModulePublic(con, L, "helpdb", "is_builtin")) { + con.printerr("Failed to load helpdb Lua code\n"); + return false; + } - // Check for possible built in commands to autocomplete first - for (auto const &command : built_in_commands) - if (command.substr(0, first.size()) == first) - possible.push_back(command); + Lua::Push(L, command); - auto plug_mgr = Core::getInstance().getPluginManager(); - for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) - { - const Plugin * plug = it->second; - for (size_t j = 0; j < plug->size(); j++) - { - const PluginCommand &pcmd = (*plug)[j]; - if (pcmd.isHotkeyCommand()) - continue; - if (pcmd.name.substr(0, first.size()) == first) - possible.push_back(pcmd.name); - } + if (!Lua::SafeCall(con, L, 1, 1)) { + con.printerr("Failed Lua call to helpdb.is_builtin.\n"); + return false; + } + + return lua_toboolean(L, -1); +} + +void get_commands(color_ostream &con, vector &commands) { + CoreSuspender suspend; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 1) || + !Lua::PushModulePublic(con, L, "helpdb", "get_commands")) { + con.printerr("Failed to load helpdb Lua code\n"); + return; } - bool all = (first.find('/') != std::string::npos); + if (!Lua::SafeCall(con, L, 0, 1)) { + con.printerr("Failed Lua call to helpdb.get_commands.\n"); + } - std::map scripts; - listAllScripts(scripts, all); - for (auto iter = scripts.begin(); iter != scripts.end(); ++iter) - if (iter->first.substr(0, first.size()) == first) - possible.push_back(iter->first); + Lua::GetVector(L, commands); +} + +static bool try_autocomplete(color_ostream &con, const std::string &first, std::string &completed) +{ + std::vector commands, possible; + + for (auto &command : commands) + if (command.substr(0, first.size()) == first) + possible.push_back(command); if (possible.size() == 1) { @@ -651,30 +584,6 @@ static std::string sc_event_name (state_change_event id) { return "SC_UNKNOWN"; } -string getBuiltinCommand(std::string cmd) -{ - std::string builtin = ""; - - // Check our list of builtin commands from the header - if (built_in_commands.count(cmd)) - builtin = cmd; - - // Check for some common aliases for built in commands - else if (cmd == "?" || cmd == "man") - builtin = "help"; - - else if (cmd == "dir") - builtin = "ls"; - - else if (cmd == "clear") - builtin = "cls"; - - else if (cmd == "devel/dump-rpc") - builtin = "devel/dump-rpc"; - - return builtin; -} - void help_helper(color_ostream &con, const string &entry_name) { CoreSuspender suspend; auto L = Lua::Core::State; @@ -753,587 +662,582 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v return CR_FAILURE; } - command_result res; - if (!first.empty()) + if (first.empty()) + return CR_NOT_IMPLEMENTED; + + if (first.find('\\') != std::string::npos) { - if(first.find('\\') != std::string::npos) + con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", first.c_str()); + for (size_t i = 0; i < first.size(); i++) { - con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", first.c_str()); - for (size_t i = 0; i < first.size(); i++) - { - if (first[i] == '\\') - first[i] = '/'; - } + if (first[i] == '\\') + first[i] = '/'; } + } - // let's see what we actually got - string builtin = getBuiltinCommand(first); - if (builtin == "help") + // let's see what we actually got + command_result res; + if (first == "help" || first == "man" || first == "?") + { + if(!parts.size()) { - if(!parts.size()) - { - if (con.is_console()) - { - con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n" - "Some basic editing capabilities are included (single-line text editing).\n" - "The console also has a command history - you can navigate it with Up and Down keys.\n" - "On Windows, you may have to resize your console window. The appropriate menu is accessible\n" - "by clicking on the program icon in the top bar of the window.\n\n"); - } - con.print("Here are some basic commands to get you started:\n" - " help|?|man - This text.\n" - " help - Usage help for the given plugin, command, or script.\n" - " tags - List the tags that the DFHack tools are grouped by.\n" - " ls|dir [] - List commands, optionally filtered by a tag or substring.\n" - " Optional parameters:\n" - " --notags: skip printing tags for each command.\n" - " --dev: include commands intended for developers and modders.\n" - " cls|clear - Clear the console.\n" - " fpause - Force DF to pause.\n" - " die - Force DF to close immediately, without saving.\n" - " keybinding - Modify bindings of commands to in-game key shortcuts.\n" - "\n" - "See more commands by running 'ls'.\n\n" - ); - - con.print("DFHack version %s\n", dfhack_version_desc().c_str()); - } - else + if (con.is_console()) { - help_helper(con, parts[0]); + con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n" + "Some basic editing capabilities are included (single-line text editing).\n" + "The console also has a command history - you can navigate it with Up and Down keys.\n" + "On Windows, you may have to resize your console window. The appropriate menu is accessible\n" + "by clicking on the program icon in the top bar of the window.\n\n"); } + con.print("Here are some basic commands to get you started:\n" + " help|?|man - This text.\n" + " help - Usage help for the given plugin, command, or script.\n" + " tags - List the tags that the DFHack tools are grouped by.\n" + " ls|dir [] - List commands, optionally filtered by a tag or substring.\n" + " Optional parameters:\n" + " --notags: skip printing tags for each command.\n" + " --dev: include commands intended for developers and modders.\n" + " cls|clear - Clear the console.\n" + " fpause - Force DF to pause.\n" + " die - Force DF to close immediately, without saving.\n" + " keybinding - Modify bindings of commands to in-game key shortcuts.\n" + "\n" + "See more commands by running 'ls'.\n\n" + ); + + con.print("DFHack version %s\n", dfhack_version_desc().c_str()); } - else if (builtin == "tags") + else { - tags_helper(con); + help_helper(con, parts[0]); } - else if (builtin == "load" || builtin == "unload" || builtin == "reload") + } + else if (first == "tags") + { + tags_helper(con); + } + else if (first == "load" || first == "unload" || first == "reload") + { + bool all = false; + bool load = (first == "load"); + bool unload = (first == "unload"); + if (parts.size()) { - bool all = false; - bool load = (builtin == "load"); - bool unload = (builtin == "unload"); - if (parts.size()) + for (auto p = parts.begin(); p != parts.end(); p++) { - for (auto p = parts.begin(); p != parts.end(); p++) - { - if (p->size() && (*p)[0] == '-') - { - if (p->find('a') != string::npos) - all = true; - } - } - if (all) - { - if (load) - plug_mgr->loadAll(); - else if (unload) - plug_mgr->unloadAll(); - else - plug_mgr->reloadAll(); - return CR_OK; - } - for (auto p = parts.begin(); p != parts.end(); p++) + if (p->size() && (*p)[0] == '-') { - if (!p->size() || (*p)[0] == '-') - continue; - if (load) - plug_mgr->load(*p); - else if (unload) - plug_mgr->unload(*p); - else - plug_mgr->reload(*p); + if (p->find('a') != string::npos) + all = true; } } - else - con.printerr("%s: no arguments\n", builtin.c_str()); + if (all) + { + if (load) + plug_mgr->loadAll(); + else if (unload) + plug_mgr->unloadAll(); + else + plug_mgr->reloadAll(); + return CR_OK; + } + for (auto p = parts.begin(); p != parts.end(); p++) + { + if (!p->size() || (*p)[0] == '-') + continue; + if (load) + plug_mgr->load(*p); + else if (unload) + plug_mgr->unload(*p); + else + plug_mgr->reload(*p); + } } - else if( builtin == "enable" || builtin == "disable" ) - { - CoreSuspender suspend; - bool enable = (builtin == "enable"); + else + con.printerr("%s: no arguments\n", first.c_str()); + } + else if( first == "enable" || first == "disable" ) + { + CoreSuspender suspend; + bool enable = (first == "enable"); - if(parts.size()) + if(parts.size()) + { + for (size_t i = 0; i < parts.size(); i++) { - for (size_t i = 0; i < parts.size(); i++) + std::string part = parts[i]; + if (part.find('\\') != std::string::npos) { - std::string part = parts[i]; - if (part.find('\\') != std::string::npos) + con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", part.c_str()); + for (size_t j = 0; j < part.size(); j++) { - con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", part.c_str()); - for (size_t j = 0; j < part.size(); j++) - { - if (part[j] == '\\') - part[j] = '/'; - } + if (part[j] == '\\') + part[j] = '/'; } + } - Plugin * plug = (*plug_mgr)[part]; + Plugin * plug = (*plug_mgr)[part]; - if(!plug) - { - std::string lua = findScript(part + ".lua"); - if (lua.size()) - { - res = enableLuaScript(con, part, enable); - } - else - { - res = CR_NOT_FOUND; - con.printerr("No such plugin or Lua script: %s\n", part.c_str()); - } - } - else if (!plug->can_set_enabled()) + if(!plug) + { + std::string lua = findScript(part + ".lua"); + if (lua.size()) { - res = CR_NOT_IMPLEMENTED; - con.printerr("Cannot %s plugin: %s\n", builtin.c_str(), part.c_str()); + res = enableLuaScript(con, part, enable); } else { - res = plug->set_enabled(con, enable); - - if (res != CR_OK || plug->is_enabled() != enable) - con.printerr("Could not %s plugin: %s\n", builtin.c_str(), part.c_str()); + res = CR_NOT_FOUND; + con.printerr("No such plugin or Lua script: %s\n", part.c_str()); } } - - return res; - } - else - { - for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) + else if (!plug->can_set_enabled()) + { + res = CR_NOT_IMPLEMENTED; + con.printerr("Cannot %s plugin: %s\n", first.c_str(), part.c_str()); + } + else { - Plugin * plug = it->second; - if (!plug->can_be_enabled()) continue; - - con.print( - "%20s\t%-3s%s\n", - (plug->getName()+":").c_str(), - plug->is_enabled() ? "on" : "off", - plug->can_set_enabled() ? "" : " (controlled elsewhere)" - ); + res = plug->set_enabled(con, enable); + + if (res != CR_OK || plug->is_enabled() != enable) + con.printerr("Could not %s plugin: %s\n", first.c_str(), part.c_str()); } } + + return res; } - else if (builtin == "ls" || builtin == "dir") - { - ls_helper(con, parts); - } - else if (builtin == "plug") + else { - const char *header_format = "%30s %10s %4s %8s\n"; - const char *row_format = "%30s %10s %4i %8s\n"; - con.print(header_format, "Name", "State", "Cmds", "Enabled"); - - plug_mgr->refresh(); for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) { Plugin * plug = it->second; - if (!plug) - continue; - if (parts.size() && std::find(parts.begin(), parts.end(), plug->getName()) == parts.end()) - continue; - color_value color; - switch (plug->getState()) - { - case Plugin::PS_LOADED: - color = COLOR_RESET; - break; - case Plugin::PS_UNLOADED: - case Plugin::PS_UNLOADING: - color = COLOR_YELLOW; - break; - case Plugin::PS_LOADING: - color = COLOR_LIGHTBLUE; - break; - case Plugin::PS_BROKEN: - color = COLOR_LIGHTRED; - break; - default: - color = COLOR_LIGHTMAGENTA; - break; - } - con.color(color); - con.print(row_format, - plug->getName().c_str(), - Plugin::getStateDescription(plug->getState()), - plug->size(), - (plug->can_be_enabled() - ? (plug->is_enabled() ? "enabled" : "disabled") - : "n/a") + if (!plug->can_be_enabled()) continue; + + con.print( + "%20s\t%-3s%s\n", + (plug->getName()+":").c_str(), + plug->is_enabled() ? "on" : "off", + plug->can_set_enabled() ? "" : " (controlled elsewhere)" ); - con.color(COLOR_RESET); } } - else if (builtin == "type") + } + else if (first == "ls" || first == "dir") + { + ls_helper(con, parts); + } + else if (first == "plug") + { + const char *header_format = "%30s %10s %4s %8s\n"; + const char *row_format = "%30s %10s %4i %8s\n"; + con.print(header_format, "Name", "State", "Cmds", "Enabled"); + + plug_mgr->refresh(); + for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) { - if (!parts.size()) - { - con.printerr("type: no argument\n"); - return CR_WRONG_USAGE; - } - con << parts[0]; - string builtin_cmd = getBuiltinCommand(parts[0]); - string lua_path = findScript(parts[0] + ".lua"); - string ruby_path = findScript(parts[0] + ".rb"); - Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); - if (builtin_cmd.size()) - { - con << " is a built-in command"; - if (builtin_cmd != parts[0]) - con << " (aliased to " << builtin_cmd << ")"; - con << std::endl; - } - else if (IsAlias(parts[0])) - { - con << " is an alias: " << GetAliasCommand(parts[0]) << std::endl; - } - else if (plug) - { - con << " is a command implemented by the plugin " << plug->getName() << std::endl; - } - else if (lua_path.size()) - { - con << " is a Lua script: " << lua_path << std::endl; - } - else if (ruby_path.size()) - { - con << " is a Ruby script: " << ruby_path << std::endl; - } - else + Plugin * plug = it->second; + if (!plug) + continue; + if (parts.size() && std::find(parts.begin(), parts.end(), plug->getName()) == parts.end()) + continue; + color_value color; + switch (plug->getState()) { - con << " is not a recognized command." << std::endl; - plug = plug_mgr->getPluginByName(parts[0]); - if (plug) - con << "Plugin " << parts[0] << " exists and implements " << plug->size() << " commands." << std::endl; - return CR_FAILURE; + case Plugin::PS_LOADED: + color = COLOR_RESET; + break; + case Plugin::PS_UNLOADED: + case Plugin::PS_UNLOADING: + color = COLOR_YELLOW; + break; + case Plugin::PS_LOADING: + color = COLOR_LIGHTBLUE; + break; + case Plugin::PS_BROKEN: + color = COLOR_LIGHTRED; + break; + default: + color = COLOR_LIGHTMAGENTA; + break; } + con.color(color); + con.print(row_format, + plug->getName().c_str(), + Plugin::getStateDescription(plug->getState()), + plug->size(), + (plug->can_be_enabled() + ? (plug->is_enabled() ? "enabled" : "disabled") + : "n/a") + ); + con.color(COLOR_RESET); } - else if (builtin == "keybinding") + } + else if (first == "type") + { + if (!parts.size()) { - if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add")) - { - std::string keystr = parts[1]; - if (parts[0] == "set") - ClearKeyBindings(keystr); - for (int i = parts.size()-1; i >= 2; i--) - { - if (!AddKeyBinding(keystr, parts[i])) { - con.printerr("Invalid key spec: %s\n", keystr.c_str()); - break; - } - } - } - else if (parts.size() >= 2 && parts[0] == "clear") - { - for (size_t i = 1; i < parts.size(); i++) - { - if (!ClearKeyBindings(parts[i])) { - con.printerr("Invalid key spec: %s\n", parts[i].c_str()); - break; - } - } - } - else if (parts.size() == 2 && parts[0] == "list") - { - std::vector list = ListKeyBindings(parts[1]); - if (list.empty()) - con << "No bindings." << endl; - for (size_t i = 0; i < list.size(); i++) - con << " " << list[i] << endl; - } - else - { - con << "Usage:" << endl - << " keybinding list " << endl - << " keybinding clear [@context]..." << endl - << " keybinding set [@context] \"cmdline\" \"cmdline\"..." << endl - << " keybinding add [@context] \"cmdline\" \"cmdline\"..." << endl - << "Later adds, and earlier items within one command have priority." << endl - << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, or Enter)." << endl - << "Context may be used to limit the scope of the binding, by" << endl - << "requiring the current context to have a certain prefix." << endl - << "Current UI context is: " - << Gui::getFocusString(Core::getTopViewscreen()) << endl; - } + con.printerr("type: no argument\n"); + return CR_WRONG_USAGE; } - else if (builtin == "alias") + con << parts[0]; + bool builtin = is_builtin(con, parts[0]); + string lua_path = findScript(parts[0] + ".lua"); + string ruby_path = findScript(parts[0] + ".rb"); + Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); + if (builtin) { - if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace")) - { - const string &name = parts[1]; - vector cmd(parts.begin() + 2, parts.end()); - if (!AddAlias(name, cmd, parts[0] == "replace")) - { - con.printerr("Could not add alias %s - already exists\n", name.c_str()); - return CR_FAILURE; - } - } - else if (parts.size() >= 2 && (parts[0] == "delete" || parts[0] == "clear")) + con << " is a built-in command"; + con << std::endl; + } + else if (IsAlias(parts[0])) + { + con << " is an alias: " << GetAliasCommand(parts[0]) << std::endl; + } + else if (plug) + { + con << " is a command implemented by the plugin " << plug->getName() << std::endl; + } + else if (lua_path.size()) + { + con << " is a Lua script: " << lua_path << std::endl; + } + else if (ruby_path.size()) + { + con << " is a Ruby script: " << ruby_path << std::endl; + } + else + { + con << " is not a recognized command." << std::endl; + plug = plug_mgr->getPluginByName(parts[0]); + if (plug) + con << "Plugin " << parts[0] << " exists and implements " << plug->size() << " commands." << std::endl; + return CR_FAILURE; + } + } + else if (first == "keybinding") + { + if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add")) + { + std::string keystr = parts[1]; + if (parts[0] == "set") + ClearKeyBindings(keystr); + for (int i = parts.size()-1; i >= 2; i--) { - if (!RemoveAlias(parts[1])) - { - con.printerr("Could not remove alias %s\n", parts[1].c_str()); - return CR_FAILURE; + if (!AddKeyBinding(keystr, parts[i])) { + con.printerr("Invalid key spec: %s\n", keystr.c_str()); + break; } } - else if (parts.size() >= 1 && (parts[0] == "list")) + } + else if (parts.size() >= 2 && parts[0] == "clear") + { + for (size_t i = 1; i < parts.size(); i++) { - auto aliases = ListAliases(); - for (auto p : aliases) - { - con << p.first << ": " << join_strings(" ", p.second) << endl; + if (!ClearKeyBindings(parts[i])) { + con.printerr("Invalid key spec: %s\n", parts[i].c_str()); + break; } } - else + } + else if (parts.size() == 2 && parts[0] == "list") + { + std::vector list = ListKeyBindings(parts[1]); + if (list.empty()) + con << "No bindings." << endl; + for (size_t i = 0; i < list.size(); i++) + con << " " << list[i] << endl; + } + else + { + con << "Usage:" << endl + << " keybinding list " << endl + << " keybinding clear [@context]..." << endl + << " keybinding set [@context] \"cmdline\" \"cmdline\"..." << endl + << " keybinding add [@context] \"cmdline\" \"cmdline\"..." << endl + << "Later adds, and earlier items within one command have priority." << endl + << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, or Enter)." << endl + << "Context may be used to limit the scope of the binding, by" << endl + << "requiring the current context to have a certain prefix." << endl + << "Current UI context is: " + << Gui::getFocusString(Core::getTopViewscreen()) << endl; + } + } + else if (first == "alias") + { + if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace")) + { + const string &name = parts[1]; + vector cmd(parts.begin() + 2, parts.end()); + if (!AddAlias(name, cmd, parts[0] == "replace")) { - con << "Usage: " << endl - << " alias add|replace " << endl - << " alias delete|clear " << endl - << " alias list" << endl; + con.printerr("Could not add alias %s - already exists\n", name.c_str()); + return CR_FAILURE; } } - else if (builtin == "fpause") + else if (parts.size() >= 2 && (parts[0] == "delete" || parts[0] == "clear")) { - World::SetPauseState(true); - if (auto scr = Gui::getViewscreenByType()) + if (!RemoveAlias(parts[1])) { - scr->worldgen_paused = true; + con.printerr("Could not remove alias %s\n", parts[1].c_str()); + return CR_FAILURE; } - con.print("The game was forced to pause!\n"); } - else if (builtin == "cls") + else if (parts.size() >= 1 && (parts[0] == "list")) { - if (con.is_console()) - ((Console&)con).clear(); - else + auto aliases = ListAliases(); + for (auto p : aliases) { - con.printerr("No console to clear.\n"); - return CR_NEEDS_CONSOLE; + con << p.first << ": " << join_strings(" ", p.second) << endl; } } - else if (builtin == "die") + else { - std::_Exit(666); + con << "Usage: " << endl + << " alias add|replace " << endl + << " alias delete|clear " << endl + << " alias list" << endl; } - else if (builtin == "kill-lua") + } + else if (first == "fpause") + { + World::SetPauseState(true); + if (auto scr = Gui::getViewscreenByType()) { - bool force = false; - for (auto it = parts.begin(); it != parts.end(); ++it) - { - if (*it == "force") - force = true; - } - if (!Lua::Interrupt(force)) - { - con.printerr( - "Failed to register hook. This can happen if you have" - " lua profiling or coverage monitoring enabled. Use" - " 'kill-lua force' to force, but this may disable" - " profiling and coverage monitoring.\n"); - } + scr->worldgen_paused = true; } - else if (builtin == "script") + con.print("The game was forced to pause!\n"); + } + else if (first == "cls" || first == "clear") + { + if (con.is_console()) + ((Console&)con).clear(); + else { - if(parts.size() == 1) - { - loadScriptFile(con, parts[0], false); - } - else - { - con << "Usage:" << endl - << " script " << endl; - return CR_WRONG_USAGE; - } + con.printerr("No console to clear.\n"); + return CR_NEEDS_CONSOLE; } - else if (builtin=="hide") + } + else if (first == "die") + { + std::_Exit(666); + } + else if (first == "kill-lua") + { + bool force = false; + for (auto it = parts.begin(); it != parts.end(); ++it) + { + if (*it == "force") + force = true; + } + if (!Lua::Interrupt(force)) + { + con.printerr( + "Failed to register hook. This can happen if you have" + " lua profiling or coverage monitoring enabled. Use" + " 'kill-lua force' to force, but this may disable" + " profiling and coverage monitoring.\n"); + } + } + else if (first == "script") + { + if(parts.size() == 1) + { + loadScriptFile(con, parts[0], false); + } + else + { + con << "Usage:" << endl + << " script " << endl; + return CR_WRONG_USAGE; + } + } + else if (first == "hide") + { + if (!getConsole().hide()) + { + con.printerr("Could not hide console\n"); + return CR_FAILURE; + } + return CR_OK; + } + else if (first == "show") + { + if (!getConsole().show()) { - if (!getConsole().hide()) + con.printerr("Could not show console\n"); + return CR_FAILURE; + } + return CR_OK; + } + else if (first == "sc-script") + { + if (parts.empty() || parts[0] == "help" || parts[0] == "?") + { + con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; + con << "Valid event names (SC_ prefix is optional):" << endl; + for (int i = SC_WORLD_LOADED; i <= SC_UNPAUSED; i++) { - con.printerr("Could not hide console\n"); - return CR_FAILURE; + std::string name = sc_event_name((state_change_event)i); + if (name != "SC_UNKNOWN") + con << " " << name << endl; } return CR_OK; } - else if (builtin=="show") + else if (parts[0] == "list") { - if (!getConsole().show()) + if(parts.size() < 2) + parts.push_back(""); + if (parts[1].size() && sc_event_id(parts[1]) == SC_UNKNOWN) { - con.printerr("Could not show console\n"); - return CR_FAILURE; + con << "Unrecognized event name: " << parts[1] << endl; + return CR_WRONG_USAGE; + } + for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) + { + if (!parts[1].size() || (it->event == sc_event_id(parts[1]))) + { + con.print("%s (%s): %s%s\n", sc_event_name(it->event).c_str(), + it->save_specific ? "save-specific" : "global", + it->save_specific ? "/raw/" : "/", + it->path.c_str()); + } } return CR_OK; } - else if (builtin == "sc-script") + else if (parts[0] == "add") { - if (parts.empty() || parts[0] == "help" || parts[0] == "?") + if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) { - con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; - con << "Valid event names (SC_ prefix is optional):" << endl; - for (int i = SC_WORLD_LOADED; i <= SC_UNPAUSED; i++) - { - std::string name = sc_event_name((state_change_event)i); - if (name != "SC_UNKNOWN") - con << " " << name << endl; - } - return CR_OK; + con << "Usage: sc-script add EVENT path-to-script [-save]" << endl; + return CR_WRONG_USAGE; } - else if (parts[0] == "list") + state_change_event evt = sc_event_id(parts[1]); + if (evt == SC_UNKNOWN) { - if(parts.size() < 2) - parts.push_back(""); - if (parts[1].size() && sc_event_id(parts[1]) == SC_UNKNOWN) - { - con << "Unrecognized event name: " << parts[1] << endl; - return CR_WRONG_USAGE; - } - for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) - { - if (!parts[1].size() || (it->event == sc_event_id(parts[1]))) - { - con.print("%s (%s): %s%s\n", sc_event_name(it->event).c_str(), - it->save_specific ? "save-specific" : "global", - it->save_specific ? "/raw/" : "/", - it->path.c_str()); - } - } - return CR_OK; + con << "Unrecognized event: " << parts[1] << endl; + return CR_FAILURE; } - else if (parts[0] == "add") + bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); + StateChangeScript script(evt, parts[2], save_specific); + for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) { - if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) - { - con << "Usage: sc-script add EVENT path-to-script [-save]" << endl; - return CR_WRONG_USAGE; - } - state_change_event evt = sc_event_id(parts[1]); - if (evt == SC_UNKNOWN) + if (script == *it) { - con << "Unrecognized event: " << parts[1] << endl; + con << "Script already registered" << endl; return CR_FAILURE; } - bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); - StateChangeScript script(evt, parts[2], save_specific); - for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) - { - if (script == *it) - { - con << "Script already registered" << endl; - return CR_FAILURE; - } - } - state_change_scripts.push_back(script); - return CR_OK; } - else if (parts[0] == "remove") + state_change_scripts.push_back(script); + return CR_OK; + } + else if (parts[0] == "remove") + { + if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) { - if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) - { - con << "Usage: sc-script remove EVENT path-to-script [-save]" << endl; - return CR_WRONG_USAGE; - } - state_change_event evt = sc_event_id(parts[1]); - if (evt == SC_UNKNOWN) - { - con << "Unrecognized event: " << parts[1] << endl; - return CR_FAILURE; - } - bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); - StateChangeScript tmp(evt, parts[2], save_specific); - auto it = std::find(state_change_scripts.begin(), state_change_scripts.end(), tmp); - if (it != state_change_scripts.end()) - { - state_change_scripts.erase(it); - return CR_OK; - } - else - { - con << "Unrecognized script" << endl; - return CR_FAILURE; - } + con << "Usage: sc-script remove EVENT path-to-script [-save]" << endl; + return CR_WRONG_USAGE; + } + state_change_event evt = sc_event_id(parts[1]); + if (evt == SC_UNKNOWN) + { + con << "Unrecognized event: " << parts[1] << endl; + return CR_FAILURE; + } + bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); + StateChangeScript tmp(evt, parts[2], save_specific); + auto it = std::find(state_change_scripts.begin(), state_change_scripts.end(), tmp); + if (it != state_change_scripts.end()) + { + state_change_scripts.erase(it); + return CR_OK; } else { - con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; - return CR_WRONG_USAGE; + con << "Unrecognized script" << endl; + return CR_FAILURE; } } - else if (builtin == "devel/dump-rpc") + else { - if (parts.size() == 1) - { - std::ofstream file(parts[0]); - CoreService core; - core.dumpMethods(file); + con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; + return CR_WRONG_USAGE; + } + } + else if (first == "devel/dump-rpc") + { + if (parts.size() == 1) + { + std::ofstream file(parts[0]); + CoreService core; + core.dumpMethods(file); - for (auto & it : *plug_mgr) - { - Plugin * plug = it.second; - if (!plug) - continue; + for (auto & it : *plug_mgr) + { + Plugin * plug = it.second; + if (!plug) + continue; - std::unique_ptr svc(plug->rpc_connect(con)); - if (!svc) - continue; + std::unique_ptr svc(plug->rpc_connect(con)); + if (!svc) + continue; - file << "// Plugin: " << plug->getName() << endl; - svc->dumpMethods(file); - } - } - else - { - con << "Usage: devel/dump-rpc \"filename\"" << endl; - return CR_WRONG_USAGE; + file << "// Plugin: " << plug->getName() << endl; + svc->dumpMethods(file); } } - else if (RunAlias(con, first, parts, res)) + else { - return res; + con << "Usage: devel/dump-rpc \"filename\"" << endl; + return CR_WRONG_USAGE; } - else + } + else if (RunAlias(con, first, parts, res)) + { + return res; + } + else + { + res = plug_mgr->InvokeCommand(con, first, parts); + if(res == CR_NOT_IMPLEMENTED) { - res = plug_mgr->InvokeCommand(con, first, parts); - if(res == CR_NOT_IMPLEMENTED) + string completed; + string filename = findScript(first + ".lua"); + bool lua = filename != ""; + if ( !lua ) { + filename = findScript(first + ".rb"); + } + if ( lua ) + res = runLuaScript(con, first, parts); + else if ( filename != "" && plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) + res = runRubyScript(con, plug_mgr, filename, parts); + else if ( try_autocomplete(con, first, completed) ) + res = CR_NOT_IMPLEMENTED; + else + con.printerr("%s is not a recognized command.\n", first.c_str()); + if (res == CR_NOT_IMPLEMENTED) { - string completed; - string filename = findScript(first + ".lua"); - bool lua = filename != ""; - if ( !lua ) { - filename = findScript(first + ".rb"); - } - if ( lua ) - res = runLuaScript(con, first, parts); - else if ( filename != "" && plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) - res = runRubyScript(con, plug_mgr, filename, parts); - else if ( try_autocomplete(con, first, completed) ) - res = CR_NOT_IMPLEMENTED; - else - con.printerr("%s is not a recognized command.\n", first.c_str()); - if (res == CR_NOT_IMPLEMENTED) + Plugin *p = plug_mgr->getPluginByName(first); + if (p) { - Plugin *p = plug_mgr->getPluginByName(first); - if (p) - { - con.printerr("%s is a plugin ", first.c_str()); - if (p->getState() == Plugin::PS_UNLOADED) - con.printerr("that is not loaded - try \"load %s\" or check stderr.log\n", - first.c_str()); - else if (p->size()) - con.printerr("that implements %zi commands - see \"ls %s\" for details\n", - p->size(), first.c_str()); - else - con.printerr("but does not implement any commands\n"); - } + con.printerr("%s is a plugin ", first.c_str()); + if (p->getState() == Plugin::PS_UNLOADED) + con.printerr("that is not loaded - try \"load %s\" or check stderr.log\n", + first.c_str()); + else if (p->size()) + con.printerr("that implements %zi commands - see \"ls %s\" for details\n", + p->size(), first.c_str()); + else + con.printerr("but does not implement any commands\n"); } } - else if (res == CR_NEEDS_CONSOLE) - con.printerr("%s needs interactive console to work.\n", first.c_str()); - return res; } - - return CR_OK; + else if (res == CR_NEEDS_CONSOLE) + con.printerr("%s needs interactive console to work.\n", first.c_str()); + return res; } - return CR_NOT_IMPLEMENTED; + return CR_OK; } bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index fb557329d..af8eee3c1 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -148,6 +148,16 @@ void Lua::Push(lua_State *state, df::coord2d pos) lua_setfield(state, -2, "y"); } +void GetVector(lua_State *state, std::vector &pvec) +{ + lua_pushnil(state); // first key + while (lua_next(state, 1) != 0) + { + pvec.push_back(lua_tostring(state, -1)); + lua_pop(state, 1); // remove value, leave key + } +} + int Lua::PushPosXYZ(lua_State *state, df::coord pos) { if (!pos.isValid()) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index df89d184f..6dc5ae0bd 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -339,6 +339,8 @@ namespace DFHack {namespace Lua { } } + DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec); + DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 8856c7ad8..b52f91f22 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -250,6 +250,7 @@ local BUILTINS = { alias='Configure helper aliases for other DFHack commands.', cls='Clear the console screen.', clear='Clear the console screen.', + ['devel/dump-rpc']='Write RPC endpoint information to a file.', die='Force DF to close immediately, without saving.', enable='Enable a plugin or persistent script.', disable='Disable a plugin or persistent script.', @@ -575,15 +576,26 @@ function search_entries(include, exclude) ensure_db() include = normalize_filter(include) exclude = normalize_filter(exclude) - local commands = {} - for command in pairs(db) do - if (not include or matches(command, include)) and - (not exclude or not matches(command, exclude)) then - table.insert(commands, command) + local entries = {} + for entry in pairs(db) do + if (not include or matches(entry, include)) and + (not exclude or not matches(entry, exclude)) then + table.insert(entries, entry) end end - table.sort(commands, sort_by_basename) - return commands + table.sort(entries, sort_by_basename) + return entries +end + +-- returns a list of all commands. used by Core's autocomplete functionality. +function get_commands() + local include = {types={ENTRY_TYPES.COMMAND}} + return search_entries(include) +end + +function is_builtin(command) + ensure_db() + return db[command] and db[command].entry_types[ENTRY_TYPES.BUILTIN] end --------------------------------------------------------------------------- @@ -633,7 +645,7 @@ function list_entries(skip_tags, include, exclude) end end if #entries == 0 then - print('no entries found.') + print('No matches.') end end From 193b9a40040dc52cde722cea4415371681b2ea73 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 14 Jul 2022 14:08:33 -0700 Subject: [PATCH 019/334] add missing namespace which did not cause compiler errors for some reason --- library/LuaApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index af8eee3c1..056a89a07 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -148,7 +148,7 @@ void Lua::Push(lua_State *state, df::coord2d pos) lua_setfield(state, -2, "y"); } -void GetVector(lua_State *state, std::vector &pvec) +void Lua::GetVector(lua_State *state, std::vector &pvec) { lua_pushnil(state); // first key while (lua_next(state, 1) != 0) From dd6fbd53b6498ddcc544fdff3eeffb3f6cdde95a Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 09:07:14 -0700 Subject: [PATCH 020/334] add getEntries() to the CommandHistory API so we can export them to lua also bump the default history size to 5000 from the paltry 100 we had --- library/include/Console.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/include/Console.h b/library/include/Console.h index 0882ba449..39a19b152 100644 --- a/library/include/Console.h +++ b/library/include/Console.h @@ -32,6 +32,7 @@ distribution. #include #include #include +#include namespace tthread { class mutex; @@ -44,7 +45,7 @@ namespace DFHack class CommandHistory { public: - CommandHistory(std::size_t capacity = 100) + CommandHistory(std::size_t capacity = 5000) { this->capacity = capacity; } @@ -114,6 +115,12 @@ namespace DFHack { history.pop_front(); } + /// adds the current list of entries to the given vector + void getEntries(std::vector &entries) + { + for (auto &entry : history) + entries.push_back(entry); + } private: std::size_t capacity; std::deque history; From c9a87511bd46d8e69cf7e816195e70edbf50e66a Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 09:18:27 -0700 Subject: [PATCH 021/334] add dfhack history repository and expose to lua --- docs/Lua API.rst | 16 +++++++++++++++- library/LuaApi.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 55a217757..fb2b18291 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -524,6 +524,20 @@ Input & Output lock. Using an explicit ``dfhack.with_suspend`` will prevent this, forcing the function to block on input with lock held. +* ``dfhack.getCommandHistory(history_id, history_filename)`` + + Returns the list of strings in the specified history. Intended to be used by + GUI scripts that don't have access to a console and so can't use + ``dfhack.lineedit``. The ``history_id`` parameter is some unique string that + the script uses to identify its command history, such as the script's name. If + this is the first time the history with the given ``history_id`` is being + accessed, it is initialized from the given file. + +* ``dfhack.addCommandToHistory(history_id, history_filename, command)`` + + Adds a command to the specified history and saves the updated history to the + specified file. + * ``dfhack.interpreter([prompt[,history_filename[,env]]])`` Starts an interactive lua interpreter, using the specified prompt @@ -837,6 +851,7 @@ can be omitted. * ``dfhack.getGitXmlExpectedCommit()`` * ``dfhack.gitXmlMatch()`` * ``dfhack.isRelease()`` +* ``dfhack.isPrerelease()`` Return information about the DFHack build in use. @@ -890,7 +905,6 @@ can be omitted. from ``dfhack.TranslateName()``), use ``print(dfhack.df2console(text))`` to ensure proper display on all platforms. - * ``dfhack.utf2df(string)`` Convert a string from UTF-8 to DF's CP437 encoding. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 056a89a07..649ceb2dc 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1369,6 +1369,38 @@ static void OpenRandom(lua_State *state) lua_pop(state, 1); } + +/********************************* +* Commandline history repository * +**********************************/ + +static std::map commandHistories; + +static CommandHistory * ensureCommandHistory(std::string id, + std::string src_file) { + if (!commandHistories.count(id)) { + commandHistories[id].load(src_file.c_str()); + } + return &commandHistories[id]; +} + +static int getCommandHistory(lua_State *state) +{ + std::string id = lua_tostring(state, 1); + std::string src_file = lua_tostring(state, 2); + std::vector entries; + ensureCommandHistory(id, src_file)->getEntries(entries); + Lua::PushVector(state, entries); + return 1; +} + +static void addCommandToHistory(std::string id, std::string src_file, + std::string command) { + CommandHistory *history = ensureCommandHistory(id, src_file); + history->add(command); + history->save(src_file.c_str()); +} + /************************ * Wrappers for C++ API * ************************/ @@ -1460,6 +1492,12 @@ static const LuaWrapper::FunctionReg dfhack_module[] = { WRAP_VERSION_FUNC(gitXmlMatch, git_xml_match), WRAP_VERSION_FUNC(isRelease, is_release), WRAP_VERSION_FUNC(isPrerelease, is_prerelease), + WRAP(addCommandToHistory), + { NULL, NULL } +}; + +static const luaL_Reg dfhack_funcs[] = { + { "getCommandHistory", getCommandHistory }, { NULL, NULL } }; @@ -3199,6 +3237,7 @@ void OpenDFHackApi(lua_State *state) OpenRandom(state); LuaWrapper::SetFunctionWrappers(state, dfhack_module); + luaL_setfuncs(state, dfhack_funcs, 0); OpenModule(state, "gui", dfhack_gui_module, dfhack_gui_funcs); OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs); OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs); From f3539f06c8bf22a57acd5bedb7b06b2ff749b436 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 09:44:24 -0700 Subject: [PATCH 022/334] integrate hotkeys with helpdb --- plugins/CMakeLists.txt | 2 +- plugins/hotkeys.cpp | 57 +++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 732673926..af646759f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,7 +130,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(forceequip forceequip.cpp) dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp) dfhack_plugin(getplants getplants.cpp) - dfhack_plugin(hotkeys hotkeys.cpp) + dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) dfhack_plugin(infiniteSky infiniteSky.cpp) dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) dfhack_plugin(jobutils jobutils.cpp) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index e4cd13574..a91c62bdf 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -8,6 +8,7 @@ #include "modules/World.h" #include "modules/Gui.h" +#include "LuaTools.h" #include "PluginManager.h" DFHACK_PLUGIN("hotkeys"); @@ -54,6 +55,8 @@ static void find_active_keybindings(df::viewscreen *screen) valid_keys.push_back("F" + int_to_string(i)); } + valid_keys.push_back("`"); + auto current_focus = Gui::getFocusString(screen); for (int shifted = 0; shifted < 2; shifted++) { @@ -120,6 +123,29 @@ static void invoke_command(const size_t index) } } +static std::string get_help(const std::string &command, bool full_help) +{ + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 2) || + !Lua::PushModulePublic(out, L, "helpdb", + full_help ? "get_entry_long_help" : "get_entry_short_help")) + return "Help text unavailable."; + + Lua::Push(L, command); + + if (!Lua::SafeCall(out, L, 1, 1)) + return "Help text unavailable."; + + const char *s = lua_tostring(L, -1); + if (!s) + return "Help text unavailable."; + + return s; +} + class ViewscreenHotkeys : public dfhack_viewscreen { public: @@ -219,31 +245,16 @@ public: if (first[0] == '#') return; - Plugin *plugin = Core::getInstance().getPluginManager()->getPluginByCommand(first); - if (plugin) + OutputString(COLOR_BROWN, x, y, "Help", true, help_start); + string help_text = get_help(first, show_usage); + vector lines; + split_string(&lines, help_text, "\n"); + for (auto it = lines.begin(); it != lines.end() && y < gps->dimy - 4; it++) { - for (size_t i = 0; i < plugin->size(); i++) + auto wrapped_lines = wrapString(*it, width); + for (auto wit = wrapped_lines.begin(); wit != wrapped_lines.end() && y < gps->dimy - 4; wit++) { - auto pc = plugin->operator[](i); - if (pc.name == first) - { - OutputString(COLOR_BROWN, x, y, "Help", true, help_start); - vector lines; - string help_text = pc.description; - if (show_usage) - help_text += "\n\n" + pc.usage; - - split_string(&lines, help_text, "\n"); - for (auto it = lines.begin(); it != lines.end() && y < gps->dimy - 4; it++) - { - auto wrapped_lines = wrapString(*it, width); - for (auto wit = wrapped_lines.begin(); wit != wrapped_lines.end() && y < gps->dimy - 4; wit++) - { - OutputString(COLOR_WHITE, x, y, *wit, true, help_start); - } - } - break; - } + OutputString(COLOR_WHITE, x, y, *wit, true, help_start); } } } From 9b340a76306e2b5aaebaf83fcd787c3ecf81502a Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 13:46:01 -0700 Subject: [PATCH 023/334] support submit2 for EditFields --- docs/Lua API.rst | 1 + library/lua/gui/widgets.lua | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index fb2b18291..6ebdce99c 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3882,6 +3882,7 @@ Attributes: If it returns false, the character is ignored. :on_change: Change notification callback; used as ``on_change(new_text,old_text)``. :on_submit: Enter key callback; if set the field will handle the key and call ``on_submit(text)``. +:on_submit2: Shift-Enter key callback; if set the field will handle the key and call ``on_submit2(text)``. :key: If specified, the field is disabled until this key is pressed. Must be given as a string. :key_sep: If specified, will be used to customize how the activation key is displayed. See ``token.key_sep`` in the ``Label`` documentation below. diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d5d94ffd9..07ce3422d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -184,6 +184,7 @@ EditField.ATTRS{ on_char = DEFAULT_NIL, on_change = DEFAULT_NIL, on_submit = DEFAULT_NIL, + on_submit2 = DEFAULT_NIL, key = DEFAULT_NIL, key_sep = DEFAULT_NIL, frame = {h=1}, @@ -253,6 +254,17 @@ function EditField:onInput(keys) return not not self.key end + if keys.SEC_SELECT then + if self.key then + self:setFocus(false) + end + if self.on_submit2 then + self.on_submit2(self.text) + return true + end + return not not self.key + end + if keys._STRING then local old = self.text if keys._STRING == 0 then From 585f6aad333557153be659c2466068dd3e3eaf46 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 15:45:03 -0700 Subject: [PATCH 024/334] fix extra space within bold segment for keybindings --- conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf.py b/conf.py index 461fa789b..831833e26 100644 --- a/conf.py +++ b/conf.py @@ -69,7 +69,8 @@ def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, for cmd, key, ctx in KEYBINDS[text]: n = nodes.paragraph() newnode += n - n += nodes.strong('Keybinding: ', 'Keybinding: ') + n += nodes.strong('Keybinding:', 'Keybinding:') + n += nodes.inline(' ', ' ') for k in key: n += nodes.inline(k, k, classes=['kbd']) if cmd != text: From 27425e47f633356135a21a74a6f5f59a1002e4ea Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 15 Jul 2022 22:22:51 -0700 Subject: [PATCH 025/334] support cursor movement in EditFields --- docs/Lua API.rst | 7 ++++ library/lua/gui/widgets.lua | 77 +++++++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 6ebdce99c..e884383d5 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3904,6 +3904,13 @@ and then call the ``on_submit`` callback. Pressing the Escape key will also release keyboard focus, but first it will restore the text that was displayed before the ``EditField`` gained focus and then call the ``on_change`` callback. +The ``EditField`` cursor can be moved to where you want to insert/remove text. +The following cursor movement keys are recognized: + +- Left/Right arrow: move the cursor one character to the left or right. +- Ctrl-Left/Right arrow: move the cursor one word to the left or right. +- Alt-Left/Right arrow: move the cursor to the beginning/end of the text. + Label class ----------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 07ce3422d..d09bdf9cd 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -197,6 +197,8 @@ function EditField:init() self:setFocus(true) end + self.cursor = 1 + self:addviews{HotkeyLabel{frame={t=0,l=0}, key=self.key, key_sep=self.key_sep, @@ -208,6 +210,19 @@ function EditField:getPreferredFocusState() return not self.key end +function EditField:setCursor(cursor) + if not cursor or cursor > #self.text then + self.cursor = #self.text + 1 + return + end + self.cursor = math.max(1, cursor) +end + +function EditField:setText(text, cursor) + self.text = text + self:setCursor(cursor) +end + function EditField:postUpdateLayout() self.text_offset = self.subviews[1]:getTextWidth() end @@ -215,14 +230,29 @@ end function EditField:onRenderBody(dc) dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0) - local cursor = '_' + local cursor_char = '_' if not self.active or not self.focus or gui.blink_visible(300) then - cursor = ' ' + cursor_char = (self.cursor > #self.text) and ' ' or + self.text:sub(self.cursor, self.cursor) end - local txt = self.text .. cursor + local txt = self.text:sub(1, self.cursor - 1) .. cursor_char .. + self.text:sub(self.cursor + 1) local max_width = dc.width - self.text_offset if #txt > max_width then - txt = string.char(27)..string.sub(txt, #txt-max_width+2) + -- get the substring in the vicinity of the cursor + max_width = max_width - 2 + local half_width = math.floor(max_width/2) + local start_pos = math.max(1, self.cursor-half_width) + local end_pos = math.min(#txt, self.cursor+half_width-1) + if self.cursor + half_width > #txt then + start_pos = #txt - max_width + end + if self.cursor - half_width <= 1 then + end_pos = max_width + 1 + end + txt = ('%s%s%s'):format(start_pos == 1 and '' or string.char(27), + txt:sub(start_pos, end_pos), + end_pos == #txt and '' or string.char(26)) end dc:advance(self.text_offset):string(txt) end @@ -252,9 +282,7 @@ function EditField:onInput(keys) return true end return not not self.key - end - - if keys.SEC_SELECT then + elseif keys.SEC_SELECT then if self.key then self:setFocus(false) end @@ -263,17 +291,42 @@ function EditField:onInput(keys) return true end return not not self.key - end - - if keys._STRING then + elseif keys.CURSOR_LEFT then + self.cursor = math.max(1, self.cursor - 1) + return true + elseif keys.A_MOVE_W_DOWN then -- Ctrl-Left (prev word start) + local _, prev_word_start = self.text:sub(1, self.cursor-1): + find('.*[^%w_%-]+[%w_%-]') + self.cursor = prev_word_start or 1 + return true + elseif keys.A_CARE_MOVE_W then -- Alt-Left (home) + self.cursor = 1 + return true + elseif keys.CURSOR_RIGHT then + self.cursor = math.min(self.cursor + 1, #self.text + 1) + return true + elseif keys.A_MOVE_E_DOWN then -- Ctrl-Right (next word end) + local _, next_word_end = self.text:find('[%w_%-]+[^%w_%-]', self.cursor) + self.cursor = next_word_end or #self.text + 1 + return true + elseif keys.A_CARE_MOVE_E then -- Alt-Right (end) + self.cursor = #self.text + 1 + return true + elseif keys._STRING then local old = self.text if keys._STRING == 0 then -- handle backspace - self.text = string.sub(old, 1, #old-1) + local del_pos = self.cursor - 1 + if del_pos > 0 then + self.text = old:sub(1, del_pos-1) .. old:sub(del_pos+1) + self.cursor = del_pos + end else local cv = string.char(keys._STRING) if not self.on_char or self.on_char(cv, old) then - self.text = old .. cv + self.text = old:sub(1, self.cursor-1) .. cv .. + old:sub(self.cursor) + self.cursor = self.cursor + 1 end end if self.on_change and self.text ~= old then From f65f36ffee84e5671b96249426d1e6724e898357 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 16 Jul 2022 22:03:39 -0700 Subject: [PATCH 026/334] move the cursor in an EditField on mouse lclick --- library/lua/gui/widgets.lua | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d09bdf9cd..2dc197064 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -265,7 +265,7 @@ function EditField:onInput(keys) if self.key and keys.LEAVESCREEN then local old = self.text - self.text = self.saved_text + self:setText(self.saved_text) if self.on_change and old ~= self.saved_text then self.on_change(self.text, old) end @@ -291,26 +291,32 @@ function EditField:onInput(keys) return true end return not not self.key + elseif keys._MOUSE_L then + local mouse_x, mouse_y = self:getMousePos() + if mouse_x then + self:setCursor(mouse_x) + return true + end elseif keys.CURSOR_LEFT then - self.cursor = math.max(1, self.cursor - 1) + self:setCursor(self.cursor - 1) return true elseif keys.A_MOVE_W_DOWN then -- Ctrl-Left (prev word start) local _, prev_word_start = self.text:sub(1, self.cursor-1): find('.*[^%w_%-]+[%w_%-]') - self.cursor = prev_word_start or 1 + self:setCursor(prev_word_start or 1) return true elseif keys.A_CARE_MOVE_W then -- Alt-Left (home) - self.cursor = 1 + self:setCursor(1) return true elseif keys.CURSOR_RIGHT then - self.cursor = math.min(self.cursor + 1, #self.text + 1) + self:setCursor(self.cursor + 1) return true elseif keys.A_MOVE_E_DOWN then -- Ctrl-Right (next word end) local _, next_word_end = self.text:find('[%w_%-]+[^%w_%-]', self.cursor) - self.cursor = next_word_end or #self.text + 1 + self:setCursor(next_word_end) return true elseif keys.A_CARE_MOVE_E then -- Alt-Right (end) - self.cursor = #self.text + 1 + self:setCursor() return true elseif keys._STRING then local old = self.text @@ -318,15 +324,14 @@ function EditField:onInput(keys) -- handle backspace local del_pos = self.cursor - 1 if del_pos > 0 then - self.text = old:sub(1, del_pos-1) .. old:sub(del_pos+1) - self.cursor = del_pos + self:setText(old:sub(1, del_pos-1) .. old:sub(del_pos+1), + del_pos) end else local cv = string.char(keys._STRING) if not self.on_char or self.on_char(cv, old) then - self.text = old:sub(1, self.cursor-1) .. cv .. - old:sub(self.cursor) - self.cursor = self.cursor + 1 + self:setText(old:sub(1,self.cursor-1)..cv..old:sub(self.cursor), + self.cursor + 1) end end if self.on_change and self.text ~= old then From 805456e82b568d5601a14e643f8b09040bd66659 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 16 Jul 2022 22:18:38 -0700 Subject: [PATCH 027/334] allow mouse lclick to select a List item --- library/lua/gui/widgets.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 2dc197064..d6505d862 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1019,6 +1019,15 @@ function List:onInput(keys) elseif self.on_submit2 and keys.SEC_SELECT then self:submit2() return true + elseif keys._MOUSE_L then + local _, mouse_y = self:getMousePos() + if mouse_y and #self.choices > 0 and + mouse_y < (#self.choices-self.page_top+1) * self.row_height then + local idx = self.page_top + math.floor(mouse_y/self.row_height) + self:setSelected(idx) + self:submit() + return true + end else for k,v in pairs(self.scroll_keys) do if keys[k] then From f35420072837b32e6a2f563b11c43c6fbc32cbbb Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 16 Jul 2022 22:23:22 -0700 Subject: [PATCH 028/334] update widget docs --- docs/Lua API.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index e884383d5..107a0ba2e 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3904,8 +3904,9 @@ and then call the ``on_submit`` callback. Pressing the Escape key will also release keyboard focus, but first it will restore the text that was displayed before the ``EditField`` gained focus and then call the ``on_change`` callback. -The ``EditField`` cursor can be moved to where you want to insert/remove text. -The following cursor movement keys are recognized: +The ``EditField`` cursor can be moved to where you want to insert/remove text +by clicking the mouse at that position. In addition, the following cursor +movement keys are recognized: - Left/Right arrow: move the cursor one character to the left or right. - Ctrl-Left/Right arrow: move the cursor one word to the left or right. @@ -4135,10 +4136,10 @@ It has the following attributes: :on_select: Selection change callback; called as ``on_select(index,choice)``. This is also called with *nil* arguments if ``setChoices`` is called with an empty list. -:on_submit: Enter key callback; if specified, the list reacts to the key - and calls it as ``on_submit(index,choice)``. -:on_submit2: Shift-Enter key callback; if specified, the list reacts to the key - and calls it as ``on_submit2(index,choice)``. +:on_submit: Enter key or mouse click callback; if specified, the list calls it + as ``on_submit(index,choice)``. +:on_submit2: Shift-Enter key callback; if specified, the list calls it as + ``on_submit2(index,choice)``. :row_height: Height of every row in text lines. :icon_width: If not *nil*, the specified number of character columns are reserved to the left of the list item for the icons. From 28161ed63d11a4ec7b4bb0263801015894357ec3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 17 Jul 2022 09:12:20 -0700 Subject: [PATCH 029/334] add command frequency file based on survey results used for ordering launcher autocomplete results --- dfhack-config/command_counts.json | 145 ++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 dfhack-config/command_counts.json diff --git a/dfhack-config/command_counts.json b/dfhack-config/command_counts.json new file mode 100644 index 000000000..6acfda21b --- /dev/null +++ b/dfhack-config/command_counts.json @@ -0,0 +1,145 @@ +{ + "manipulator": 75, + "autolabor": 59, + "reveal": 51, + "help": 50, + "ls": 50, + "die": 50, + "tags": 50, + "embark-assistant": 42, + "prospect": 37, + "autodump": 36, + "clean": 35, + "gui/workflow": 28, + "workflow": 28, + "exportlegends": 26, + "gui/autobutcher": 25, + "autobutcher": 25, + "digv": 24, + "fastdwarf": 22, + "autonestbox": 20, + "showmood": 19, + "gui/liquids": 18, + "liquids": 18, + "search": 18, + "gui/quickfort": 15, + "quickfort": 15, + "createitem": 14, + "stocks": 14, + "autofarm": 12, + "autochop": 12, + "tiletypes": 12, + "exterminate": 12, + "buildingplan": 12, + "quicksave": 11, + "gui/gm-editor": 11, + "cleanowned": 10, + "gui/autogems": 9, + "autogems": 9, + "stonesense": 9, + "gui/stockpiles": 8, + "stockpiles": 8, + "changevein": 8, + "gui/teleport": 7, + "teleport": 7, + "seedwatch": 6, + "automelt": 6, + "embark-tools": 6, + "cursecheck": 5, + "open-legends": 5, + "ban-cooking": 5, + "burial": 5, + "automaterial": 5, + "remove-stress": 5, + "gui/blueprint": 5, + "blueprint": 5, + "tailor": 4, + "startdwarf": 4, + "3dveins": 4, + "digcircle": 4, + "nestboxes": 3, + "deathcause": 3, + "list-agreements": 3, + "gui/room-list": 3, + "points": 3, + "region-pops": 3, + "gui/advfort": 3, + "unsuspend": 3, + "locate-ore": 3, + "changelayer": 3, + "source": 3, + "gui/gm-unit": 3, + "combine-drinks": 3, + "combine-plants": 3, + "deteriorate": 3, + "warn-starving": 3, + "gaydar": 2, + "gui/dfstatus": 2, + "gui/rename": 2, + "rename": 2, + "fix-ster": 2, + "job-material": 2, + "stockflow": 2, + "drain-aquifer": 2, + "full-heal": 2, + "spawnunit": 2, + "flashstep": 2, + "gui/family-affairs": 2, + "caravan": 2, + "mousequery": 2, + "tweak": 2, + "confirm": 2, + "autoclothing": 1, + "autounsuspend": 1, + "prioritize": 1, + "dwarfmonitor": 1, + "show-unit-syndromes": 1, + "troubleshoot-item": 1, + "gui/mechanisms": 1, + "gui/pathable": 1, + "hotkeys": 1, + "infiniteSky": 1, + "force": 1, + "hermit": 1, + "strangemood": 1, + "weather": 1, + "add-recipe": 1, + "autotrade": 1, + "zone": 1, + "autonick": 1, + "stripcaged": 1, + "unforbid": 1, + "workorder": 1, + "gui/mod-manager": 1, + "spotclean": 1, + "plant": 1, + "regrass": 1, + "dig-now": 1, + "build-now": 1, + "clear-webs": 1, + "gui/siege-engine": 1, + "assign-skills": 1, + "brainwash": 1, + "elevate-mental": 1, + "elevate-physical": 1, + "launch": 1, + "linger": 1, + "make-legendary": 1, + "rejuvenate": 1, + "resurrect-adv": 1, + "questport": 1, + "getplants": 1, + "gui/stamper": 1, + "tweak": 1, + "fixveins": 1, + "deramp": 1, + "fix/dead-units": 1, + "fix/fat-dwarves": 1, + "fix/loyaltycascade": 1, + "fix/retrieve-units": 1, + "tweak": 1, + "sort-items": 1, + "gui/color-schemes": 1, + "color-schemes": 1, + "season-palette": 1 +} From 5723b76585985fff5bef29a2b2702dccbb42b36d Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 17 Jul 2022 15:43:58 -0700 Subject: [PATCH 030/334] click to correct cursor position on long strings where the left side of the string has been trimmed --- library/lua/gui/widgets.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d6505d862..f4573a185 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -197,6 +197,7 @@ function EditField:init() self:setFocus(true) end + self.start_pos = 1 self.cursor = 1 self:addviews{HotkeyLabel{frame={t=0,l=0}, @@ -238,6 +239,7 @@ function EditField:onRenderBody(dc) local txt = self.text:sub(1, self.cursor - 1) .. cursor_char .. self.text:sub(self.cursor + 1) local max_width = dc.width - self.text_offset + self.start_pos = 1 if #txt > max_width then -- get the substring in the vicinity of the cursor max_width = max_width - 2 @@ -245,11 +247,12 @@ function EditField:onRenderBody(dc) local start_pos = math.max(1, self.cursor-half_width) local end_pos = math.min(#txt, self.cursor+half_width-1) if self.cursor + half_width > #txt then - start_pos = #txt - max_width + start_pos = #txt - (max_width - 1) end if self.cursor - half_width <= 1 then end_pos = max_width + 1 end + self.start_pos = start_pos > 1 and start_pos - 1 or start_pos txt = ('%s%s%s'):format(start_pos == 1 and '' or string.char(27), txt:sub(start_pos, end_pos), end_pos == #txt and '' or string.char(26)) @@ -294,7 +297,7 @@ function EditField:onInput(keys) elseif keys._MOUSE_L then local mouse_x, mouse_y = self:getMousePos() if mouse_x then - self:setCursor(mouse_x) + self:setCursor(self.start_pos + mouse_x) return true end elseif keys.CURSOR_LEFT then From 7f302888ec0b5b8a16185d9e507fca431c7d5454 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 17 Jul 2022 16:04:36 -0700 Subject: [PATCH 031/334] make HotkeyLabels react to clicking also be better about initializing EditField frame height --- library/lua/gui/widgets.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index f4573a185..aea62bf87 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -187,10 +187,14 @@ EditField.ATTRS{ on_submit2 = DEFAULT_NIL, key = DEFAULT_NIL, key_sep = DEFAULT_NIL, - frame = {h=1}, modal = false, } +function EditField:preinit(init_table) + local frame = init_table.frame or {} + frame.h = frame.h or 1 +end + function EditField:init() local function on_activate() self.saved_text = self.text @@ -744,6 +748,16 @@ function HotkeyLabel:init() on_activate=self.on_activate}} end +function HotkeyLabel:onInput(keys) + if HotkeyLabel.super.onInput(self, keys) then + return true + elseif keys._MOUSE_L and self:getMousePos() then + self.on_activate() + return true + end + +end + ---------------------- -- CycleHotkeyLabel -- ---------------------- From e650bd094292ab28810b457aec3f9a9c04a78cfc Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 17 Jul 2022 17:05:29 -0700 Subject: [PATCH 032/334] add comment about plugin docs --- library/LuaApi.cpp | 5 ++++- library/include/PluginManager.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 649ceb2dc..c332933a2 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3133,7 +3133,10 @@ static int internal_getCommandHelp(lua_State *L) { help += "."; } - help += "\n" + pc.usage; + if (pc.usage.size()) + { + help += "\n" + pc.usage; + } lua_pushstring(L, help.c_str()); return 1; } diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index f168d7e47..79b7e0492 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -97,6 +97,10 @@ namespace DFHack /// create a command with a name, description, function pointer to its code /// and saying if it needs an interactive terminal /// Most commands shouldn't require an interactive terminal! + /// Note that the description and usage fields are only used for + /// out-of-tree plugins that do not have rendered help installed in + /// the hack/docs directory. Help for all internal plugins comes from + /// the rendered .rst files. PluginCommand(const char * _name, const char * _description, command_function function_, From 301c8e93a19a515a720a69c137a85e7640932290 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 10:58:35 -0700 Subject: [PATCH 033/334] move builtin docs to individual files --- conf.py | 3 + docs/Builtin.rst | 280 ------------------------------- docs/Tags.rst | 2 + docs/builtins/alias.rst | 29 ++++ docs/builtins/clear.rst | 4 + docs/builtins/cls.rst | 5 + docs/builtins/devel/dump-rpc.rst | 8 + docs/builtins/die.rst | 4 + docs/builtins/dir.rst | 4 + docs/builtins/disable.rst | 20 +++ docs/builtins/enable.rst | 20 +++ docs/builtins/fpause.rst | 5 + docs/builtins/help.rst | 19 +++ docs/builtins/hide.rst | 8 + docs/builtins/keybinding.rst | 56 +++++++ docs/builtins/kill-lua.rst | 6 + docs/builtins/load.rst | 13 ++ docs/builtins/ls.rst | 34 ++++ docs/builtins/plug.rst | 12 ++ docs/builtins/reload.rst | 14 ++ docs/builtins/sc-script.rst | 9 + docs/builtins/script.rst | 19 +++ docs/builtins/show.rst | 9 + docs/builtins/tags.rst | 9 + docs/builtins/type.rst | 10 ++ docs/builtins/unload.rst | 11 ++ docs/index-tools.rst | 15 +- index.rst | 20 ++- library/lua/helpdb.lua | 74 ++++---- 29 files changed, 390 insertions(+), 332 deletions(-) delete mode 100644 docs/Builtin.rst create mode 100644 docs/builtins/alias.rst create mode 100644 docs/builtins/clear.rst create mode 100644 docs/builtins/cls.rst create mode 100644 docs/builtins/devel/dump-rpc.rst create mode 100644 docs/builtins/die.rst create mode 100644 docs/builtins/dir.rst create mode 100644 docs/builtins/disable.rst create mode 100644 docs/builtins/enable.rst create mode 100644 docs/builtins/fpause.rst create mode 100644 docs/builtins/help.rst create mode 100644 docs/builtins/hide.rst create mode 100644 docs/builtins/keybinding.rst create mode 100644 docs/builtins/kill-lua.rst create mode 100644 docs/builtins/load.rst create mode 100644 docs/builtins/ls.rst create mode 100644 docs/builtins/plug.rst create mode 100644 docs/builtins/reload.rst create mode 100644 docs/builtins/sc-script.rst create mode 100644 docs/builtins/script.rst create mode 100644 docs/builtins/show.rst create mode 100644 docs/builtins/tags.rst create mode 100644 docs/builtins/type.rst create mode 100644 docs/builtins/unload.rst diff --git a/conf.py b/conf.py index 831833e26..7cf4a3a32 100644 --- a/conf.py +++ b/conf.py @@ -105,6 +105,8 @@ def doc_all_dirs(): # TODO: as we scan the docs, parse out the tags and short descriptions and # build a map for use in generating the tags pages and links in the tool # doc footers + for root, _, files in os.walk('docs/builtins'): + tools.extend(doc_dir(root, files, os.path.relpath(root, 'docs/builtins'))) for root, _, files in os.walk('docs/plugins'): tools.extend(doc_dir(root, files, os.path.relpath(root, 'docs/plugins'))) for root, _, files in os.walk('scripts/docs'): @@ -266,6 +268,7 @@ exclude_patterns = [ 'depends/*', 'docs/html/*', 'docs/text/*', + 'docs/builtins/*', 'docs/plugins/*', 'scripts/docs/*', ] diff --git a/docs/Builtin.rst b/docs/Builtin.rst deleted file mode 100644 index 8e59840b4..000000000 --- a/docs/Builtin.rst +++ /dev/null @@ -1,280 +0,0 @@ -.. _built-in-commands: - -Built-in Commands -================= -The following commands are provided by the 'core' components of DFHack, rather -than plugins or scripts. - -.. contents:: - :local: - -.. _alias: - -alias ------ -The ``alias`` command allows configuring aliases to other DFHack commands. -Aliases are resolved immediately after built-in commands, which means that an -alias cannot override a built-in command, but can override a command implemented -by a plugin or script. - -Usage: - -:``alias list``: lists all configured aliases -:``alias add [arguments...]``: adds an alias -:``alias replace [arguments...]``: replaces an existing - alias with a new command, or adds the alias if it does not already exist -:``alias delete ``: removes the specified alias - -Aliases can be given additional arguments when created and invoked, which will -be passed to the underlying command in order. An example with -`devel/print-args`:: - - [DFHack]# alias add pargs devel/print-args example - [DFHack]# pargs text - example - text - - -.. _cls: - -cls ---- -Clear the terminal. Does not delete command history. - - -.. _die: - -die ---- -Instantly kills DF without saving. - - -.. _disable: -.. _enable: - -enable ------- -Many plugins and scripts can be in a distinct enabled or disabled state. Some of -them activate and deactivate automatically depending on the contents of the -world raws. Others store their state in world data. However a number of them -have to be enabled globally, and the init file is the right place to do it. - -Most such plugins or scripts support the built-in ``enable`` and ``disable`` -commands. Calling them at any time without arguments prints a list of enabled -and disabled plugins, and shows whether that can be changed through the same -commands. Passing plugin names to these commands will enable or disable the -specified plugins. For example, to enable the `manipulator` plugin:: - - enable manipulator - -It is also possible to enable or disable multiple plugins at once:: - - enable manipulator search - - -.. _fpause: - -fpause ------- -Forces DF to pause. This is useful when your FPS drops below 1 and you lose -control of the game. - - -.. _help: - -help ----- -Most commands support using the ``help `` built-in command to retrieve -further help without having to look online. ``? `` and ``man `` are -aliases. - -Some commands (including many scripts) instead take ``help`` or ``?`` as an -option on their command line - ie `` help``. - - -.. _hide: - -hide ----- -Hides the DFHack terminal window. Only available on Windows. - - -.. _keybinding: - -keybinding ----------- -To set keybindings, use the built-in ``keybinding`` command. Like any other -command it can be used at any time from the console, but bindings are not -remembered between runs of the game unless re-created in `dfhack.init`. - -Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or ``\``` -are supported. - -Possible ways to call the command: - -``keybinding list `` - List bindings active for the key combination. -``keybinding clear ...`` - Remove bindings for the specified keys. -``keybinding add "cmdline" "cmdline"...`` - Add bindings for the specified key. -``keybinding set "cmdline" "cmdline"...`` - Clear, and then add bindings for the specified key. - -The ```` parameter above has the following *case-sensitive* syntax:: - - [Ctrl-][Alt-][Shift-]KEY[@context[|context...]] - -where the *KEY* part can be any recognized key and [] denote optional parts. - -When multiple commands are bound to the same key combination, DFHack selects -the first applicable one. Later ``add`` commands, and earlier entries within one -``add`` command have priority. Commands that are not specifically intended for -use as a hotkey are always considered applicable. - -The ``context`` part in the key specifier above can be used to explicitly -restrict the UI state where the binding would be applicable. If called without -parameters, the ``keybinding`` command among other things prints the current -context string. - -Only bindings with a ``context`` tag that either matches the current context -fully, or is a prefix ending at a ``/`` boundary would be considered for -execution, i.e. when in context ``foo/bar/baz``, keybindings restricted to any -of ``@foo/bar/baz``, ``@foo/bar``, ``@foo`` or none will be active. - -Multiple contexts can be specified by separating them with a pipe (``|``) - for -example, ``@foo|bar|baz/foo`` would match anything under ``@foo``, ``@bar``, or -``@baz/foo``. - -Interactive commands like `liquids` cannot be used as hotkeys. - - -.. _kill-lua: - -kill-lua --------- -Stops any currently-running Lua scripts. By default, scripts can only be -interrupted every 256 instructions. Use ``kill-lua force`` to interrupt the next -instruction. - - -.. _load: -.. _unload: -.. _reload: - -load ----- -``load``, ``unload``, and ``reload`` control whether a plugin is loaded into -memory - note that plugins are loaded but disabled unless you explicitly enable -them. Usage:: - - load|unload|reload PLUGIN|(-a|--all) - -Allows dealing with plugins individually by name, or all at once. - -Note that plugins do not maintain their enabled state if they are reloaded, so -you may need to use `enable` to re-enable a plugin after reloading it. - - -.. _ls: -.. _dir: - -ls --- -``ls`` (or ``dir``) does not list files like the Unix command, but rather -available commands. In order to group related commands, each command is -associated with a list of tags. You can filter the listed commands by a -tag or a substring of the command name. Usage: - -:``ls``: Lists all available commands and the tags associated with them - (if any). -:``ls TAG``: Shows only commands that have the given tag. Use the `tags` command - to see the list of available tags. -:``ls STRING``: Shows commands that include the given string. E.g. ``ls auto`` - will show all the commands with "auto" in their names. If the string is also - the name of a tag, then it will be interpreted as a tag name. - -You can also pass some optional parameters to change how ``ls`` behaves: - -:``--notags``: Don't print out the tags associated with each command. -:``--dev``: Include commands intended for developers and modders. - - -.. _plug: - -plug ----- -Lists available plugins and whether they are enabled. - -``plug`` - Lists available plugins (*not* commands implemented by plugins) -``plug [PLUGIN] [PLUGIN] ...`` - List state and detailed description of the given plugins, - including commands implemented by the plugin. - - -.. _sc-script: - -sc-script ---------- -Allows additional scripts to be run when certain events occur (similar to -onLoad\*.init scripts) - - -.. _script: - -script ------- -Reads a text file, and runs each line as a DFHack command as if it had been -typed in by the user - treating the input like `an init file `. - -Some other tools, such as `autobutcher` and `workflow`, export their settings as -the commands to create them - which can later be reloaded with ``script``. - - -.. _show: - -show ----- -Shows the terminal window after it has been `hidden `. Only available on -Windows. You'll need to use it from a `keybinding` set beforehand, or the -in-game `command-prompt`. - - -.. _tags: - -tags ----- - -List the strings that the DFHack tools can be tagged with. You can find groups -of related tools by passing the tag name to `ls`. - -.. _type: - -type ----- -``type command`` shows where ``command`` is implemented. - -.. _devel/dump-rpc: - -devel/dump-rpc --------------- - -Writes RPC endpoint information to the specified file. - -Usage:: - - devel/dump-rpc FILENAME - -Other Commands --------------- -The following commands are *not* built-in, but offer similarly useful functions. - -* `command-prompt` -* `hotkeys` -* `lua` -* `multicmd` -* `nopause` -* `quicksave` -* `rb` -* `repeat` diff --git a/docs/Tags.rst b/docs/Tags.rst index effc0ad23..7a5288cbf 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,3 +1,5 @@ +.. _tags: + Tags ==== diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst new file mode 100644 index 000000000..8ca1f014c --- /dev/null +++ b/docs/builtins/alias.rst @@ -0,0 +1,29 @@ +alias +----- + +Configure helper aliases for other DFHack commands. Aliases are resolved +immediately after built-in commands, which means that an alias cannot override +a built-in command, but can override a command implemented by a plugin or +script. + +Usage: + +- ``alias list`` + lists all configured aliases +- ``alias add [arguments...]`` + adds an alias +- ``alias replace [arguments...]`` + replaces an existing alias with a new command, or adds the alias if it does + not already exist +- ``alias delete `` + removes the specified alias + +Aliases can be given additional arguments when created and invoked, which will +be passed to the underlying command in order. + +Example:: + + [DFHack]# alias add pargs devel/print-args example + [DFHack]# pargs text + example + text diff --git a/docs/builtins/clear.rst b/docs/builtins/clear.rst new file mode 100644 index 000000000..b964a8b3f --- /dev/null +++ b/docs/builtins/clear.rst @@ -0,0 +1,4 @@ +clear +----- + +Clear the terminal screen. This command is an alias for `cls`. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst new file mode 100644 index 000000000..b05354d5a --- /dev/null +++ b/docs/builtins/cls.rst @@ -0,0 +1,5 @@ +cls +--- + +Clear the terminal screen. Can also be invoked as ``clear``. Note that this +command does not delete command history. It just clears the text on the screen. diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst new file mode 100644 index 000000000..af173a179 --- /dev/null +++ b/docs/builtins/devel/dump-rpc.rst @@ -0,0 +1,8 @@ +devel/dump-rpc +-------------- + +Writes RPC endpoint information to the specified file. + +Usage:: + + devel/dump-rpc diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst new file mode 100644 index 000000000..d2dae484f --- /dev/null +++ b/docs/builtins/die.rst @@ -0,0 +1,4 @@ +die +--- + +Instantly exits DF without saving. diff --git a/docs/builtins/dir.rst b/docs/builtins/dir.rst new file mode 100644 index 000000000..5c2059099 --- /dev/null +++ b/docs/builtins/dir.rst @@ -0,0 +1,4 @@ +dir +--- + +List available DFHack commands. This is an alias of the `ls` command. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst new file mode 100644 index 000000000..11077c883 --- /dev/null +++ b/docs/builtins/disable.rst @@ -0,0 +1,20 @@ +disable +------- + +Deactivate a DFHack tool that has some persistent effect. Many plugins and +scripts can be in a distinct enabled or disabled state. Some of them activate +and deactivate automatically depending on the contents of the world raws. Others +store their state in world data. However a number of them have to be enabled +globally, and the init file is the right place to do it. + +Most such plugins or scripts support the built-in ``enable`` and ``disable`` +commands. Calling them at any time without arguments prints a list of enabled +and disabled plugins, and shows whether that can be changed through the same +commands. Passing plugin names to these commands will enable or disable the +specified plugins. For example, to disable the `manipulator` plugin:: + + disable manipulator + +It is also possible to enable or disable multiple plugins at once:: + + disable manipulator search diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst new file mode 100644 index 000000000..eef76c45d --- /dev/null +++ b/docs/builtins/enable.rst @@ -0,0 +1,20 @@ +enable +------ + +Activate a DFHack tool that has some persistent effect. Many plugins and scripts +can be in a distinct enabled or disabled state. Some of them activate and +deactivate automatically depending on the contents of the world raws. Others +store their state in world data. However a number of them have to be enabled +globally, and the init file is the right place to do it. + +Most such plugins or scripts support the built-in ``enable`` and ``disable`` +commands. Calling them at any time without arguments prints a list of enabled +and disabled plugins, and shows whether that can be changed through the same +commands. Passing plugin names to these commands will enable or disable the +specified plugins. For example, to enable the `manipulator` plugin:: + + enable manipulator + +It is also possible to enable or disable multiple plugins at once:: + + enable manipulator search diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst new file mode 100644 index 000000000..68e4eb07e --- /dev/null +++ b/docs/builtins/fpause.rst @@ -0,0 +1,5 @@ +fpause +------ + +Forces DF to pause. This is useful when your FPS drops below 1 and you lose +control of the game. diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst new file mode 100644 index 000000000..aa9e4b170 --- /dev/null +++ b/docs/builtins/help.rst @@ -0,0 +1,19 @@ +help +---- + +Display help about a command or plugin. + +Usage:: + + help|?|man + help|?|man + +Examples:: + + help blueprint + man blueprint + +Both examples above will display the help text for the `blueprint` command. + +Some commands also take ``help`` or ``?`` as an option on their command line +for the same effect - e.g. ``blueprint help``. diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst new file mode 100644 index 000000000..5c8d08452 --- /dev/null +++ b/docs/builtins/hide.rst @@ -0,0 +1,8 @@ +hide +---- + +Hides the DFHack terminal window. You can show it again with the `show` +command, though you'll need to use it from a `keybinding` set beforehand or the +in-game `command-prompt`. + +Only available on Windows. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst new file mode 100644 index 000000000..bce450e46 --- /dev/null +++ b/docs/builtins/keybinding.rst @@ -0,0 +1,56 @@ +keybinding +---------- + +Create hotkeys that will run DFHack commands. Like any other command it can be +used at any time from the console, but bindings are not remembered between runs +of the game unless re-created in `dfhack.init`. + +Hotkeys can be any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or +``\```. + +Usage: + +- ``keybinding`` + Show some useful information, including the current game context. +- ``keybinding list `` + List bindings active for the key combination. +- ``keybinding clear [...]`` + Remove bindings for the specified keys. +- ``keybinding add "cmdline" ["cmdline"...]`` + Add bindings for the specified key. +- ``keybinding set "cmdline" ["cmdline"...]`` + Clear, and then add bindings for the specified key. + +The ```` parameter above has the following **case-sensitive** syntax:: + + [Ctrl-][Alt-][Shift-]KEY[@context[|context...]] + +where the ``KEY`` part can be any recognized key and [] denote optional parts. + +When multiple commands are bound to the same key combination, DFHack selects +the first applicable one. Later ``add`` commands, and earlier entries within one +``add`` command have priority. Commands that are not specifically intended for +use as a hotkey are always considered applicable. + +The ``context`` part in the key specifier above can be used to explicitly +restrict the UI state where the binding would be applicable. + +Only bindings with a ``context`` tag that either matches the current context +fully, or is a prefix ending at a ``/`` boundary would be considered for +execution, i.e. when in context ``foo/bar/baz``, keybindings restricted to any +of ``@foo/bar/baz``, ``@foo/bar``, ``@foo``, or none will be active. + +Multiple contexts can be specified by separating them with a pipe (``|``) - for +example, ``@foo|bar|baz/foo`` would match anything under ``@foo``, ``@bar``, or +``@baz/foo``. + +Interactive commands like `liquids` cannot be used as hotkeys. + +Examples: + +- ``keybinding add Alt-F1 hotkeys`` + Bind Alt-F1 to run the `hotkeys` command on any screen at any time. +- ``keybinding add Alt-F@dwarfmode gui/quickfort`` + Bind Alt-F to run `gui/quickfort`, but only when on a screen that shows the + main map. + diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst new file mode 100644 index 000000000..ee354846a --- /dev/null +++ b/docs/builtins/kill-lua.rst @@ -0,0 +1,6 @@ +kill-lua +-------- + +Gracefully stops any currently-running Lua scripts. Use this command to stop +a misbehaving script that appears to be stuck. Use ``kill-lua force`` if just +``kill-lua`` doesn't work. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst new file mode 100644 index 000000000..fb654b8a4 --- /dev/null +++ b/docs/builtins/load.rst @@ -0,0 +1,13 @@ +load +---- + +Load and register a plugin library. Also see `unload` and `reload` for related +actions. + +Usage:: + + load [ ...] + load -a|--all + +You can load individual named plugins or all plugins at once. Note that plugins +are disabled after loading/reloading until you explicitly `enable` them. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst new file mode 100644 index 000000000..5eaa9dc01 --- /dev/null +++ b/docs/builtins/ls.rst @@ -0,0 +1,34 @@ +ls +-- + +List available DFHack commands. In order to group related commands, each command +is associated with a list of tags. You can filter the listed commands by a tag +or a substring of the command name. The `dir` command is an alias of this +command. + +Usage: + +- ``ls []`` + Lists all available commands and the tags associated with them. +- ``ls []`` + Shows only commands that have the given tag. Use the `tags` command to see + the list of available tags. +- ``ls []`` + Shows commands that include the given string. E.g. ``ls quick`` will show all + the commands with "quick" in their names. If the string is also the name of a + tag, then it will be interpreted as a tag name. + +You can also pass some optional parameters to change how ``ls`` behaves: + +- ``--notags`` + Don't print out the tags associated with each command. +- ``--dev`` + Include commands intended for developers and modders. + +Examples: + +- ``ls adventure`` + Lists all commands with the ``adventure`` tag. +- ``ls --dev trigger`` + Lists all commands, including developer and modding commands, that match the + substring "trigger" diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst new file mode 100644 index 000000000..da0adb657 --- /dev/null +++ b/docs/builtins/plug.rst @@ -0,0 +1,12 @@ +plug +---- + +Lists available plugins and whether they are enabled. + +Usage: + +- ``plug`` + Lists available plugins and whether they are enabled. +- ``plug [ ...]`` + Shows the commands implemented by the named plugins and whether the plugins + are enabled. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst new file mode 100644 index 000000000..8f28e5ca6 --- /dev/null +++ b/docs/builtins/reload.rst @@ -0,0 +1,14 @@ +reload +------ + +Reload a loaded plugin. Developer use this command to reload a plugin that they +are actively modifying. Also see `load` and `unload` for related actions. + +Usage:: + + reload [ ...] + reload -a|--all + +You can reload individual named plugins or all plugins at once. Note that +plugins are disabled after loading/reloading until you explicitly `enable` +them. diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst new file mode 100644 index 000000000..1d5d177c7 --- /dev/null +++ b/docs/builtins/sc-script.rst @@ -0,0 +1,9 @@ +sc-script +--------- + +Runs commands when game state changes occur. This is similar to the static +`init-files` but can be set dynamically. + +Usage:: + + sc-script diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst new file mode 100644 index 000000000..ae2b9f0f4 --- /dev/null +++ b/docs/builtins/script.rst @@ -0,0 +1,19 @@ +script +------ + +Executes a batch file of DFHack commands. It reads a text file and runs each +line as a DFHack command as if it had been typed in by the user - treating the +input like `an init file `. + +Some other tools, such as `autobutcher` and `workflow`, export their settings as +the commands to create them - which can later be reloaded with ``script``. + +Usage:: + + script + +Examples: + +- ``script startup.txt`` + Executes the commands in ``startup.txt``, which exists in your DF game + directory. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst new file mode 100644 index 000000000..8a6f9b446 --- /dev/null +++ b/docs/builtins/show.rst @@ -0,0 +1,9 @@ +show +---- + +Unhides the DFHack terminal window. Useful if you have hidden the terminal with +`hide` and you want it back. Since the terminal window won't be available to run +this command, you'll need to use it from a `keybinding` set beforehand or the +in-game `command-prompt`. + +Only available on Windows. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst new file mode 100644 index 000000000..9de0f8df3 --- /dev/null +++ b/docs/builtins/tags.rst @@ -0,0 +1,9 @@ +tags +---- + +List the strings that DFHack tools can be tagged with. You can find groups of +related tools by passing the tag name to the `ls` command. + +Usage:: + + tags diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst new file mode 100644 index 000000000..c4c752bef --- /dev/null +++ b/docs/builtins/type.rst @@ -0,0 +1,10 @@ +type +---- + +Describes how a command is implemented. DFHack commands can be provided by +plugins, scripts, or by the core library itself. The ``type`` command can tell +you which is the source of a particular command. + +Usage:: + + type diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst new file mode 100644 index 000000000..f4069ed88 --- /dev/null +++ b/docs/builtins/unload.rst @@ -0,0 +1,11 @@ +unload +------ + +Unload a plugin from memory. Also see `load` and `reload` for related actions. + +Usage:: + + unload [ ...] + unload -a|--all + +You can unload individual named plugins or all plugins at once. diff --git a/docs/index-tools.rst b/docs/index-tools.rst index dca14ae3b..a9c540d66 100644 --- a/docs/index-tools.rst +++ b/docs/index-tools.rst @@ -1,24 +1,15 @@ .. _tools-index: -============ -DFHack Tools -============ +================== +DFHack Tools Index +================== These pages contain information about the plugins, scripts, and built-in commands distributed with DFHack. -.. note:: - In order to avoid user confusion, as a matter of policy all GUI tools - display the word :guilabel:`DFHack` on the screen somewhere while active. - - When that is not appropriate because they merely add keybinding hints to - existing DF screens, they deliberately use red instead of green for the key. - .. toctree:: :titlesonly: :glob: - /docs/Tags - /docs/Builtin /docs/tools/* /docs/tools/*/* diff --git a/index.rst b/index.rst index 29ac3c129..3e5cdec5d 100644 --- a/index.rst +++ b/index.rst @@ -30,7 +30,25 @@ User Manual /docs/Installing /docs/Support /docs/Core - /docs/index-tools /docs/guides/index /docs/index-about /docs/index-dev + +Tools +===== + +DFHack commands, plugins, and scripts are grouped by tags to make it easier to +find groups of related tools. + +.. note:: + In order to avoid user confusion, as a matter of policy all GUI tools + display the word :guilabel:`DFHack` on the screen somewhere while active. + + When that is not appropriate because they merely add keybinding hints to + existing DF screens, they deliberately use red instead of green for the key. + +.. toctree:: + :maxdepth: 1 + + /docs/Tags + /docs/index-tools diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index b52f91f22..18ba5b746 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -16,7 +16,6 @@ local _ENV = mkmodule('helpdb') -- paths local RENDERED_PATH = 'hack/docs/docs/tools/' -local BUILTIN_HELP = 'hack/docs/docs/Builtin.txt' local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' -- used when reading help text embedded in script sources @@ -25,6 +24,7 @@ local SCRIPT_DOC_END = ']====]' local SCRIPT_DOC_BEGIN_RUBY = '=begin' local SCRIPT_DOC_END_RUBY = '=end' +-- enums local ENTRY_TYPES = { BUILTIN='builtin', PLUGIN='plugin', @@ -38,6 +38,33 @@ local HELP_SOURCES = { SCRIPT='script', } +-- builtins +local BUILTINS = { + 'alias', + 'clear', + 'cls', + 'devel/dump-rpc', + 'die', + 'dir', + 'disable', + 'enable', + 'fpause', + 'help', + 'hide', + 'keybinding', + 'kill-lua', + 'load', + 'ls', + 'plug', + 'reload', + 'script', + 'sc-script', + 'show', + 'tags', + 'type', + 'unload', +} + -- entry name -> { -- entry_types (set of ENTRY_TYPES), -- short_help (string), @@ -246,46 +273,15 @@ local function update_db(old_db, db, source, entry_name, kwargs) end end -local BUILTINS = { - alias='Configure helper aliases for other DFHack commands.', - cls='Clear the console screen.', - clear='Clear the console screen.', - ['devel/dump-rpc']='Write RPC endpoint information to a file.', - die='Force DF to close immediately, without saving.', - enable='Enable a plugin or persistent script.', - disable='Disable a plugin or persistent script.', - fpause='Force DF to pause.', - help='Usage help for the given plugin, command, or script.', - hide='Hide the terminal window (Windows only).', - keybinding='Modify bindings of commands to in-game key shortcuts.', - ['kill-lua']='Stop a misbehaving Lua script.', - ['load']='Load and register a plugin library.', - unload='Unregister and unload a plugin.', - reload='Unload and reload a plugin library.', - ls='List commands, optionally filtered by a tag or substring.', - dir='List commands, optionally filtered by a tag or substring.', - plug='List plugins and whether they are enabled.', - ['sc-script']='Automatically run specified scripts on state change events.', - script='Run commands specified in a file.', - show='Show a hidden terminal window (Windows only).', - tags='List the tags that the DFHack tools are grouped by.', - ['type']='Discover how a command is implemented.', -} - -- add the builtin commands to the db local function scan_builtins(old_db, db) - local entry = make_default_entry('builtin', - {[ENTRY_TYPES.BUILTIN]=true, [ENTRY_TYPES.COMMAND]=true}, - HELP_SOURCES.RENDERED, 0, BUILTIN_HELP) - -- read in builtin help - local f = io.open(BUILTIN_HELP) - if f then - entry.long_help = f:read('*all') - end - for b,short_help in pairs(BUILTINS) do - local builtin_entry = copyall(entry) - builtin_entry.short_help = short_help - db[b] = builtin_entry + local entry_types = {[ENTRY_TYPES.BUILTIN]=true, [ENTRY_TYPES.COMMAND]=true} + for _,builtin in ipairs(BUILTINS) do + update_db(old_db, db, + has_rendered_help(builtin) and + HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, + builtin, + {entry_types=entry_types}) end end From 019856883bce4c911ee01f2383b48c838ecdb002 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 11:10:56 -0700 Subject: [PATCH 034/334] update sc-script docs based on code spelunking --- docs/builtins/sc-script.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index 1d5d177c7..4c99f3225 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -2,8 +2,19 @@ sc-script --------- Runs commands when game state changes occur. This is similar to the static -`init-files` but can be set dynamically. +`init-files` but is slightly more flexible since it can be set dynamically. -Usage:: +Usage: - sc-script +- ``sc-script [help]`` + Show the list of valid event names. +- ``sc-script list []`` + List the currently registered files for all events or the specified event. +- ``sc-script add|remove [ ...]`` + Register or unregister a file to be run for the specified event. + +Examples: + +- ``sc-script add SC_MAP_LOADED spawn_extra_monsters.init`` + Registers the ``spawn_extra_monsters.init`` file to be run whenever a new map + is loaded. From d27def7128f598a933edbbdf5342e44486414632 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 11:16:05 -0700 Subject: [PATCH 035/334] spacing is important otherwise the usage and examples lists don't put the explanations on the next line --- docs/builtins/alias.rst | 10 +++++----- docs/builtins/disable.rst | 4 ++-- docs/builtins/enable.rst | 4 ++-- docs/builtins/keybinding.rst | 17 ++++++++--------- docs/builtins/ls.rst | 22 +++++++++++----------- docs/builtins/plug.rst | 6 +++--- docs/builtins/sc-script.rst | 10 +++++----- docs/builtins/script.rst | 4 ++-- 8 files changed, 38 insertions(+), 39 deletions(-) diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 8ca1f014c..034d0f33a 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -9,14 +9,14 @@ script. Usage: - ``alias list`` - lists all configured aliases + Lists all configured aliases - ``alias add [arguments...]`` - adds an alias + Adds an alias - ``alias replace [arguments...]`` - replaces an existing alias with a new command, or adds the alias if it does - not already exist + Replaces an existing alias with a new command, or adds the alias if it does + not already exist - ``alias delete `` - removes the specified alias + Removes the specified alias Aliases can be given additional arguments when created and invoked, which will be passed to the underlying command in order. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 11077c883..d871ff06e 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -13,8 +13,8 @@ and disabled plugins, and shows whether that can be changed through the same commands. Passing plugin names to these commands will enable or disable the specified plugins. For example, to disable the `manipulator` plugin:: - disable manipulator + disable manipulator It is also possible to enable or disable multiple plugins at once:: - disable manipulator search + disable manipulator search diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index eef76c45d..8338cc7f4 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -13,8 +13,8 @@ and disabled plugins, and shows whether that can be changed through the same commands. Passing plugin names to these commands will enable or disable the specified plugins. For example, to enable the `manipulator` plugin:: - enable manipulator + enable manipulator It is also possible to enable or disable multiple plugins at once:: - enable manipulator search + enable manipulator search diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index bce450e46..7ab92c206 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -11,15 +11,15 @@ Hotkeys can be any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or Usage: - ``keybinding`` - Show some useful information, including the current game context. + Show some useful information, including the current game context. - ``keybinding list `` - List bindings active for the key combination. + List bindings active for the key combination. - ``keybinding clear [...]`` - Remove bindings for the specified keys. + Remove bindings for the specified keys. - ``keybinding add "cmdline" ["cmdline"...]`` - Add bindings for the specified key. + Add bindings for the specified key. - ``keybinding set "cmdline" ["cmdline"...]`` - Clear, and then add bindings for the specified key. + Clear, and then add bindings for the specified key. The ```` parameter above has the following **case-sensitive** syntax:: @@ -49,8 +49,7 @@ Interactive commands like `liquids` cannot be used as hotkeys. Examples: - ``keybinding add Alt-F1 hotkeys`` - Bind Alt-F1 to run the `hotkeys` command on any screen at any time. + Bind Alt-F1 to run the `hotkeys` command on any screen at any time. - ``keybinding add Alt-F@dwarfmode gui/quickfort`` - Bind Alt-F to run `gui/quickfort`, but only when on a screen that shows the - main map. - + Bind Alt-F to run `gui/quickfort`, but only when on a screen that shows the + main map. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 5eaa9dc01..fd2e532ed 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -9,26 +9,26 @@ command. Usage: - ``ls []`` - Lists all available commands and the tags associated with them. + Lists all available commands and the tags associated with them. - ``ls []`` - Shows only commands that have the given tag. Use the `tags` command to see - the list of available tags. + Shows only commands that have the given tag. Use the `tags` command to see + the list of available tags. - ``ls []`` - Shows commands that include the given string. E.g. ``ls quick`` will show all - the commands with "quick" in their names. If the string is also the name of a - tag, then it will be interpreted as a tag name. + Shows commands that include the given string. E.g. ``ls quick`` will show + all the commands with "quick" in their names. If the string is also the + name of a tag, then it will be interpreted as a tag name. You can also pass some optional parameters to change how ``ls`` behaves: - ``--notags`` - Don't print out the tags associated with each command. + Don't print out the tags associated with each command. - ``--dev`` - Include commands intended for developers and modders. + Include commands intended for developers and modders. Examples: - ``ls adventure`` - Lists all commands with the ``adventure`` tag. + Lists all commands with the ``adventure`` tag. - ``ls --dev trigger`` - Lists all commands, including developer and modding commands, that match the - substring "trigger" + Lists all commands, including developer and modding commands, that match the + substring "trigger" diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index da0adb657..93bd452a7 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -6,7 +6,7 @@ Lists available plugins and whether they are enabled. Usage: - ``plug`` - Lists available plugins and whether they are enabled. + Lists available plugins and whether they are enabled. - ``plug [ ...]`` - Shows the commands implemented by the named plugins and whether the plugins - are enabled. + Shows the commands implemented by the named plugins and whether the plugins + are enabled. diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index 4c99f3225..20a0e9497 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -7,14 +7,14 @@ Runs commands when game state changes occur. This is similar to the static Usage: - ``sc-script [help]`` - Show the list of valid event names. + Show the list of valid event names. - ``sc-script list []`` - List the currently registered files for all events or the specified event. + List the currently registered files for all events or the specified event. - ``sc-script add|remove [ ...]`` - Register or unregister a file to be run for the specified event. + Register or unregister a file to be run for the specified event. Examples: - ``sc-script add SC_MAP_LOADED spawn_extra_monsters.init`` - Registers the ``spawn_extra_monsters.init`` file to be run whenever a new map - is loaded. + Registers the ``spawn_extra_monsters.init`` file to be run whenever a new + map is loaded. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index ae2b9f0f4..ae4017ca7 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -15,5 +15,5 @@ Usage:: Examples: - ``script startup.txt`` - Executes the commands in ``startup.txt``, which exists in your DF game - directory. + Executes the commands in ``startup.txt``, which exists in your DF game + directory. From 2207f2699216a4a7d974ea423c1bb1d2bc5a9ea3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 11:34:58 -0700 Subject: [PATCH 036/334] update docs for 3dveins --- docs/plugins/3dveins.rst | 33 +++++++++++++++++++++++---------- plugins/3dveins.cpp | 9 +++------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 0e6841792..9a074a4c8 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,14 +1,27 @@ 3dveins ======= -Removes all existing veins from the map and generates new ones using -3D Perlin noise, in order to produce a layout that smoothly flows between -Z levels. The vein distribution is based on the world seed, so running -the command for the second time should produce no change. It is best to -run it just once immediately after embark. - -This command is intended as only a cosmetic change, so it takes -care to exactly preserve the mineral counts reported by `prospect` ``all``. -The amounts of different layer stones may slightly change in some cases -if vein mass shifts between Z layers. + +Rewrites layer veins to expand in 3D space. Existing, flat veins are removed +and new 3D veins that naturally span z-levels are generated in their place. +The transformation preserves the mineral counts reported by `prospect`. + +Usage:: + + 3dveins [verbose] + +The ``verbose`` option prints out extra information to the console. + +Example:: + + 3dveins + +New veins are generated using 3D Perlin noise in order to produce a layout that +flows smoothly between z-levels. The vein distribution is based on the world +seed, so running the command for the second time should produce no change. It is +best to run it just once immediately after embark. + +This command is intended as only a cosmetic change, so it takes care to exactly +preserve the mineral counts reported by ``prospect all``. The amounts of layer +stones may slightly change in some cases if vein mass shifts between z layers. The only undo option is to restore your save from backup. diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 929ee24f6..eaf741caf 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -52,12 +52,9 @@ command_result cmd_3dveins(color_ostream &out, std::vector & param DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "3dveins", "Rewrites the veins to make them extend in 3D space.", - cmd_3dveins, false, - " Run this after embark to change all veins on the map to a shape\n" - " that consistently spans Z levels. The operation preserves the\n" - " mineral counts reported by prospect.\n" - )); + "3dveins", + "Rewrites the veins to make them extend in 3D space.", + cmd_3dveins)); return CR_OK; } From 02ba204f5b94eeca18e3c42a5ce4466516ebc02a Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 11:39:52 -0700 Subject: [PATCH 037/334] update docs for add-spatter --- docs/plugins/add-spatter.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 15b99ff52..b6bb97cf7 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -1,6 +1,9 @@ add-spatter =========== -This plugin makes reactions with names starting with ``SPATTER_ADD_`` -produce contaminants on the items instead of improvements. The plugin is -intended to give some use to all those poisons that can be bought from caravans, -so they're immune to being washed away by water or destroyed by `clean`. + +Make tagged reactions produce contaminants. The plugin is intended to give some +use to all those poisons that can be bought from caravans. It automatically +enables itself when you load a world with reactions that include names starting +with ``SPATTER_ADD_``. These reactions will then produce contaminants on items +instead of improvements. The contaminants are immune to being washed away by +water or destroyed by `clean`. From 5ff31e0cc13c5725e5158939b6fcf36b9ecd0a6e Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 12:36:11 -0700 Subject: [PATCH 038/334] remove defunct advtools plugin --- docs/plugins/adv-bodyswap.rst | 12 - plugins/CMakeLists.txt | 1 - plugins/advtools.cpp | 827 ---------------------------------- 3 files changed, 840 deletions(-) delete mode 100644 docs/plugins/adv-bodyswap.rst delete mode 100644 plugins/advtools.cpp diff --git a/docs/plugins/adv-bodyswap.rst b/docs/plugins/adv-bodyswap.rst deleted file mode 100644 index 46b961517..000000000 --- a/docs/plugins/adv-bodyswap.rst +++ /dev/null @@ -1,12 +0,0 @@ -adv-bodyswap -============ -This allows taking control over your followers and other creatures in adventure -mode. For example, you can make them pick up new arms and armor and equip them -properly. - -Usage: - -* When viewing unit details, body-swaps into that unit. -* In the main adventure mode screen, reverts transient swap. - -:dfhack-keybind:`adv-bodyswap` diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index af646759f..b9d63839e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -82,7 +82,6 @@ if(BUILD_SUPPORTED) dfhack_plugin(3dveins 3dveins.cpp) dfhack_plugin(add-spatter add-spatter.cpp) - # dfhack_plugin(advtools advtools.cpp) dfhack_plugin(autochop autochop.cpp) dfhack_plugin(autoclothing autoclothing.cpp) dfhack_plugin(autodump autodump.cpp) diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp deleted file mode 100644 index a1965cba0..000000000 --- a/plugins/advtools.cpp +++ /dev/null @@ -1,827 +0,0 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" -#include "MiscUtils.h" -#include "modules/World.h" -#include "modules/Translation.h" -#include "modules/Materials.h" -#include "modules/Maps.h" -#include "modules/Items.h" -#include "modules/Gui.h" -#include "modules/Units.h" - -#include "DataDefs.h" -#include "df/world.h" -#include "df/ui_advmode.h" -#include "df/item.h" -#include "df/unit.h" -#include "df/unit_inventory_item.h" -#include "df/unit_relationship_type.h" -#include "df/map_block.h" -#include "df/nemesis_record.h" -#include "df/historical_figure.h" -#include "df/general_ref_is_nemesisst.h" -#include "df/general_ref_contains_itemst.h" -#include "df/general_ref_contained_in_itemst.h" -#include "df/general_ref_unit_holderst.h" -#include "df/general_ref_building_civzone_assignedst.h" -#include "df/material.h" -#include "df/craft_material_class.h" -#include "df/viewscreen_optionst.h" -#include "df/viewscreen_dungeonmodest.h" -#include "df/viewscreen_dungeon_monsterstatusst.h" -#include "df/nemesis_flags.h" - -#include - -using namespace DFHack; -using namespace df::enums; - -using df::nemesis_record; -using df::historical_figure; - -using namespace DFHack::Translation; -/* -advtools -======== -A package of different adventure mode tools. Usage: - -:list-equipped [all]: List armor and weapons equipped by your companions. - If all is specified, also lists non-metal clothing. -:metal-detector [all-types] [non-trader]: - Reveal metal armor and weapons in shops. The options - disable the checks on item type and being in shop. -*/ - -DFHACK_PLUGIN("advtools"); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui_advmode); - -/********************* - * PLUGIN INTERFACE * - *********************/ - -static bool bodyswap_hotkey(df::viewscreen *top); - -command_result adv_bodyswap (color_ostream &out, std::vector & parameters); -command_result adv_tools (color_ostream &out, std::vector & parameters); - -DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) -{ - if (!ui_advmode) - return CR_OK; - - commands.push_back(PluginCommand( - "advtools", "Adventure mode tools.", - adv_tools, false, - " list-equipped [all]\n" - " List armor and weapons equipped by your companions.\n" - " If all is specified, also lists non-metal clothing.\n" - " metal-detector [all-types] [non-trader]\n" - " Reveal metal armor and weapons in shops. The options\n" - " disable the checks on item type and being in shop.\n" - )); - - commands.push_back(PluginCommand( - "adv-bodyswap", "Change the adventurer unit.", - adv_bodyswap, bodyswap_hotkey, - " - When viewing unit details, body-swaps into that unit.\n" - " - In the main adventure mode screen, reverts transient swap.\n" - "Options:\n" - " force\n" - " Allow swapping into non-companion units.\n" - " permanent\n" - " Permanently change the unit to be the adventurer.\n" - " Otherwise it will revert if adv-bodyswap is called\n" - " in the main screen, or if the main menu, Fast Travel\n" - " or Sleep/Wait screen is opened.\n" - " noinherit\n" - " In permanent mode, don't reassign companions to the new unit.\n" - )); - - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} - -df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap); - -DFHACK_PLUGIN_IS_ENABLED(in_transient_swap); - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_WORLD_LOADED: - case SC_WORLD_UNLOADED: - in_transient_swap = false; - break; - default: - break; - } - return CR_OK; -} - -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - // Revert transient swaps before trouble happens - if (in_transient_swap) - { - auto screen = Core::getTopViewscreen(); - bool revert = false; - - if (strict_virtual_cast(screen)) - { - using namespace df::enums::ui_advmode_menu; - - switch (ui_advmode->menu) - { - case Travel: - // was also Sleep, now equivalent - revert = true; - break; - default: - break; - } - } - else if (strict_virtual_cast(screen)) - { - // Options may mean save game - revert = true; - } - - if (revert) - { - getPlayerNemesis(out, true); - in_transient_swap = false; - } - } - - return CR_OK; -} - -/********************* - * UTILITY FUNCTIONS * - *********************/ - -static bool bodyswap_hotkey(df::viewscreen *top) -{ - return !!virtual_cast(top) || - !!virtual_cast(top); -} - -bool bodySwap(color_ostream &out, df::unit *player) -{ - if (!player) - { - out.printerr("Unit to swap is NULL\n"); - return false; - } - - auto &vec = world->units.active; - - int idx = linear_index(vec, player); - if (idx < 0) - { - out.printerr("Unit to swap not found: %d\n", player->id); - return false; - } - - if (idx != 0) - std::swap(vec[0], vec[idx]); - - return true; -} - -df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap) -{ - auto real_nemesis = vector_get(world->nemesis.all, ui_advmode->player_id); - if (!real_nemesis || !real_nemesis->unit) - { - out.printerr("Invalid player nemesis id: %d\n", ui_advmode->player_id); - return NULL; - } - - if (restore_swap) - { - df::unit *ctl = world->units.active[0]; - auto ctl_nemesis = Units::getNemesis(ctl); - - if (ctl_nemesis != real_nemesis) - { - if (!bodySwap(out, real_nemesis->unit)) - return NULL; - - auto name = TranslateName(&real_nemesis->unit->name, false); - out.print("Returned into the body of %s.\n", name.c_str()); - } - - real_nemesis->unit->relationship_ids[df::unit_relationship_type::GroupLeader] = -1; - in_transient_swap = false; - } - - return real_nemesis; -} - -void changeGroupLeader(df::nemesis_record *new_nemesis, df::nemesis_record *old_nemesis) -{ - auto &cvec = new_nemesis->companions; - - // Swap companions - cvec.swap(old_nemesis->companions); - - vector_erase_at(cvec, linear_index(cvec, new_nemesis->id)); - insert_into_vector(cvec, old_nemesis->id); - - // Update follow - new_nemesis->group_leader_id = -1; - new_nemesis->unit->relationship_ids[df::unit_relationship_type::GroupLeader] = -1; - - for (unsigned i = 0; i < cvec.size(); i++) - { - auto nm = df::nemesis_record::find(cvec[i]); - if (!nm) - continue; - - nm->group_leader_id = new_nemesis->id; - if (nm->unit) - nm->unit->relationship_ids[df::unit_relationship_type::GroupLeader] = new_nemesis->unit_id; - } -} - -void copyAcquaintances(df::nemesis_record *new_nemesis, df::nemesis_record *old_nemesis) -{ - auto &svec = old_nemesis->unit->adventurer_knows; - auto &tvec = new_nemesis->unit->adventurer_knows; - - for (unsigned i = 0; i < svec.size(); i++) - insert_into_vector(tvec, svec[i]); - - insert_into_vector(tvec, old_nemesis->unit_id); -} - -void sortCompanionNemesis(std::vector *list, int player_id = -1) -{ - std::map table; - std::vector output; - - output.reserve(list->size()); - - if (player_id < 0) - { - auto real_nemesis = vector_get(world->nemesis.all, ui_advmode->player_id); - if (real_nemesis) - player_id = real_nemesis->id; - } - - // Index records; find the player - for (size_t i = 0; i < list->size(); i++) - { - auto item = (*list)[i]; - if (item->id == player_id) - output.push_back(item); - else - table[item->figure->id] = item; - } - - // Pull out the items by the persistent sort order - auto &order_vec = ui_advmode->companions.all_histfigs; - for (size_t i = 0; i < order_vec.size(); i++) - { - auto it = table.find(order_vec[i]); - if (it == table.end()) - continue; - output.push_back(it->second); - table.erase(it); - } - - // The remaining ones in reverse id order - for (auto it = table.rbegin(); it != table.rend(); ++it) - output.push_back(it->second); - - list->swap(output); -} - -void listCompanions(color_ostream &out, std::vector *list, bool units = true) -{ - nemesis_record *player = getPlayerNemesis(out, false); - if (!player) - return; - - list->push_back(player); - - for (size_t i = 0; i < player->companions.size(); i++) - { - auto item = nemesis_record::find(player->companions[i]); - if (item && (item->unit || !units)) - list->push_back(item); - } -} - -std::string getUnitNameProfession(df::unit *unit) -{ - std::string name = TranslateName(&unit->name, false) + ", "; - if (unit->custom_profession.empty()) - name += ENUM_ATTR_STR(profession, caption, unit->profession); - else - name += unit->custom_profession; - return name; -} - -enum InventoryMode { - INV_HAULED, - INV_WEAPON, - INV_WORN, - INV_IN_CONTAINER -}; - -typedef std::pair inv_item; - -static void listContainerInventory(std::vector *list, df::item *container) -{ - auto &refs = container->general_refs; - for (size_t i = 0; i < refs.size(); i++) - { - auto ref = refs[i]; - if (!strict_virtual_cast(ref)) - continue; - - df::item *child = ref->getItem(); - if (!child) continue; - - list->push_back(inv_item(child, INV_IN_CONTAINER)); - listContainerInventory(list, child); - } -} - -void listUnitInventory(std::vector *list, df::unit *unit) -{ - auto &items = unit->inventory; - for (size_t i = 0; i < items.size(); i++) - { - auto item = items[i]; - InventoryMode mode; - - switch (item->mode) { - case df::unit_inventory_item::Hauled: - mode = INV_HAULED; - break; - case df::unit_inventory_item::Weapon: - mode = INV_WEAPON; - break; - default: - mode = INV_WORN; - } - - list->push_back(inv_item(item->item, mode)); - listContainerInventory(list, item->item); - } -} - -bool isShopItem(df::item *item) -{ - for (size_t k = 0; k < item->general_refs.size(); k++) - { - auto ref = item->general_refs[k]; - if (virtual_cast(ref)) - return true; - } - - return false; -} - -bool isWeaponArmor(df::item *item) -{ - using namespace df::enums::item_type; - - switch (item->getType()) { - case HELM: - case ARMOR: - case WEAPON: - case AMMO: - case GLOVES: - case PANTS: - case SHOES: - return true; - default: - return false; - } -} - -int containsMetalItems(df::item *item, bool all, bool non_trader, bool rec = false) -{ - int cnt = 0; - - auto &refs = item->general_refs; - for (size_t i = 0; i < refs.size(); i++) - { - auto ref = refs[i]; - - if (strict_virtual_cast(ref)) - return 0; - if (!rec && strict_virtual_cast(ref)) - return 0; - - if (strict_virtual_cast(ref)) - { - df::item *child = ref->getItem(); - if (!child) continue; - - cnt += containsMetalItems(child, all, non_trader, true); - } - } - - if (!non_trader && !isShopItem(item)) - return cnt; - if (!all && !isWeaponArmor(item)) - return cnt; - - MaterialInfo minfo(item); - if (minfo.getCraftClass() != craft_material_class::Metal) - return cnt; - - return ++cnt; -} - -void joinCounts(std::map &counts) -{ - for (auto it = counts.begin(); it != counts.end(); it++) - { - df::coord pt = it->first; - while (pt.x > 0 && counts.count(pt - df::coord(1,0,0))) - pt.x--; - while (pt.y > 0 &&counts.count(pt - df::coord(0,1,0))) - pt.y--; - while (pt.x < 0 && counts.count(pt + df::coord(1,0,0))) - pt.x++; - while (pt.y < 0 &&counts.count(pt + df::coord(0,1,0))) - pt.y++; - - if (pt == it->first) - continue; - - counts[pt] += it->second; - it->second = 0; - } -} - -/********************* - * FORMATTING * - *********************/ - -static void printCompanionHeader(color_ostream &out, size_t i, df::unit *unit) -{ - out.color(COLOR_GREY); - - if (i < 28) - out << char('a'+i); - else - out << i; - - out << ": " << getUnitNameProfession(unit); - if (Units::isDead(unit)) - out << " (DEAD)"; - if (Units::isGhost(unit)) - out << " (GHOST)"; - out << endl; - - out.reset_color(); -} - -static size_t formatSize(std::vector *out, const std::map in, size_t *cnt) -{ - size_t len = 0; - - for (auto it = in.begin(); it != in.end(); ++it) - { - std::string line = it->first; - if (it->second != 1) - line += stl_sprintf(" [%d]", it->second); - len = std::max(len, line.size()); - out->push_back(line); - } - - if (out->empty()) - { - out->push_back("(none)"); - len = 6; - } - - if (cnt) - *cnt = std::max(*cnt, out->size()); - - return len; -} - -static std::string formatDirection(df::coord delta) -{ - std::string ns, ew, dir; - - if (delta.x > 0) - ew = "E"; - else if (delta.x < 0) - ew = "W"; - - if (delta.y > 0) - ns = "S"; - else if (delta.y < 0) - ns = "N"; - - if (abs(delta.x) > abs(delta.y)*5) - dir = ew; - else if (abs(delta.y) > abs(delta.x)*5) - dir = ns; - else if (abs(delta.x) > abs(delta.y)*2) - dir = ew + ns + ew; - else if (abs(delta.y) > abs(delta.x)*2) - dir = ns + ns + ew; - else if (delta.x || delta.y) - dir = ns + ew; - else - dir = "***"; - - int dist = (int)sqrt((double)(delta.x*delta.x + delta.y*delta.y)); - return stl_sprintf("%d away %s %+d", dist, dir.c_str(), delta.z); -} - -static void printEquipped(color_ostream &out, df::unit *unit, bool all) -{ - std::vector items; - listUnitInventory(&items, unit); - - std::map head, body, legs, weapons; - - for (auto it = items.begin(); it != items.end(); ++it) - { - df::item *item = it->first; - - // Skip non-worn non-weapons - ItemTypeInfo iinfo(item); - - bool is_weapon = (it->second == INV_WEAPON || iinfo.type == item_type::AMMO); - if (!(is_weapon || it->second == INV_WORN)) - continue; - - // Skip non-metal, unless all - MaterialInfo minfo(item); - df::craft_material_class mclass = minfo.getCraftClass(); - - bool is_metal = (mclass == craft_material_class::Metal); - if (!(is_weapon || all || is_metal)) - continue; - - // Format the name - std::string name; - if (is_metal) - name = minfo.toString() + " "; - else if (mclass != craft_material_class::None) - name = toLower(ENUM_KEY_STR(craft_material_class,mclass)) + " "; - name += iinfo.toString(); - - // Add to the right table - int count = item->getStackSize(); - - if (is_weapon) - { - weapons[name] += count; - continue; - } - - switch (iinfo.type) { - case item_type::HELM: - head[name] += count; - break; - case item_type::ARMOR: - case item_type::GLOVES: - case item_type::BACKPACK: - case item_type::QUIVER: - body[name] += count; - break; - case item_type::PANTS: - case item_type::SHOES: - legs[name] += count; - break; - default: - weapons[name] += count; - } - } - - std::vector cols[4]; - size_t sizes[4]; - size_t lines = 0; - - sizes[0] = formatSize(&cols[0], head, &lines); - sizes[1] = formatSize(&cols[1], body, &lines); - sizes[2] = formatSize(&cols[2], legs, &lines); - sizes[3] = formatSize(&cols[3], weapons, &lines); - - for (size_t i = 0; i < lines; i++) - { - for (int j = 0; j < 4; j++) - { - size_t sz = std::max(sizes[j], size_t(18)); - out << "| " << std::left << std::setw(sz) << vector_get(cols[j],i) << " "; - } - - out << "|" << std::endl; - } -} - -/********************* - * COMMANDS * - *********************/ - -command_result adv_bodyswap (color_ostream &out, std::vector & parameters) -{ - // HOTKEY COMMAND; CORE IS SUSPENDED - bool force = false; - bool permanent = false; - bool no_make_leader = false; - - for (unsigned i = 0; i < parameters.size(); i++) - { - auto &item = parameters[i]; - - if (item == "force") - force = true; - else if (item == "permanent") - permanent = true; - else if (item == "noinherit") - no_make_leader = true; - else - return CR_WRONG_USAGE; - } - - // Get the real player; undo previous transient swap - auto real_nemesis = getPlayerNemesis(out, true); - if (!real_nemesis) - return CR_FAILURE; - - // Get the unit to swap to - auto new_unit = Gui::getSelectedUnit(out, true); - auto new_nemesis = Units::getNemesis(new_unit); - - if (!new_nemesis) - { - if (new_unit) - { - out.printerr("Cannot swap into a non-historical unit.\n"); - return CR_FAILURE; - } - - return CR_OK; - } - - if (new_nemesis == real_nemesis) - return CR_OK; - - // Verify it's a companion - if (!force && linear_index(real_nemesis->companions, new_nemesis->id) < 0) - { - out.printerr("This is not your companion - use force to bodyswap.\n"); - return CR_FAILURE; - } - - // Swap - if (!bodySwap(out, new_nemesis->unit)) - return CR_FAILURE; - - auto name = TranslateName(&new_nemesis->unit->name, false); - out.print("Swapped into the body of %s.\n", name.c_str()); - - // Permanently re-link everything - if (permanent) - { - using namespace df::enums::nemesis_flags; - - ui_advmode->player_id = linear_index(world->nemesis.all, new_nemesis); - - // Flag 0 appears to be the 'active adventurer' flag, and - // the player_id field above seems to be computed using it - // when a savegame is loaded. - // Also, unless this is set, it is impossible to retire. - real_nemesis->flags.set(ACTIVE_ADVENTURER, false); - new_nemesis->flags.set(ACTIVE_ADVENTURER, true); - - real_nemesis->flags.set(RETIRED_ADVENTURER, true); // former retired adventurer - new_nemesis->flags.set(ADVENTURER, true); // blue color in legends - - // Reassign companions and acquaintances - if (!no_make_leader) - { - changeGroupLeader(new_nemesis, real_nemesis); - copyAcquaintances(new_nemesis, real_nemesis); - } - } - else - { - in_transient_swap = true; - - // Make the player unit follow around to avoid bad consequences - // if it is unloaded before the transient swap is reverted. - real_nemesis->unit->relationship_ids[df::unit_relationship_type::GroupLeader] = new_nemesis->unit_id; - } - - return CR_OK; -} - -command_result adv_tools (color_ostream &out, std::vector & parameters) -{ - if (parameters.empty()) - return CR_WRONG_USAGE; - - CoreSuspender suspend; - - const auto &command = parameters[0]; - if (command == "list-equipped") - { - bool all = false; - for (size_t i = 1; i < parameters.size(); i++) - { - if (parameters[i] == "all") - all = true; - else - return CR_WRONG_USAGE; - } - - std::vector list; - - listCompanions(out, &list); - sortCompanionNemesis(&list); - - for (size_t i = 0; i < list.size(); i++) - { - auto item = list[i]; - auto unit = item->unit; - - printCompanionHeader(out, i, unit); - printEquipped(out, unit, all); - } - - return CR_OK; - } - else if (command == "metal-detector") - { - bool all = false, non_trader = false; - for (size_t i = 1; i < parameters.size(); i++) - { - if (parameters[i] == "all-types") - all = true; - else if (parameters[i] == "non-trader") - non_trader = true; - else - return CR_WRONG_USAGE; - } - - auto *player = getPlayerNemesis(out, false); - if (!player) - return CR_FAILURE; - - df::coord player_pos = player->unit->pos; - - int total = 0; - std::map counts; - - auto &items = world->items.all; - for (size_t i = 0; i < items.size(); i++) - { - df::item *item = items[i]; - - int num = containsMetalItems(item, all, non_trader); - if (!num) - continue; - - df::map_block *block = Maps::getTileBlock(item->pos); - if (!block) - continue; - - total += num; - counts[(item->pos - player_pos)/10] += num; - - auto &designations = block->designation; - auto &dgn = designations[item->pos.x%16][item->pos.y%16]; - - dgn.bits.hidden = 0; // revealed - dgn.bits.pile = 1; // visible - } - - joinCounts(counts); - - out.print("%d items of metal merchandise found in the vicinity.\n", total); - for (auto it = counts.begin(); it != counts.end(); it++) - { - if (!it->second) - continue; - - df::coord delta = it->first * 10; - out.print(" %s: %d\n", formatDirection(delta).c_str(), it->second); - } - - return CR_OK; - } - else - return CR_WRONG_USAGE; -} From 51c817191ca186a38a237457b895569bc10e7d9d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 12:52:38 -0700 Subject: [PATCH 039/334] update docs for autochop --- docs/plugins/autochop.rst | 22 +++++++++++++--------- plugins/autochop.cpp | 7 +++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 4a669480f..7c517e901 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -1,16 +1,20 @@ autochop ======== -Automatically manage tree cutting designation to keep available logs withing given -quotas. -Open the dashboard by running:: +Auto-harvest trees when low on stockpiled logs. This plugin can designate trees +for chopping when your stocks are low on logs. + +Usage:: enable autochop -The plugin must be activated (with :kbd:`d`-:kbd:`t`-:kbd:`c`-:kbd:`a`) before -it can be used. You can then set logging quotas and restrict designations to -specific burrows (with 'Enter') if desired. The plugin's activity cycle runs -once every in game day. +Then, open the settings menu with :kbd:`c` from the designations menu (the +option appears when you have "Chop Down Trees" selected with :kbd:`d`-:kbd:`t`). + +Set your desired thresholds and enable autochopping with :kbd:`a`. + +You can also restrict autochopping to specific burrows. Highlight a burrow name +with the Up/Down arrow keys and hit :kbd:`Enter` to mark it as the autochop +burrrow. -If you add ``enable autochop`` to your dfhack.init there will be a hotkey to -open the dashboard from the chop designation menu. +Autochop checks your stock of logs and designates trees once every in game day. diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 04ae8af71..d96e7a972 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -935,10 +935,9 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "autochop", "Auto-harvest trees when low on stockpiled logs", - df_autochop, false, - "Opens the automated chopping control screen. Specify 'debug' to forcibly save settings.\n" - )); + "autochop", + "Auto-harvest trees when low on stockpiled logs.", + df_autochop)); initialize(); return CR_OK; From 3969a366ecfa45f11923dcbb594923be87c25fc0 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 13:02:03 -0700 Subject: [PATCH 040/334] update docs for autoclothing --- docs/plugins/autoclothing.rst | 13 +++++++++++-- plugins/autoclothing.cpp | 15 +++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 7169844dd..a3c53a65a 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -1,11 +1,20 @@ autoclothing ============ -Automatically manage clothing work orders, allowing the user to set how many of -each clothing type every citizen should have. Usage:: +Automatically manage clothing work orders. It allows you to set how many of each +clothing type every citizen should have. +Usage:: + + enable autoclothing autoclothing [number] +``material`` can be "cloth", "silk", "yarn", or "leather". The ``item`` can be +anything your civilization can produce, such as "dress" or "mitten". + +When invoked without a number, it shows the current configuration for that +material and item. + Examples: * ``autoclothing cloth "short skirt" 10``: diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index d19e647d5..83eb0398a 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -1,5 +1,3 @@ - -// some headers required for a plugin. Nothing special, just the basics. #include "Core.h" #include #include @@ -184,16 +182,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector [number]\n" - "Example:\n" - " autoclothing cloth \"short skirt\" 10\n" - " Sets the desired number of cloth short skirts available per citizen to 10.\n" - " autoclothing cloth dress\n" - " Displays the currently set number of cloth dresses chosen per citizen.\n" - )); + "autoclothing", + "Automatically manage clothing work orders", + autoclothing)); return CR_OK; } From 25bc59297bdc48e83bf39d4fbb9ac7b3c284ea8d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 13:28:16 -0700 Subject: [PATCH 041/334] update docs for autodump (and related hotkeys) --- docs/plugins/autodump-destroy-here.rst | 10 ++++ docs/plugins/autodump-destroy-item.rst | 13 +++++ docs/plugins/autodump.rst | 81 +++++++++++++++++++++----- plugins/autodump.cpp | 34 ++++------- 4 files changed, 101 insertions(+), 37 deletions(-) create mode 100644 docs/plugins/autodump-destroy-here.rst create mode 100644 docs/plugins/autodump-destroy-item.rst diff --git a/docs/plugins/autodump-destroy-here.rst b/docs/plugins/autodump-destroy-here.rst new file mode 100644 index 000000000..375a799c0 --- /dev/null +++ b/docs/plugins/autodump-destroy-here.rst @@ -0,0 +1,10 @@ +autodump-destroy-here +===================== + +Destroy items marked for dumping under cursor. If called again before the game +is resumed, cancels destruction of the items. This is an alias for the +`autodump` command ``autodump destroy-here``, intended for use as a keybinding. + +Usage:: + + autodump-destroy-here diff --git a/docs/plugins/autodump-destroy-item.rst b/docs/plugins/autodump-destroy-item.rst new file mode 100644 index 000000000..a9f3554c3 --- /dev/null +++ b/docs/plugins/autodump-destroy-item.rst @@ -0,0 +1,13 @@ +autodump-destroy-item +===================== + +Destroy the selected item. The item may be selected in the :kbd:`k` list or in +the container item list. If called again before the game is resumed, cancels +destruction of the item. + +This command is intended for use as a keybinding. See the `autodump` command +for other dumping/destroying options. + +Usage:: + + autodump-destroy-item diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 9bd74cff7..8064583ea 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -1,29 +1,82 @@ autodump ======== -This plugin adds an option to the :kbd:`q` menu for stckpiles when `enabled `. -When autodump is enabled for a stockpile, any items placed in the stockpile will + +Quickly designate or teleport items to be dumped. When `enabled `, this +plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autodump`` +option is selected for the stockpile, any items placed in the stockpile will automatically be designated to be dumped. -Alternatively, you can use it to quickly move all items designated to be dumped. -Items are instantly moved to the cursor position, the dump flag is unset, -and the forbid flag is set, as if it had been dumped normally. -Be aware that any active dump item tasks still point at the item. +When invoked as a command, it can instantly move all items designated to be +dumped to the tile under the cursor. After moving the items, the dump flag is +unset and the forbid flag is set, just as if it had been dumped normally. Be +aware that dwarves that are en route to pick up the item for dumping may still +come and move the item to your dump zone. + +The cursor must be placed on a floor tile so the items can be dumped there. + +Usage:: -Cursor must be placed on a floor tile so the items can be dumped there. + enable autodump + autodump [] Options: -:destroy: Destroy instead of dumping. Doesn't require a cursor. - If called again before the game is resumed, cancels destroy. -:destroy-here: As ``destroy``, but only the selected item in the :kbd:`k` list, - or inside a container. +- ``destroy`` + Destroy instead of dumping. Doesn't require a cursor. If ``autodump`` is + called again with this option before the game is resumed, it cancels + the destroy action. +- ``destroy-here`` + As ``destroy``, but only the selected item in the :kbd:`k` list, or inside a + container. +- ``visible`` + Only process items that are not hidden. +- ``hidden`` + Only process hidden items. +- ``forbidden`` + Only process forbidden items (default: only unforbidden). + +Examples: + +- ``autodump`` + Teleports all unforbidden items marked for dumping to the cursor position. +- ``autodump destroy`` + Destroys all unforbidden items marked for dumping + + Alias ``autodump-destroy-here``, for keybindings. :dfhack-keybind:`autodump-destroy-here` -:visible: Only process items that are not hidden. -:hidden: Only process hidden items. -:forbidden: Only process forbidden items (default: only unforbidden). ``autodump-destroy-item`` destroys the selected item, which may be selected in the :kbd:`k` list, or inside a container. If called again before the game is resumed, cancels destruction of the item. :dfhack-keybind:`autodump-destroy-item` + + + + commands.push_back(PluginCommand( + "autodump", "Teleport items marked for dumping to the cursor.", + df_autodump, false, + " This utility lets you quickly move all items designated to be dumped.\n" + " Items are instantly moved to the cursor position, the dump flag is unset,\n" + " and the forbid flag is set, as if it had been dumped normally.\n" + " Be aware that any active dump item tasks still point at the item.\n" + "Options:\n" + " destroy - instead of dumping, destroy the items instantly.\n" + " destroy-here - only affect the tile under cursor.\n" + " visible - only process items that are not hidden.\n" + " hidden - only process hidden items.\n" + " forbidden - only process forbidden items (default: only unforbidden).\n" + )); + commands.push_back(PluginCommand( + "autodump-destroy-here", "Destroy items marked for dumping under cursor.", + df_autodump_destroy_here, Gui::cursor_hotkey, + " Identical to autodump destroy-here, but intended for use as keybinding.\n" + )); + commands.push_back(PluginCommand( + "autodump-destroy-item", "Destroy the selected item.", + df_autodump_destroy_item, Gui::any_item_hotkey, + " Destroy the selected item. The item may be selected\n" + " in the 'k' list, or inside a container. If called\n" + " again before the game is resumed, cancels destroy.\n" + )); + return CR_OK; diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 054d28f77..fb215c26b 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -275,31 +275,19 @@ command_result df_autodump_destroy_item(color_ostream &out, vector & pa DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "autodump", "Teleport items marked for dumping to the cursor.", - df_autodump, false, - " This utility lets you quickly move all items designated to be dumped.\n" - " Items are instantly moved to the cursor position, the dump flag is unset,\n" - " and the forbid flag is set, as if it had been dumped normally.\n" - " Be aware that any active dump item tasks still point at the item.\n" - "Options:\n" - " destroy - instead of dumping, destroy the items instantly.\n" - " destroy-here - only affect the tile under cursor.\n" - " visible - only process items that are not hidden.\n" - " hidden - only process hidden items.\n" - " forbidden - only process forbidden items (default: only unforbidden).\n" - )); + "autodump", + "Teleport items marked for dumping to the cursor.", + df_autodump)); commands.push_back(PluginCommand( - "autodump-destroy-here", "Destroy items marked for dumping under cursor.", - df_autodump_destroy_here, Gui::cursor_hotkey, - " Identical to autodump destroy-here, but intended for use as keybinding.\n" - )); + "autodump-destroy-here", + "Destroy items marked for dumping under cursor.", + df_autodump_destroy_here, + Gui::cursor_hotkey)); commands.push_back(PluginCommand( - "autodump-destroy-item", "Destroy the selected item.", - df_autodump_destroy_item, Gui::any_item_hotkey, - " Destroy the selected item. The item may be selected\n" - " in the 'k' list, or inside a container. If called\n" - " again before the game is resumed, cancels destroy.\n" - )); + "autodump-destroy-item", + "Destroy the selected item.", + df_autodump_destroy_item, + Gui::any_item_hotkey)); return CR_OK; } From 3ca7997d3e87b45858098419a7ea50eefb337c8f Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 13:49:51 -0700 Subject: [PATCH 042/334] update docs for autofarm --- docs/plugins/autofarm.rst | 41 +++++++++++++++++++++++++-------------- plugins/autofarm.cpp | 18 ++++------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index b330517d0..d5bb12e41 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -1,21 +1,32 @@ autofarm ======== -Automatically handles crop selection in farm plots based on current plant -stocks, and selects crops for planting if current stock is below a threshold. -Selected crops are dispatched on all farmplots. (Note that this plugin replaces -an older Ruby script of the same name.) - -Use the `enable` or `disable ` commands to change whether this plugin is -enabled. +Automatically manage farm crop selection. This plugin periodically scans your +plant stocks and assigns crops to your farm plots based on which plant stocks +are low (as long as you have the appropriate seeds). The target threshold for +each crop type is configurable. Usage: -* ``autofarm runonce``: - Updates all farm plots once, without enabling the plugin -* ``autofarm status``: - Prints status information, including any applied limits -* ``autofarm default 30``: - Sets the default threshold -* ``autofarm threshold 150 helmet_plump tail_pig``: - Sets thresholds of individual plants +- ``enable autofarm`` + Enable the plugin and start managing crop assignment. +* ``autofarm runonce`` + Updates all farm plots once, without enabling the plugin. +* ``autofarm status`` + Prints status information, including any defined thresholds. +* ``autofarm default `` + Sets the default threshold. +* ``autofarm threshold [ ...]`` + Sets thresholds of individual plant types. + +You can find the identifiers for the crop types in your world by running the +following command:: + + lua "for _,plant in ipairs(df.global.world.raws.plants.all) do if plant.flags.SEED then print(plant.id) end end" + +Examples: + +- ``autofarm default 30`` + Set the default threshold to 30. +- ``autofarm threshold 150 MUSHROOM_HELMET_PLUMP GRASS_TAIL_PIG`` + Set the threshold for Plump Helmets and Pig Tails to 150 diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 2dafdee49..1a02b6a06 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -38,15 +38,6 @@ DFHACK_PLUGIN("autofarm"); DFHACK_PLUGIN_IS_ENABLED(enabled); -const char* tagline = "Automatically handle crop selection in farm plots based on current plant stocks."; -const char* usage = ( - "``enable autofarm``: Enables the plugin\n" - "``autofarm runonce``: Updates farm plots (one-time only)\n" - "``autofarm status``: Prints status information\n" - "``autofarm default 30``: Sets the default threshold\n" - "``autofarm threshold 150 helmet_plump tail_pig``: Sets thresholds\n" - ); - class AutoFarm { private: std::map thresholds; @@ -330,7 +321,7 @@ public: void status(color_ostream& out) { - out << (enabled ? "Running." : "Stopped.") << '\n'; + out << "Autofarm is " << (enabled ? "Active." : "Stopped.") << '\n'; for (auto& lc : lastCounts) { auto plant = world->raws.plants.all[lc.first]; @@ -355,10 +346,9 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector ()); return CR_OK; From b1916f16b191e975d56900101c21a2b7d99b2443 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 13:59:11 -0700 Subject: [PATCH 043/334] update docs for autogems --- docs/plugins/autogems-reload.rst | 9 +++++++++ docs/plugins/autogems.rst | 13 +++++++++---- plugins/autogems.cpp | 20 ++------------------ 3 files changed, 20 insertions(+), 22 deletions(-) create mode 100644 docs/plugins/autogems-reload.rst diff --git a/docs/plugins/autogems-reload.rst b/docs/plugins/autogems-reload.rst new file mode 100644 index 000000000..a37117746 --- /dev/null +++ b/docs/plugins/autogems-reload.rst @@ -0,0 +1,9 @@ +autogems-reload +=============== + +Reloads the autogems configuration file. You might need to do this if you have +manually modified the contents while the game is running. + +Usage:: + + autogems-reload diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 59800e1b9..ad3b0f9c3 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,7 +1,12 @@ autogems ======== -Creates a new Workshop Order setting, automatically cutting rough gems -when `enabled `. -See `gui/autogems` for a configuration UI. If necessary, the ``autogems-reload`` -command reloads the configuration file produced by that script. +Automatically cut rough gems. This plugin periodically scans your stocks of +rough gems and creates manager orders for cutting them at a Jeweler's Workshop. + +Usage:: + + enable autogems + +Run `gui/autogems` for a configuration UI, or access the new ``Auto Cut Gems`` +option from the Current Workshop Orders screen (:kbd:`o`-:kbd:`W`). diff --git a/plugins/autogems.cpp b/plugins/autogems.cpp index fcd8fcdc7..d296caf7f 100644 --- a/plugins/autogems.cpp +++ b/plugins/autogems.cpp @@ -41,19 +41,6 @@ typedef int32_t mat_index; typedef std::map gem_map; bool running = false; -const char *tagline = "Creates a new Workshop Order setting, automatically cutting rough gems."; -const char *usage = ( - " enable autogems\n" - " Enable the plugin.\n" - " disable autogems\n" - " Disable the plugin.\n" - "\n" - "While enabled, the Current Workshop Orders screen (o-W) have a new option:\n" - " g: Auto Cut Gems\n" - "\n" - "While this option is enabled, jobs will be created in Jeweler's Workshops\n" - "to cut any accessible rough gems.\n" -); std::set blacklist; void add_task(mat_index gem_type, df::building_workshopst *workshop) { @@ -385,11 +372,8 @@ DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) { DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( "autogems-reload", - "Reload autogems config file", - cmd_reload_config, - false, - "Reload autogems config file" - )); + "Reload autogems config file.", + cmd_reload_config)); return CR_OK; } From 6c760d1a3d5d4f92240e1f996aa282213b3d6052 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 14:33:24 -0700 Subject: [PATCH 044/334] update docs for autohauler --- docs/plugins/autohauler.rst | 59 +++++++++++++++++++++++++++---------- plugins/autohauler.cpp | 42 ++------------------------ 2 files changed, 47 insertions(+), 54 deletions(-) diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 289874f94..4defac18d 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -1,20 +1,49 @@ autohauler ========== -Autohauler is an autolabor fork. -Rather than the all-of-the-above means of autolabor, autohauler will instead -only manage hauling labors and leave skilled labors entirely to the user, who -will probably use Dwarf Therapist to do so. +Automatically manage hauling labors. Similar to `autolabor`, but instead of +managing all labors, ``autohauler`` only addresses hauling labors, leaving the +assignment of skilled labors entirely up to you. You can use the in-game +`manipulator` UI or an external tool like Dwarf Therapist to do so. -Idle dwarves will be assigned the hauling labors; everyone else (including -those currently hauling) will have the hauling labors removed. This is to -encourage every dwarf to do their assigned skilled labors whenever possible, -but resort to hauling when those jobs are not available. This also implies -that the user will have a very tight skill assignment, with most skilled -labors only being assigned to just one dwarf, no dwarf having more than two -active skilled labors, and almost every non-military dwarf having at least -one skilled labor assigned. +Idle dwarves who are not on active military duty will be assigned the hauling +labors; everyone else (including those currently hauling) will have the hauling +labors removed. This is to encourage every dwarf to do their assigned skilled +labors whenever possible, but resort to hauling when those jobs are not +available. This also implies that the user will have a very tight skill +assignment, with most skilled labors only being assigned to just a few dwarves +and almost every non-military dwarf having at least one skilled labor assigned. -Autohauler allows skills to be flagged as to prevent hauling labors from -being assigned when the skill is present. By default this is the unused -ALCHEMIST labor but can be changed by the user. +Autohauler allows a skill to be used as a flag to exempt a dwarf from +``autohauler``'s effects. By default, this is the unused ALCHEMIST labor, but it +can be changed by the user. + +Usage: + +- ``enable autohauler`` + Start managing hauling labors. +- ``autohauler status`` + Show autohauler status and status of fort dwarves. +- ``autohauler haulers`` + Set whether a particular labor should be assigned to haulers. +- ``autohauler allow|forbid`` + Set whether a particular labor should mark a dwarf as exempt from hauling. + By default, only the ``ALCHEMIST`` labor is set to ``forbid``. +- ``autohauler reset-all| reset`` + Reset a particular labor (or all labors) to their default + haulers/allow/forbid state. +- ``autohauler list`` + Show the active configuration for all labors. +- ``autohauler frameskip `` + Set the number of frames between runs of autohauler. +- ``autohauler debug`` + In the next cycle, output the state of every dwarf. + +Examples: +- ``autohauler HAUL_STONE haulers`` + Set stone hauling as a hauling labor (this is already the default). +- ``autohauler RECOVER_WOUNDED allow`` + Allow the "Recover wounded" labor to be manually assigned by the player. By + default it is automatically given to haulers. +- ``autohauler MINE forbid`` + Don't assign hauling labors to dwarves with the Mining labor enabled. diff --git a/plugins/autohauler.cpp b/plugins/autohauler.cpp index 35bf90ca1..b52e9cfb5 100644 --- a/plugins/autohauler.cpp +++ b/plugins/autohauler.cpp @@ -712,45 +712,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector haulers\n" - " Set a labor to be handled by hauler dwarves.\n" - " autohauler allow\n" - " Allow hauling if a specific labor is enabled.\n" - " autohauler forbid\n" - " Forbid hauling if a specific labor is enabled.\n" - " autohauler reset\n" - " Return a labor to the default handling.\n" - " autohauler reset-all\n" - " Return all labors to the default handling.\n" - " autohauler frameskip \n" - " Set the number of frames between runs of autohauler.\n" - " autohauler list\n" - " List current status of all labors.\n" - " autohauler status\n" - " Show basic status information.\n" - " autohauler debug\n" - " In the next cycle, will output the state of every dwarf.\n" - "Function:\n" - " When enabled, autohauler periodically checks your dwarves and assigns\n" - " hauling jobs to idle dwarves while removing them from busy dwarves.\n" - " This plugin, in contrast to autolabor, is explicitly designed to be\n" - " used alongside Dwarf Therapist.\n" - " Warning: autohauler will override any manual changes you make to\n" - " hauling labors while it is enabled...but why would you make them?\n" - "Examples:\n" - " autohauler HAUL_STONE haulers\n" - " Set stone hauling as a hauling labor.\n" - " autohauler BOWYER allow\n" - " Allow hauling when the bowyer labor is enabled.\n" - " autohauler MINE forbid\n" - " Forbid hauling while the mining labor is disabled." - )); + "autohauler", + "Automatically manage hauling labors.", + autohauler)); // Initialize plugin labor lists init_state(out); From 87e67ea8b3d6b3ee6c26d88660ab1931d91d3a0d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 14:53:06 -0700 Subject: [PATCH 045/334] update docs for autolabor --- docs/plugins/autohauler.rst | 3 +- docs/plugins/autolabor.rst | 100 ++++++++++++++++++++---------------- plugins/autolabor.cpp | 45 ++-------------- 3 files changed, 61 insertions(+), 87 deletions(-) diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 4defac18d..d1743f5ef 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -21,7 +21,8 @@ can be changed by the user. Usage: - ``enable autohauler`` - Start managing hauling labors. + Start managing hauling labors. This is normally all you need to do. + Autohauler works well on default settings. - ``autohauler status`` Show autohauler status and status of fort dwarves. - ``autohauler haulers`` diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index b63b9102b..b60146d28 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -1,70 +1,82 @@ autolabor ========= -Automatically manage dwarf labors to efficiently complete jobs. -Autolabor tries to keep as many dwarves as possible busy but -also tries to have dwarves specialize in specific skills. -The key is that, for almost all labors, once a dwarf begins a job it will finish that -job even if the associated labor is removed. Autolabor therefore frequently checks -which dwarf or dwarves should take new jobs for that labor, and sets labors accordingly. -Labors with equipment (mining, hunting, and woodcutting), which are abandoned -if labors change mid-job, are handled slightly differently to minimise churn. +Automatically manage dwarf labors. Autolabor attempts to keep as many dwarves as +possible busy while allowing dwarves to specialize in specific skills. + +Autolabor frequently checks how many jobs of each type are available and sets +labors proportionally in order to get them all done quickly. Labors with +equipment -- mining, hunting, and woodcutting -- which are abandoned if labors +change mid-job, are handled slightly differently to minimise churn. + +Dwarves on active military duty or dwarves assigned to burrows are left +untouched by autolabor. .. warning:: - *autolabor will override any manual changes you make to labors while - it is enabled, including through other tools such as Dwarf Therapist* + autolabor will override any manual changes you make to labors while it is + enabled, including through other tools such as Dwarf Therapist. -Simple usage: +Usage:: -:enable autolabor: Enables the plugin with default settings. (Persistent per fortress) -:disable autolabor: Disables the plugin. + enable autolabor -Anything beyond this is optional - autolabor works well on the default settings. +Anything beyond this is optional - autolabor works well with the default +settings. Once you have enabled it in a fortress, it stays enabled until you +explicitly disable it, even if you save and reload your game. -By default, each labor is assigned to between 1 and 200 dwarves (2-200 for mining). -By default 33% of the workforce become haulers, who handle all hauling jobs as well -as cleaning, pulling levers, recovering wounded, removing constructions, and filling ponds. -Other jobs are automatically assigned as described above. Each of these settings can be adjusted. +By default, each labor is assigned to between 1 and 200 dwarves (2-200 for +mining). 33% of the workforce become haulers, who handle all hauling jobs as +well as cleaning, pulling levers, recovering wounded, removing constructions, +and filling ponds. Other jobs are automatically assigned as described above. +Each of these settings can be adjusted. -Jobs are rarely assigned to nobles with responsibilities for meeting diplomats or merchants, -never to the chief medical dwarf, and less often to the bookeeper and manager. +Jobs are rarely assigned to nobles with responsibilities for meeting diplomats +or merchants, never to the chief medical dwarf, and less often to the bookeeper +and manager. -Hunting is never assigned without a butchery, and fishing is never assigned without a fishery. +Hunting is never assigned without a butchery, and fishing is never assigned +without a fishery. -For each labor a preference order is calculated based on skill, biased against masters of other -trades and excluding those who can't do the job. The labor is then added to the best -dwarves for that labor. We assign at least the minimum number of dwarfs, in order of preference, -and then assign additional dwarfs that meet any of these conditions: +For each labor, a preference order is calculated based on skill, biased against +masters of other trades and excluding those who can't do the job. The labor is +then added to the best dwarves for that labor, then to additional +dwarfs that meet any of these conditions: * The dwarf is idle and there are no idle dwarves assigned to this labor * The dwarf has non-zero skill associated with the labor * The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. -We stop assigning dwarfs when we reach the maximum allowed. +We stop assigning dwarves when we reach the maximum allowed. Advanced usage: -:autolabor []: - Set number of dwarves assigned to a labor. -:autolabor haulers: Set a labor to be handled by hauler dwarves. -:autolabor disable: Turn off autolabor for a specific labor. -:autolabor reset: Return a labor to the default handling. -:autolabor reset-all: Return all labors to the default handling. -:autolabor list: List current status of all labors. -:autolabor status: Show basic status information. +- ``autolabor [] []`` + Set range of dwarves assigned to a labor, optionally specifying the size of + the pool of most skilled dwarves that will ever be considered for this + labor. +- ``autolabor haulers`` + Set a labor to be handled by hauler dwarves. +- ``autolabor disable`` + Turn off autolabor for a specific labor. +- ``autolabor reset-all| reset`` + Return a labor (or all labors) to the default handling. +- ``autolabor list`` + List current status of all labors. +- ``autolabor status`` + Show basic status information. See `autolabor-artisans` for a differently-tuned setup. Examples: -``autolabor MINE`` - Keep at least 5 dwarves with mining enabled. -``autolabor CUT_GEM 1 1`` - Keep exactly 1 dwarf with gemcutting enabled. -``autolabor COOK 1 1 3`` - Keep 1 dwarf with cooking enabled, selected only from the top 3. -``autolabor FEED_WATER_CIVILIANS haulers`` - Have haulers feed and water wounded dwarves. -``autolabor CUTWOOD disable`` - Turn off autolabor for wood cutting. +- ``autolabor MINE 5`` + Keep at least 5 dwarves with mining enabled. +- ``autolabor CUT_GEM 1 1`` + Keep exactly 1 dwarf with gemcutting enabled. +- ``autolabor COOK 1 1 3`` + Keep 1 dwarf with cooking enabled, selected only from the top 3. +- ``autolabor FEED_WATER_CIVILIANS haulers`` + Have haulers feed and water wounded dwarves. +- ``autolabor CUTWOOD disable`` + Turn off autolabor for wood cutting. diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index ba116c1c5..15c6903b4 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -686,48 +686,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector [] []\n" - " Set number of dwarves assigned to a labor.\n" - " autolabor haulers\n" - " Set a labor to be handled by hauler dwarves.\n" - " autolabor disable\n" - " Turn off autolabor for a specific labor.\n" - " autolabor reset\n" - " Return a labor to the default handling.\n" - " autolabor reset-all\n" - " Return all labors to the default handling.\n" - " autolabor list\n" - " List current status of all labors.\n" - " autolabor status\n" - " Show basic status information.\n" - "Function:\n" - " When enabled, autolabor periodically checks your dwarves and enables or\n" - " disables labors. It tries to keep as many dwarves as possible busy but\n" - " also tries to have dwarves specialize in specific skills.\n" - " Warning: autolabor will override any manual changes you make to labors\n" - " while it is enabled.\n" - " To prevent particular dwarves from being managed by autolabor, put them\n" - " in any burrow.\n" - " To restrict the assignment of a labor to only the top most skilled\n" - " dwarves, add a talent pool number .\n" - "Examples:\n" - " autolabor MINE 2\n" - " Keep at least 2 dwarves with mining enabled.\n" - " autolabor CUT_GEM 1 1\n" - " Keep exactly 1 dwarf with gemcutting enabled.\n" - " autolabor COOK 1 1 3\n" - " Keep 1 dwarf with cooking enabled, selected only from the top 3.\n" - " autolabor FEED_WATER_CIVILIANS haulers\n" - " Have haulers feed and water wounded dwarves.\n" - " autolabor CUTWOOD disable\n" - " Turn off autolabor for wood cutting.\n" - )); + "autolabor", + "Automatically manage dwarf labors.", + autolabor)); init_state(); From 0ba3a4684b6cd55cafcb7d773b8000cc4f14bb0f Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:13:04 -0700 Subject: [PATCH 046/334] update docs for automaterial --- docs/plugins/automaterial.rst | 54 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 81de0cb3d..51701a1fc 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -1,33 +1,45 @@ automaterial ============ -This makes building constructions (walls, floors, fortifications, etc) a little bit -easier by saving you from having to trawl through long lists of materials each time -you place one. -Firstly, it moves the last used material for a given construction type to the top of -the list, if there are any left. So if you build a wall with chalk blocks, the next -time you place a wall the chalk blocks will be at the top of the list, regardless of -distance (it only does this in "grouped" mode, as individual item lists could be huge). -This should mean you can place most constructions without having to search for your -preferred material type. +Sorts building materials by recent usage. This makes building constructions +(walls, floors, fortifications, etc) much easier by saving you from having to +trawl through long lists of materials each time you place one. + +It moves the last used material for a given construction type to the top of the +list, if there are any left. So if you build a wall with chalk blocks, the next +time you place a wall the chalk blocks will be at the top of the list, +regardless of distance (it only does this in "grouped" mode, as individual item +lists could be huge). This means you can place most constructions without having +to search for your preferred material type. + +Usage:: + + enable automaterial .. image:: ../images/automaterial-mat.png -Pressing :kbd:`a` while highlighting any material will enable that material for "auto select" -for this construction type. You can enable multiple materials as autoselect. Now the next -time you place this type of construction, the plugin will automatically choose materials -for you from the kinds you enabled. If there is enough to satisfy the whole placement, -you won't be prompted with the material screen - the construction will be placed and you -will be back in the construction menu as if you did it manually. +Pressing :kbd:`a` while highlighting any material will enable that material for +"auto select" for this construction type. You can enable multiple materials. Now +the next time you place this type of construction, the plugin will automatically +choose materials for you from the kinds you enabled. If there is enough to +satisfy the whole placement, you won't be prompted with the material screen at +all -- the construction will be placed and you will be back in the construction +menu. When choosing the construction placement, you will see a couple of options: .. image:: ../images/automaterial-pos.png -Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you need -to go to the material selection screen so you can toggle some materials on or off. +Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you +need to go to the material selection screen so you can toggle some materials on +or off. + +The other option (auto type selection, off by default) can be toggled on with +:kbd:`t`. If you toggle this option on, instead of returning you to the main +construction menu after selecting materials, it returns you back to this screen. +If you use this along with several autoselect enabled materials, you should be +able to place complex constructions more conveniently. -The other option (auto type selection, off by default) can be toggled on with :kbd:`t`. If you -toggle this option on, instead of returning you to the main construction menu after selecting -materials, it returns you back to this screen. If you use this along with several autoselect -enabled materials, you should be able to place complex constructions more conveniently. +The ``automaterial`` plugin also enables extra contruction placement modes, such +as designating areas larger than 10x10 and allowing you to designate hollow +rectangles instead of the default filled ones. From fd84fdce73789f0d0f956c113a1ec02088d21bbe Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:13:20 -0700 Subject: [PATCH 047/334] update docs for automelt --- docs/plugins/automelt.rst | 12 +++++++++--- plugins/automelt.cpp | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 73f8ba502..749aac28a 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -1,5 +1,11 @@ automelt ======== -When automelt is enabled for a stockpile, any meltable items placed -in it will be designated to be melted. -This plugin adds an option to the :kbd:`q` menu when `enabled `. + +Quickly designate items to be melted. When `enabled `, this plugin adds +an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` option is +selected for the stockpile, any items placed in the stockpile will automatically +be designated to be melted. + +Usage:: + + enable automelt diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 852b324d6..e4033ed63 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -297,9 +297,9 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back( - PluginCommand( - "automelt", "Automatically melt metal items in marked stockpiles.", - automelt_cmd, false, "")); + PluginCommand("automelt", + "Automatically melt metal items in marked stockpiles.", + automelt_cmd)); return CR_OK; } From 08154ca1b5c1e0c635ceda48edad9dbaa3e2adee Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:13:27 -0700 Subject: [PATCH 048/334] update docs for autotrade --- docs/plugins/autotrade.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 4f0d95efd..e695a0a53 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -1,5 +1,11 @@ autotrade ========= -When autotrade is enabled for a stockpile, any items placed in it will be -designated to be taken to the Trade Depot whenever merchants are on the map. -This plugin adds an option to the :kbd:`q` menu when `enabled `. + +Quickly designate items to be traded. When `enabled `, this plugin adds +an option to the :kbd:`q` menu for stockpiles. When the ``autotrade`` option is +selected for the stockpile, any items placed in the stockpile will automatically +be designated to be traded. + +Usage:: + + enable autotrade From 2bc6e09ba0183ade4c483b353be0c965c56ab81a Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:20:43 -0700 Subject: [PATCH 049/334] fix formatting typos --- docs/plugins/autodump.rst | 39 ------------------------------------- docs/plugins/autofarm.rst | 8 ++++---- docs/plugins/autohauler.rst | 1 + 3 files changed, 5 insertions(+), 43 deletions(-) diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 8064583ea..83b50e498 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -41,42 +41,3 @@ Examples: Teleports all unforbidden items marked for dumping to the cursor position. - ``autodump destroy`` Destroys all unforbidden items marked for dumping - - - Alias ``autodump-destroy-here``, for keybindings. - :dfhack-keybind:`autodump-destroy-here` - -``autodump-destroy-item`` destroys the selected item, which may be selected -in the :kbd:`k` list, or inside a container. If called again before the game -is resumed, cancels destruction of the item. -:dfhack-keybind:`autodump-destroy-item` - - - - commands.push_back(PluginCommand( - "autodump", "Teleport items marked for dumping to the cursor.", - df_autodump, false, - " This utility lets you quickly move all items designated to be dumped.\n" - " Items are instantly moved to the cursor position, the dump flag is unset,\n" - " and the forbid flag is set, as if it had been dumped normally.\n" - " Be aware that any active dump item tasks still point at the item.\n" - "Options:\n" - " destroy - instead of dumping, destroy the items instantly.\n" - " destroy-here - only affect the tile under cursor.\n" - " visible - only process items that are not hidden.\n" - " hidden - only process hidden items.\n" - " forbidden - only process forbidden items (default: only unforbidden).\n" - )); - commands.push_back(PluginCommand( - "autodump-destroy-here", "Destroy items marked for dumping under cursor.", - df_autodump_destroy_here, Gui::cursor_hotkey, - " Identical to autodump destroy-here, but intended for use as keybinding.\n" - )); - commands.push_back(PluginCommand( - "autodump-destroy-item", "Destroy the selected item.", - df_autodump_destroy_item, Gui::any_item_hotkey, - " Destroy the selected item. The item may be selected\n" - " in the 'k' list, or inside a container. If called\n" - " again before the game is resumed, cancels destroy.\n" - )); - return CR_OK; diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index d5bb12e41..d66830fe0 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -10,13 +10,13 @@ Usage: - ``enable autofarm`` Enable the plugin and start managing crop assignment. -* ``autofarm runonce`` +- ``autofarm runonce`` Updates all farm plots once, without enabling the plugin. -* ``autofarm status`` +- ``autofarm status`` Prints status information, including any defined thresholds. -* ``autofarm default `` +- ``autofarm default `` Sets the default threshold. -* ``autofarm threshold [ ...]`` +- ``autofarm threshold [ ...]`` Sets thresholds of individual plant types. You can find the identifiers for the crop types in your world by running the diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index d1743f5ef..3bdd00950 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -41,6 +41,7 @@ Usage: In the next cycle, output the state of every dwarf. Examples: + - ``autohauler HAUL_STONE haulers`` Set stone hauling as a hauling labor (this is already the default). - ``autohauler RECOVER_WOUNDED allow`` From 154dc7c96c7760dbf5e62eb23bb62ac4bb9eb48a Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:32:43 -0700 Subject: [PATCH 050/334] update docs for blueprint --- docs/plugins/blueprint.rst | 108 ++++++++++++++++++++----------------- plugins/blueprint.cpp | 5 +- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 6d5e306d1..edfc1f096 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -1,7 +1,9 @@ blueprint ========= -The ``blueprint`` command exports the structure of a portion of your fortress in -a blueprint file that you (or anyone else) can later play back with `quickfort`. + +Record a live game map in a quickfort blueprint. With ``blueprint``, you can +export the structure of a portion of your fortress in a blueprint file that you +(or anyone else) can later play back with `quickfort`. Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` subdirectory of your DF folder. The map area to turn into a blueprint is either @@ -9,104 +11,110 @@ selected interactively with the ``blueprint gui`` command or, if the GUI is not used, starts at the active cursor location and extends right and down for the requested width and height. -**Usage:** - - ``blueprint [] [ []] []`` +Usage:: - ``blueprint gui [ []] []`` + blueprint [] [ []] [] + blueprint gui [ []] [] -**Examples:** +Examples: -``blueprint gui`` +- ``blueprint gui`` Runs `gui/blueprint`, the interactive frontend, where all configuration for a ``blueprint`` command can be set visually and interactively. - -``blueprint 30 40 bedrooms`` +- ``blueprint 30 40 bedrooms`` Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting - from the active cursor on the current z-level. Blueprints are written - sequentially to ``bedrooms.csv`` in the ``blueprints`` directory. - -``blueprint 30 40 bedrooms dig --cursor 108,100,150`` + from the active cursor on the current z-level. Blueprints are written to + ``bedrooms.csv`` in the ``blueprints`` directory. +- ``blueprint 30 40 bedrooms dig --cursor 108,100,150`` Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and the start of the blueprint area is set to a specific value instead of using the in-game cursor position. -**Positional Parameters:** +Positional Parameters: -:``width``: Width of the area (in tiles) to translate. -:``height``: Height of the area (in tiles) to translate. -:``depth``: Number of z-levels to translate. Positive numbers go *up* from the - cursor and negative numbers go *down*. Defaults to 1 if not specified, - indicating that the blueprint should only include the current z-level. -:``name``: Base name for blueprint files created in the ``blueprints`` - directory. If no name is specified, "blueprint" is used by default. The - string must contain some characters other than numbers so the name won't be - confused with the optional ``depth`` parameter. +- ``width`` + Width of the area (in tiles) to translate. +- ``height`` + Height of the area (in tiles) to translate. +- ``depth`` + Number of z-levels to translate. Positive numbers go *up* from the cursor + and negative numbers go *down*. Defaults to 1 if not specified, indicating + that the blueprint should only include the current z-level. +- ``name`` + Base name for blueprint files created in the ``blueprints`` directory. If no + name is specified, "blueprint" is used by default. The string must contain + some characters other than numbers so the name won't be confused with the + optional ``depth`` parameter. -**Phases:** +Phases: If you want to generate blueprints only for specific phases, add their names to the commandline, anywhere after the blueprint base name. You can list multiple phases; just separate them with a space. -:``dig``: Generate quickfort ``#dig`` blueprints for digging natural stone. -:``carve``: Generate quickfort ``#dig`` blueprints for smoothing and carving. -:``build``: Generate quickfort ``#build`` blueprints for constructions and - buildings. -:``place``: Generate quickfort ``#place`` blueprints for placing stockpiles. -:``zone``: Generate quickfort ``#zone`` blueprints for designating zones. -:``query``: Generate quickfort ``#query`` blueprints for configuring rooms. +- ``dig`` + Generate quickfort ``#dig`` blueprints for digging natural stone. +- ``carve`` + Generate quickfort ``#dig`` blueprints for smoothing and carving. +- ``build`` + Generate quickfort ``#build`` blueprints for constructions and buildings. +- ``place`` + Generate quickfort ``#place`` blueprints for placing stockpiles. +- ``zone`` + Generate quickfort ``#zone`` blueprints for designating zones. +- ``query`` + Generate quickfort ``#query`` blueprints for configuring rooms. If no phases are specified, phases are autodetected. For example, a ``#place`` blueprint will be created only if there are stockpiles in the blueprint area. -**Options:** +Options: -``-c``, ``--cursor ,,``: +- ``-c``, ``--cursor ,,`` Use the specified map coordinates instead of the current cursor position for the upper left corner of the blueprint range. If this option is specified, then an active game map cursor is not necessary. -``-e``, ``--engrave``: +- ``-e``, ``--engrave`` Record engravings in the ``carve`` phase. If this option is not specified, engravings are ignored. -``-f``, ``--format ``: +- ``-f``, ``--format `` Select the output format of the generated files. See the ``Output formats`` section below for options. If not specified, the output format defaults to "minimal", which will produce a small, fast ``.csv`` file. -``-h``, ``--help``: +- ``-h``, ``--help`` Show command help text. -``-s``, ``--playback-start ,,``: +- ``-s``, ``--playback-start ,,`` Specify the column and row offsets (relative to the upper-left corner of the blueprint, which is ``1,1``) where the player should put the cursor when the blueprint is played back with `quickfort`, in `quickfort start marker ` format, for example: ``10,10,central stairs``. If there is a space in the comment, you will need - to surround the parameter string in double quotes: ``"-s10,10,central stairs"`` or - ``--playback-start "10,10,central stairs"`` or - ``"--playback-start=10,10,central stairs"``. -``-t``, ``--splitby ``: + to surround the parameter string in double quotes: + ``"-s10,10,central stairs"`` or ``--playback-start "10,10,central stairs"`` + or ``"--playback-start=10,10,central stairs"``. +- ``-t``, ``--splitby `` Split blueprints into multiple files. See the ``Splitting output into multiple files`` section below for details. If not specified, defaults to "none", which will create a standard quickfort `multi-blueprint ` file. -**Output formats:** +Output formats: Here are the values that can be passed to the ``--format`` flag: -:``minimal``: +- ``minimal`` Creates ``.csv`` files with minimal file size that are fast to read and write. This is the default. -:``pretty``: - Makes the blueprints in the ``.csv`` files easier to read and edit with a text - editor by adding extra spacing and alignment markers. +- ``pretty`` + Makes the blueprints in the ``.csv`` files easier to read and edit with a + text editor by adding extra spacing and alignment markers. -**Splitting output into multiple files:** +Splitting output into multiple files: The ``--splitby`` flag can take any of the following values: -:``none``: +- ``none`` Writes all blueprints into a single file. This is the standard format for quickfort fortress blueprint bundles and is the default. -:``phase``: +- ``phase`` Creates a separate file for each phase. diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 1401333eb..3a0cb60b4 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -118,7 +118,10 @@ struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::all command_result blueprint(color_ostream &, vector &); DFhackCExport command_result plugin_init(color_ostream &, vector &commands) { - commands.push_back(PluginCommand("blueprint", "Record the structure of a live game map in a quickfort blueprint file", blueprint, false)); + commands.push_back( + PluginCommand("blueprint", + "Record a live game map in a quickfort blueprint.", + blueprint)); return CR_OK; } From 629c22b148422916bd2544d5ff03b7972b4f40cf Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:38:28 -0700 Subject: [PATCH 051/334] update docs for building-hacks --- docs/Lua API.rst | 6 +++--- docs/plugins/building-hacks.rst | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 docs/plugins/building-hacks.rst diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 107a0ba2e..aba0bcab7 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -4287,10 +4287,10 @@ blueprint files: The names of the functions are also available as the keys of the ``valid_phases`` table. -.. _building-hacks: +.. _building-hacks-api: -building-hacks -============== +building-hacks API +================== This plugin overwrites some methods in workshop df class so that mechanical workshops are possible. Although plugin export a function it's recommended to use lua decorated function. diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst new file mode 100644 index 000000000..950d1751a --- /dev/null +++ b/docs/plugins/building-hacks.rst @@ -0,0 +1,11 @@ +building-hacks +============== + +Allows mods to create and manage powered workshops. + +Usage:: + + enable building-hacks + +Please see the `building-hacks-api` for information on accessing this plugin +from Lua scripts. From 19a4accca4c247ccd2f6c8999df5154d2cab0e4b Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 16:58:01 -0700 Subject: [PATCH 052/334] update docs for bulidingplan --- docs/plugins/buildingplan.rst | 136 +++++++++++++++++----------------- plugins/buildingplan.cpp | 6 +- 2 files changed, 70 insertions(+), 72 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index ca6273d10..4b21da8af 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -1,88 +1,86 @@ buildingplan ============ -When active (via ``enable buildingplan``), this plugin adds a planning mode for -building placement. You can then place furniture, constructions, and other buildings -before the required materials are available, and they will be created in a suspended -state. Buildingplan will periodically scan for appropriate items, and the jobs will -be unsuspended when the items are available. -This is very useful when combined with `workflow` - you can set a constraint -to always have one or two doors/beds/tables/chairs/etc available, and place -as many as you like. The plugins then take over and fulfill the orders, -with minimal space dedicated to stockpiles. +Plan building construction before you have materials. This plugin adds a +planning mode for building placement. You can then place furniture, +constructions, and other buildings before the required materials are available, +and they will be created in a suspended state. Buildingplan will periodically +scan for appropriate items, and the jobs will be unsuspended when the items are +available. -.. _buildingplan-filters: - -Item filtering --------------- - -While placing a building, you can set filters for what materials you want the building made -out of, what quality you want the component items to be, and whether you want the items to -be decorated. - -If a building type takes more than one item to construct, use :kbd:`Ctrl`:kbd:`Left` and -:kbd:`Ctrl`:kbd:`Right` to select the item that you want to set filters for. Any filters that -you set will be used for all buildings of the selected type placed from that point onward -(until you set a new filter or clear the current one). Buildings placed before the filters -were changed will keep the filter values that were set when the building was placed. +This is very useful when combined with manager work orders or `workflow` -- you +can set a constraint to always have one or two doors/beds/tables/chairs/etc. +available, and place as many as you like. Materials are used to build the +planned buildings as they are produced, with minimal space dedicated to +stockpiles. -For example, you can be sure that all your constructed walls are the same color by setting -a filter to accept only certain types of stone. +Usage:: -Quickfort mode --------------- - -If you use the external Python Quickfort to apply building blueprints instead of the native -DFHack `quickfort` script, you must enable Quickfort mode. This temporarily enables -buildingplan for all building types and adds an extra blank screen after every building -placement. This "dummy" screen is needed for Python Quickfort to interact successfully with -Dwarf Fortress. + enable buildingplan + buildingplan set + buildingplan set true|false -Note that Quickfort mode is only for compatibility with the legacy Python Quickfort. The -DFHack `quickfort` script does not need Quickfort mode to be enabled. The `quickfort` script -will successfully integrate with buildingplan as long as the buildingplan plugin is enabled. +Running ``buildingplan set`` without parameters displays the current settings. .. _buildingplan-settings: Global settings --------------- -The buildingplan plugin has several global settings that can be set from the UI (:kbd:`G` -from any building placement screen, for example: :kbd:`b`:kbd:`a`:kbd:`G`). These settings -can also be set from the ``DFHack#`` prompt once a map is loaded (or from your -``onMapLoad.init`` file) with the syntax:: +The buildingplan plugin has global settings that can be set from the UI +(:kbd:`G` from any building placement screen, for example: +:kbd:`b`:kbd:`a`:kbd:`G`). These settings can also be set via the +``buildingplan set`` command. The available settings are: + +- ``all_enabled`` (default: false) + Enable planning mode for all building types. +- ``blocks``, ``boulders``, ``logs``, ``bars`` (defaults: true, true, true, false) + Allow blocks, boulders, logs, or bars to be matched for generic "building + material" items. +- ``quickfort_mode`` (default: false) + Enable compatibility mode for the legacy Python Quickfort (this setting is + not required for DFHack `quickfort`) + +The settings for ``blocks``, ``boulders``, ``logs``, and ``bars`` are saved with +your fort, so you only have to set them once and they will be persisted in your +save. + +If you normally embark with some blocks on hand for early workshops, you might +want to add this line to your ``dfhack-config/init/onMapLoad.init`` file to +always configure buildingplan to just use blocks for buildlings and +constructions:: - buildingplan set + on-new-fortress buildingplan set boulders false; buildingplan set logs false -and displayed with:: +.. _buildingplan-filters: - buildingplan set +Item filtering +-------------- -The available settings are: - -+----------------+---------+-----------+---------------------------------------+ -| Setting | Default | Persisted | Description | -+================+=========+===========+=======================================+ -| all_enabled | false | no | Enable planning mode for all building | -| | | | types. | -+----------------+---------+-----------+---------------------------------------+ -| blocks | true | yes | Allow blocks, boulders, logs, or bars | -+----------------+---------+ | to be matched for generic "building | -| boulders | true | | material" items | -+----------------+---------+ | | -| logs | true | | | -+----------------+---------+ | | -| bars | false | | | -+----------------+---------+-----------+---------------------------------------+ -| 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 -could add this to your ``onMapLoad.init`` file:: +While placing a building, you can set filters for what materials you want the +building made out of, what quality you want the component items to be, and +whether you want the items to be decorated. - on-new-fortress buildingplan set boulders false; buildingplan set logs false +If a building type takes more than one item to construct, use +:kbd:`Ctrl`:kbd:`Left` and :kbd:`Ctrl`:kbd:`Right` to select the item that you +want to set filters for. Any filters that you set will be used for all buildings +of the selected type placed from that point onward (until you set a new filter +or clear the current one). Buildings placed before the filters were changed will +keep the filter values that were set when the building was placed. + +For example, you can be sure that all your constructed walls are the same color +by setting a filter to accept only certain types of stone. + +Quickfort mode +-------------- + +If you use the external Python Quickfort to apply building blueprints instead of +the native DFHack `quickfort` script, you must enable Quickfort mode. This +temporarily enables buildingplan for all building types and adds an extra blank +screen after every building placement. This "dummy" screen is needed for Python +Quickfort to interact successfully with Dwarf Fortress. -Persisted settings (i.e. ``blocks``, ``boulders``, ``logs``, and ``bars``) are saved with -your game, so you only need to set them to the values you want once. +Note that Quickfort mode is only for compatibility with the legacy Python +Quickfort. The DFHack `quickfort` script does not need this Quickfort mode to be +enabled. The `quickfort` script will successfully integrate with buildingplan as +long as the buildingplan plugin itself is enabled. diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 594eaf48f..b13c8daa3 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -1058,9 +1058,9 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back( - PluginCommand( - "buildingplan", "Plan building construction before you have materials", - buildingplan_cmd, false, "Run 'buildingplan debug [on|off]' to toggle debugging, or 'buildingplan version' to query the plugin version.")); + PluginCommand("buildingplan", + "Plan building construction before you have materials.", + buildingplan_cmd)); return CR_OK; } From 66b7bcaf1a6965f27e6d3bce1fcdd012c06ac1b3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 18 Jul 2022 17:09:48 -0700 Subject: [PATCH 053/334] update docs for burrows --- docs/plugins/burrows.rst | 61 ++++++++++++++++++++++------------------ plugins/burrows.cpp | 33 +++------------------- 2 files changed, 38 insertions(+), 56 deletions(-) diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 787b3ff37..658b7d209 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -1,38 +1,45 @@ burrows ======= -Miscellaneous burrow control. Allows manipulating burrows and automated burrow -expansion while digging. -Options: +Quick commands for burrow control. Allows manipulating burrows and automated +burrow expansion while digging. -:enable feature ...: - Enable features of the plugin. -:disable feature ...: - Disable features of the plugin. -:clear-unit burrow burrow ...: - Remove all units from the burrows. -:clear-tiles burrow burrow ...: - Remove all tiles from the burrows. -:set-units target-burrow src-burrow ...: - Clear target, and adds units from source burrows. -:add-units target-burrow src-burrow ...: +Usage: + +- ``enable auto-grow`` + When a wall inside a burrow with a name ending in '+' is dug out, the burrow + will be extended to newly-revealed adjacent walls. This final '+' may be + omitted in burrow name args of other ``burrows`` commands. Note that digging + 1-wide corridors with the miner inside the burrow is SLOW. +- ``disable auto-grow`` + Disables auto-grow processing. +- ``clear-unit [ ...]`` + Remove all units from the named burrows. +- ``clear-tiles [ ...]`` + Remove all tiles from the named burrows. +- ``set-units target-burrow [ ...]`` + Clear all units from the target burrow, then add units from the named source + burrows. +- ``add-units target-burrow [ ...]`` Add units from the source burrows to the target. -:remove-units target-burrow src-burrow ...: +- ``remove-units target-burrow [ ...]`` Remove units in source burrows from the target. -:set-tiles target-burrow src-burrow ...: - Clear target and adds tiles from the source burrows. -:add-tiles target-burrow src-burrow ...: +- ``set-tiles target-burrow [ ...]`` + Clear target burrow tiles and adds tiles from the names source burrows. +- ``add-tiles target-burrow [ ...]`` Add tiles from the source burrows to the target. -:remove-tiles target-burrow src-burrow ...: +- ``remove-tiles target-burrow [ ...]`` Remove tiles in source burrows from the target. - For these three options, in place of a source burrow it is - possible to use one of the following keywords: ABOVE_GROUND, - SUBTERRANEAN, INSIDE, OUTSIDE, LIGHT, DARK, HIDDEN, REVEALED +In place of a source burrow, you can use one of the following keywords: -Features: +- ``ABOVE_GROUND`` +- ``SUBTERRANEAN`` +- ``INSIDE`` +- ``OUTSIDE`` +- ``LIGHT`` +- ``DARK`` +- ``HIDDEN`` +- ``REVEALED`` -:auto-grow: When a wall inside a burrow with a name ending in '+' is dug - out, the burrow is extended to newly-revealed adjacent walls. - This final '+' may be omitted in burrow name args of commands above. - Digging 1-wide corridors with the miner inside the burrow is SLOW. +to add tiles with the given properties. diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index 3b1d3fe86..029b3c715 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -53,35 +53,10 @@ static void deinit_map(color_ostream &out); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand( - "burrow", "Miscellaneous burrow control.", burrow, false, - " burrow enable options...\n" - " burrow disable options...\n" - " Enable or disable features of the plugin.\n" - " See below for a list and explanation.\n" - " burrow clear-units burrow burrow...\n" - " burrow clear-tiles burrow burrow...\n" - " Removes all units or tiles from the burrows.\n" - " burrow set-units target-burrow src-burrow...\n" - " burrow add-units target-burrow src-burrow...\n" - " burrow remove-units target-burrow src-burrow...\n" - " Adds or removes units in source burrows to/from the target\n" - " burrow. Set is equivalent to clear and add.\n" - " burrow set-tiles target-burrow src-burrow...\n" - " burrow add-tiles target-burrow src-burrow...\n" - " burrow remove-tiles target-burrow src-burrow...\n" - " Adds or removes tiles in source burrows to/from the target\n" - " burrow. In place of a source burrow it is possible to use\n" - " one of the following keywords:\n" - " ABOVE_GROUND, SUBTERRANEAN, INSIDE, OUTSIDE,\n" - " LIGHT, DARK, HIDDEN, REVEALED\n" - "Implemented features:\n" - " auto-grow\n" - " When a wall inside a burrow with a name ending in '+' is dug\n" - " out, the burrow is extended to newly-revealed adjacent walls.\n" - " This final '+' may be omitted in burrow name args of commands above.\n" - " Note: Digging 1-wide corridors with the miner inside the burrow is SLOW.\n" - )); + commands.push_back( + PluginCommand("burrow", + "Quick commands for burrow control.", + burrow)); if (Core::getInstance().isMapLoaded()) init_map(out); From e3588cf49c83ecd2f99af0ef0bd05853e1ffb394 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 19 Jul 2022 22:13:53 -0700 Subject: [PATCH 054/334] return entire tag data structure for iterating --- library/lua/helpdb.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 18ba5b746..0c0bd9955 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -186,7 +186,9 @@ local function update_entry(entry, iterator, opts) table.insert(lines, line) ::continue:: end - entry.long_help = table.concat(lines, '\n') + if #lines > 0 then + entry.long_help = table.concat(lines, '\n') + end end -- create db entry based on parsing sphinx-rendered help text @@ -457,13 +459,13 @@ function get_tags() return set_to_sorted_list(tag_index) end --- returns the description associated with the given tag -function get_tag_description(tag) +function get_tag_data(tag) ensure_db() if not tag_index[tag] then - error('invalid tag: ' .. tag) + dfhack.printerr('invalid tag: ' .. tag) + return {} end - return tag_index[tag].description + return tag_index[tag] end --------------------------------------------------------------------------- @@ -621,7 +623,8 @@ function tags() local tags = get_tags() local width = get_max_width(tags, 10) for _,tag in ipairs(tags) do - print((' %-'..width..'s %s'):format(tag, get_tag_description(tag))) + print((' %-'..width..'s %s'):format(tag, + get_tag_data(tag).description)) end end From ddcb9b4921dcf28ecbe9c0aa4b62ac6dadfdbbed Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 19 Jul 2022 22:35:08 -0700 Subject: [PATCH 055/334] add missing 'man' builtin --- library/lua/helpdb.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 0c0bd9955..5e2b28ede 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -55,6 +55,7 @@ local BUILTINS = { 'kill-lua', 'load', 'ls', + 'man', 'plug', 'reload', 'script', From 954e2461409a7bad8bae88594cee0a114d516e3c Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 19 Jul 2022 23:01:25 -0700 Subject: [PATCH 056/334] reformat builtins and properly read tags --- docs/Tags.rst | 25 ++++++++++++++++--------- docs/builtins/alias.rst | 10 ++++++++-- docs/builtins/clear.rst | 5 ++++- docs/builtins/cls.rst | 7 +++++-- docs/builtins/devel/dump-rpc.rst | 5 ++++- docs/builtins/die.rst | 5 ++++- docs/builtins/dir.rst | 5 ++++- docs/builtins/disable.rst | 22 +++++++--------------- docs/builtins/enable.rst | 22 ++++++++++++++++------ docs/builtins/fpause.rst | 9 ++++++++- docs/builtins/help.rst | 12 +++++++++--- docs/builtins/hide.rst | 9 ++++++++- docs/builtins/keybinding.rst | 8 ++++++-- docs/builtins/kill-lua.rst | 15 ++++++++++++--- docs/builtins/load.rst | 5 ++++- docs/builtins/ls.rst | 8 ++++++-- docs/builtins/man.rst | 7 +++++++ docs/builtins/plug.rst | 5 ++++- docs/builtins/reload.rst | 5 ++++- docs/builtins/sc-script.rst | 8 ++++++-- docs/builtins/script.rst | 8 ++++++-- docs/builtins/show.rst | 9 ++++++++- docs/builtins/tags.rst | 5 ++++- docs/builtins/type.rst | 5 ++++- docs/builtins/unload.rst | 5 ++++- library/lua/helpdb.lua | 2 +- 26 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 docs/builtins/man.rst diff --git a/docs/Tags.rst b/docs/Tags.rst index 7a5288cbf..56e5af650 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -5,12 +5,19 @@ Tags This is the list of tags and their descriptions. -- adventure: tools relevant to adventure mode -- fort: tools relevant to fort mode -- legends: tools relevant to legends mode -- enable: tools that are able to be enabled/disabled for some persistent effect -- items: tools that create or modify in-game items -- units: tools that create or modify units -- jobs: tools that create or modify jobs -- labors: tools that deal with labor assignment -- auto: tools that automatically manage some aspect of your fortress +- adventure: Tools relevant to adventure mode +- fort: Tools relevant to fort mode +- legends: Tools relevant to legends mode +- items: Tools that create or modify in-game items +- units: Tools that create or modify units +- jobs: Tools that create or modify jobs +- labors: Tools that deal with labor assignment +- auto: Tools that automatically manage some aspect of your fortress +- map: Map modification +- system: Tools related to working with DFHack commands or the core DFHack library +- productivity: Tools that help you do things that you could do manually, but using the tool is better and faster +- animals: Tools that help you manage animals +- fix: Tools that fix specific bugs +- inspection: Tools that let you inspect game data +- buildings/furniture: Tools that help you work wtih placing or configuring buildings and furniture +- quickfort: Tools that are involved in creating and playing back blueprints diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 034d0f33a..325bb9dd7 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,5 +1,8 @@ alias ------ +===== + +Tags: system +:dfhack-keybind:`alias` Configure helper aliases for other DFHack commands. Aliases are resolved immediately after built-in commands, which means that an alias cannot override @@ -21,7 +24,10 @@ Usage: Aliases can be given additional arguments when created and invoked, which will be passed to the underlying command in order. -Example:: +Example +------- + +:: [DFHack]# alias add pargs devel/print-args example [DFHack]# pargs text diff --git a/docs/builtins/clear.rst b/docs/builtins/clear.rst index b964a8b3f..44c4f679a 100644 --- a/docs/builtins/clear.rst +++ b/docs/builtins/clear.rst @@ -1,4 +1,7 @@ clear ------ +===== + +Tags: system +:dfhack-keybind:`clear` Clear the terminal screen. This command is an alias for `cls`. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index b05354d5a..0514353bd 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,5 +1,8 @@ cls ---- +=== -Clear the terminal screen. Can also be invoked as ``clear``. Note that this +Tags: system +:dfhack-keybind:`cls` + +Clear the terminal screen. Can also be invoked as `clear`. Note that this command does not delete command history. It just clears the text on the screen. diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index af173a179..e6a710409 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -1,5 +1,8 @@ devel/dump-rpc --------------- +============== + +Tags: system +:dfhack-keybind:`devel/dump-rpc` Writes RPC endpoint information to the specified file. diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index d2dae484f..7a7414b8c 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,4 +1,7 @@ die ---- +=== + +Tags: system +:dfhack-keybind:`die` Instantly exits DF without saving. diff --git a/docs/builtins/dir.rst b/docs/builtins/dir.rst index 5c2059099..2eca9218c 100644 --- a/docs/builtins/dir.rst +++ b/docs/builtins/dir.rst @@ -1,4 +1,7 @@ dir ---- +=== + +Tags: system +:dfhack-keybind:`dir` List available DFHack commands. This is an alias of the `ls` command. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index d871ff06e..413b8325f 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,20 +1,12 @@ disable -------- +======= -Deactivate a DFHack tool that has some persistent effect. Many plugins and -scripts can be in a distinct enabled or disabled state. Some of them activate -and deactivate automatically depending on the contents of the world raws. Others -store their state in world data. However a number of them have to be enabled -globally, and the init file is the right place to do it. +Tags: system +:dfhack-keybind:`disable` -Most such plugins or scripts support the built-in ``enable`` and ``disable`` -commands. Calling them at any time without arguments prints a list of enabled -and disabled plugins, and shows whether that can be changed through the same -commands. Passing plugin names to these commands will enable or disable the -specified plugins. For example, to disable the `manipulator` plugin:: +Deactivate a DFHack tool that has some persistent effect. See the `enable` +command for more info. - disable manipulator +Usage:: -It is also possible to enable or disable multiple plugins at once:: - - disable manipulator search + disable [ ...] diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 8338cc7f4..78534a98c 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,5 +1,8 @@ enable ------- +====== + +Tags: system +:dfhack-keybind:`enable` Activate a DFHack tool that has some persistent effect. Many plugins and scripts can be in a distinct enabled or disabled state. Some of them activate and @@ -7,14 +10,21 @@ deactivate automatically depending on the contents of the world raws. Others store their state in world data. However a number of them have to be enabled globally, and the init file is the right place to do it. -Most such plugins or scripts support the built-in ``enable`` and ``disable`` +Most such plugins or scripts support the built-in ``enable`` and `disable` commands. Calling them at any time without arguments prints a list of enabled and disabled plugins, and shows whether that can be changed through the same commands. Passing plugin names to these commands will enable or disable the -specified plugins. For example, to enable the `manipulator` plugin:: +specified plugins. + +Usage:: - enable manipulator + enable + enable [ ...] -It is also possible to enable or disable multiple plugins at once:: +Examples +-------- - enable manipulator search +- ``enable manipulator`` + Enable the ``manipulator`` plugin. +- ``enable manipulator search`` + Enable multiple plugins at once. diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index 68e4eb07e..60459848e 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,5 +1,12 @@ fpause ------- +====== + +Tags: system +:dfhack-keybind:`fpause` Forces DF to pause. This is useful when your FPS drops below 1 and you lose control of the game. + +Usage:: + + fpause diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index aa9e4b170..49c010fec 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,5 +1,8 @@ help ----- +==== + +Tags: system +:dfhack-keybind:`help` Display help about a command or plugin. @@ -8,7 +11,10 @@ Usage:: help|?|man help|?|man -Examples:: +Examples +-------- + +:: help blueprint man blueprint @@ -16,4 +22,4 @@ Examples:: Both examples above will display the help text for the `blueprint` command. Some commands also take ``help`` or ``?`` as an option on their command line -for the same effect - e.g. ``blueprint help``. +for the same effect -- e.g. ``blueprint help``. diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 5c8d08452..6b391972f 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,8 +1,15 @@ hide ----- +==== + +Tags: system +:dfhack-keybind:`hide` Hides the DFHack terminal window. You can show it again with the `show` command, though you'll need to use it from a `keybinding` set beforehand or the in-game `command-prompt`. Only available on Windows. + +Usage:: + + hide diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index 7ab92c206..72ceaaf71 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,5 +1,8 @@ keybinding ----------- +========== + +Tags: system +:dfhack-keybind:`keybinding` Create hotkeys that will run DFHack commands. Like any other command it can be used at any time from the console, but bindings are not remembered between runs @@ -46,7 +49,8 @@ example, ``@foo|bar|baz/foo`` would match anything under ``@foo``, ``@bar``, or Interactive commands like `liquids` cannot be used as hotkeys. -Examples: +Examples +-------- - ``keybinding add Alt-F1 hotkeys`` Bind Alt-F1 to run the `hotkeys` command on any screen at any time. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index ee354846a..875376952 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,6 +1,15 @@ kill-lua --------- +======== + +Tags: system +:dfhack-keybind:`kill-lua` Gracefully stops any currently-running Lua scripts. Use this command to stop -a misbehaving script that appears to be stuck. Use ``kill-lua force`` if just -``kill-lua`` doesn't work. +a misbehaving script that appears to be stuck. + +Usage:: + + kill-lua + kill-lua force + +Use ``kill-lua force`` if just ``kill-lua`` doesn't seem to work. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index fb654b8a4..cac760c2a 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,5 +1,8 @@ load ----- +==== + +Tags: system +:dfhack-keybind:`load` Load and register a plugin library. Also see `unload` and `reload` for related actions. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index fd2e532ed..6875083f5 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,5 +1,8 @@ ls --- +== + +Tags: system +:dfhack-keybind:`ls` List available DFHack commands. In order to group related commands, each command is associated with a list of tags. You can filter the listed commands by a tag @@ -25,7 +28,8 @@ You can also pass some optional parameters to change how ``ls`` behaves: - ``--dev`` Include commands intended for developers and modders. -Examples: +Examples +-------- - ``ls adventure`` Lists all commands with the ``adventure`` tag. diff --git a/docs/builtins/man.rst b/docs/builtins/man.rst new file mode 100644 index 000000000..3b5b52515 --- /dev/null +++ b/docs/builtins/man.rst @@ -0,0 +1,7 @@ +man +=== + +Tags: system +:dfhack-keybind:`man` + +An alias for the `help` command. diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 93bd452a7..8f714b7cf 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,5 +1,8 @@ plug ----- +==== + +Tags: system +:dfhack-keybind:`plug` Lists available plugins and whether they are enabled. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index 8f28e5ca6..d61e5c2fd 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,5 +1,8 @@ reload ------- +====== + +Tags: system +:dfhack-keybind:`reload` Reload a loaded plugin. Developer use this command to reload a plugin that they are actively modifying. Also see `load` and `unload` for related actions. diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index 20a0e9497..2c80e1130 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,5 +1,8 @@ sc-script ---------- +========= + +Tags: system +:dfhack-keybind:`sc-script` Runs commands when game state changes occur. This is similar to the static `init-files` but is slightly more flexible since it can be set dynamically. @@ -13,7 +16,8 @@ Usage: - ``sc-script add|remove [ ...]`` Register or unregister a file to be run for the specified event. -Examples: +Examples +-------- - ``sc-script add SC_MAP_LOADED spawn_extra_monsters.init`` Registers the ``spawn_extra_monsters.init`` file to be run whenever a new diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index ae4017ca7..d69f3bbc7 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,5 +1,8 @@ script ------- +====== + +Tags: system +:dfhack-keybind:`script` Executes a batch file of DFHack commands. It reads a text file and runs each line as a DFHack command as if it had been typed in by the user - treating the @@ -12,7 +15,8 @@ Usage:: script -Examples: +Examples +-------- - ``script startup.txt`` Executes the commands in ``startup.txt``, which exists in your DF game diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index 8a6f9b446..e192f857c 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,5 +1,8 @@ show ----- +==== + +Tags: system +:dfhack-keybind:`show` Unhides the DFHack terminal window. Useful if you have hidden the terminal with `hide` and you want it back. Since the terminal window won't be available to run @@ -7,3 +10,7 @@ this command, you'll need to use it from a `keybinding` set beforehand or the in-game `command-prompt`. Only available on Windows. + +Usage:: + + show diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index 9de0f8df3..3d7933fca 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,5 +1,8 @@ tags ----- +==== + +Tags: system +:dfhack-keybind:`tags` List the strings that DFHack tools can be tagged with. You can find groups of related tools by passing the tag name to the `ls` command. diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index c4c752bef..16325c90f 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,5 +1,8 @@ type ----- +==== + +Tags: system +:dfhack-keybind:`type` Describes how a command is implemented. DFHack commands can be provided by plugins, scripts, or by the core library itself. The ``type`` command can tell diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index f4069ed88..99eed500c 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,5 +1,8 @@ unload ------- +====== + +Tags: system +:dfhack-keybind:`unload` Unload a plugin from memory. Also see `load` and `reload` for related actions. diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 5e2b28ede..a972cca24 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -268,7 +268,7 @@ local function update_db(old_db, db, source, entry_name, kwargs) error('unhandled help source: ' .. source) end db[entry_name] = entry - for _,tag in ipairs(entry.tags) do + for tag in pairs(entry.tags) do -- ignore unknown tags if tag_index[tag] then table.insert(tag_index[tag], entry_name) From 1cad77601e15bf543ddab5326a3c38c665a63ced Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 19 Jul 2022 23:11:02 -0700 Subject: [PATCH 057/334] update formatting for plugin docs --- docs/plugins/3dveins.rst | 6 +++++- docs/plugins/add-spatter.rst | 3 +++ docs/plugins/autochop.rst | 3 +++ docs/plugins/autoclothing.rst | 6 +++++- docs/plugins/autodump-destroy-here.rst | 3 +++ docs/plugins/autodump-destroy-item.rst | 3 +++ docs/plugins/autodump.rst | 9 +++++++-- docs/plugins/autofarm.rst | 6 +++++- docs/plugins/autogems-reload.rst | 3 +++ docs/plugins/autogems.rst | 3 +++ docs/plugins/autohauler.rst | 6 +++++- docs/plugins/autolabor.rst | 9 +++++++-- docs/plugins/automaterial.rst | 3 +++ docs/plugins/automelt.rst | 3 +++ docs/plugins/autotrade.rst | 3 +++ docs/plugins/blueprint.rst | 21 +++++++++++++++------ docs/plugins/building-hacks.rst | 3 +++ docs/plugins/buildingplan.rst | 3 +++ docs/plugins/burrows.rst | 3 +++ 19 files changed, 85 insertions(+), 14 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 9a074a4c8..89c241df6 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,6 +1,9 @@ 3dveins ======= +Tags: +:dfhack-keybind:`3dveins` + Rewrites layer veins to expand in 3D space. Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in their place. The transformation preserves the mineral counts reported by `prospect`. @@ -11,7 +14,8 @@ Usage:: The ``verbose`` option prints out extra information to the console. -Example:: +Example +------- 3dveins diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index b6bb97cf7..92808017c 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -1,6 +1,9 @@ add-spatter =========== +Tags: +:dfhack-keybind:`add-spatter` + Make tagged reactions produce contaminants. The plugin is intended to give some use to all those poisons that can be bought from caravans. It automatically enables itself when you load a world with reactions that include names starting diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 7c517e901..704b97143 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -1,6 +1,9 @@ autochop ======== +Tags: +:dfhack-keybind:`autochop` + Auto-harvest trees when low on stockpiled logs. This plugin can designate trees for chopping when your stocks are low on logs. diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index a3c53a65a..a8cc001dd 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -1,6 +1,9 @@ autoclothing ============ +Tags: +:dfhack-keybind:`autoclothing` + Automatically manage clothing work orders. It allows you to set how many of each clothing type every citizen should have. @@ -15,7 +18,8 @@ anything your civilization can produce, such as "dress" or "mitten". When invoked without a number, it shows the current configuration for that material and item. -Examples: +Examples +-------- * ``autoclothing cloth "short skirt" 10``: Sets the desired number of cloth short skirts available per citizen to 10. diff --git a/docs/plugins/autodump-destroy-here.rst b/docs/plugins/autodump-destroy-here.rst index 375a799c0..35356166c 100644 --- a/docs/plugins/autodump-destroy-here.rst +++ b/docs/plugins/autodump-destroy-here.rst @@ -1,6 +1,9 @@ autodump-destroy-here ===================== +Tags: +:dfhack-keybind:`autodump-destroy-here` + Destroy items marked for dumping under cursor. If called again before the game is resumed, cancels destruction of the items. This is an alias for the `autodump` command ``autodump destroy-here``, intended for use as a keybinding. diff --git a/docs/plugins/autodump-destroy-item.rst b/docs/plugins/autodump-destroy-item.rst index a9f3554c3..9447590d0 100644 --- a/docs/plugins/autodump-destroy-item.rst +++ b/docs/plugins/autodump-destroy-item.rst @@ -1,6 +1,9 @@ autodump-destroy-item ===================== +Tags: +:dfhack-keybind:`autodump-destroy-item` + Destroy the selected item. The item may be selected in the :kbd:`k` list or in the container item list. If called again before the game is resumed, cancels destruction of the item. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 83b50e498..5ac0d1cd5 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -1,6 +1,9 @@ autodump ======== +Tags: +:dfhack-keybind:`autodump` + Quickly designate or teleport items to be dumped. When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autodump`` option is selected for the stockpile, any items placed in the stockpile will @@ -19,7 +22,8 @@ Usage:: enable autodump autodump [] -Options: +Options +------- - ``destroy`` Destroy instead of dumping. Doesn't require a cursor. If ``autodump`` is @@ -35,7 +39,8 @@ Options: - ``forbidden`` Only process forbidden items (default: only unforbidden). -Examples: +Examples +-------- - ``autodump`` Teleports all unforbidden items marked for dumping to the cursor position. diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index d66830fe0..9a3e16d43 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -1,6 +1,9 @@ autofarm ======== +Tags: +:dfhack-keybind:`autofarm` + Automatically manage farm crop selection. This plugin periodically scans your plant stocks and assigns crops to your farm plots based on which plant stocks are low (as long as you have the appropriate seeds). The target threshold for @@ -24,7 +27,8 @@ following command:: lua "for _,plant in ipairs(df.global.world.raws.plants.all) do if plant.flags.SEED then print(plant.id) end end" -Examples: +Examples +-------- - ``autofarm default 30`` Set the default threshold to 30. diff --git a/docs/plugins/autogems-reload.rst b/docs/plugins/autogems-reload.rst index a37117746..0988496cb 100644 --- a/docs/plugins/autogems-reload.rst +++ b/docs/plugins/autogems-reload.rst @@ -1,6 +1,9 @@ autogems-reload =============== +Tags: +:dfhack-keybind:`autogems-reload` + Reloads the autogems configuration file. You might need to do this if you have manually modified the contents while the game is running. diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index ad3b0f9c3..724752217 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,6 +1,9 @@ autogems ======== +Tags: +:dfhack-keybind:`autogems` + Automatically cut rough gems. This plugin periodically scans your stocks of rough gems and creates manager orders for cutting them at a Jeweler's Workshop. diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 3bdd00950..f28044479 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -1,6 +1,9 @@ autohauler ========== +Tags: +:dfhack-keybind:`autohauler` + Automatically manage hauling labors. Similar to `autolabor`, but instead of managing all labors, ``autohauler`` only addresses hauling labors, leaving the assignment of skilled labors entirely up to you. You can use the in-game @@ -40,7 +43,8 @@ Usage: - ``autohauler debug`` In the next cycle, output the state of every dwarf. -Examples: +Examples +-------- - ``autohauler HAUL_STONE haulers`` Set stone hauling as a hauling labor (this is already the default). diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index b60146d28..f402703c8 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -1,6 +1,9 @@ autolabor ========= +Tags: +:dfhack-keybind:`autolabor` + Automatically manage dwarf labors. Autolabor attempts to keep as many dwarves as possible busy while allowing dwarves to specialize in specific skills. @@ -49,7 +52,8 @@ dwarfs that meet any of these conditions: We stop assigning dwarves when we reach the maximum allowed. -Advanced usage: +Advanced usage +-------------- - ``autolabor [] []`` Set range of dwarves assigned to a labor, optionally specifying the size of @@ -68,7 +72,8 @@ Advanced usage: See `autolabor-artisans` for a differently-tuned setup. -Examples: +Examples +-------- - ``autolabor MINE 5`` Keep at least 5 dwarves with mining enabled. diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 51701a1fc..429a25d70 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -1,6 +1,9 @@ automaterial ============ +Tags: +:dfhack-keybind:`automaterial` + Sorts building materials by recent usage. This makes building constructions (walls, floors, fortifications, etc) much easier by saving you from having to trawl through long lists of materials each time you place one. diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 749aac28a..b16368ac5 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -1,6 +1,9 @@ automelt ======== +Tags: +:dfhack-keybind:`automelt` + Quickly designate items to be melted. When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` option is selected for the stockpile, any items placed in the stockpile will automatically diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index e695a0a53..405621b2b 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -1,6 +1,9 @@ autotrade ========= +Tags: +:dfhack-keybind:`autotrade` + Quickly designate items to be traded. When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autotrade`` option is selected for the stockpile, any items placed in the stockpile will automatically diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index edfc1f096..9d74fa47a 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -1,6 +1,9 @@ blueprint ========= +Tags: +:dfhack-keybind:`blueprint` + Record a live game map in a quickfort blueprint. With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with `quickfort`. @@ -16,7 +19,8 @@ Usage:: blueprint [] [ []] [] blueprint gui [ []] [] -Examples: +Examples +-------- - ``blueprint gui`` Runs `gui/blueprint`, the interactive frontend, where all configuration for @@ -30,7 +34,8 @@ Examples: the start of the blueprint area is set to a specific value instead of using the in-game cursor position. -Positional Parameters: +Positional parameters +--------------------- - ``width`` Width of the area (in tiles) to translate. @@ -46,7 +51,8 @@ Positional Parameters: some characters other than numbers so the name won't be confused with the optional ``depth`` parameter. -Phases: +Phases +------ If you want to generate blueprints only for specific phases, add their names to the commandline, anywhere after the blueprint base name. You can list multiple @@ -68,7 +74,8 @@ phases; just separate them with a space. If no phases are specified, phases are autodetected. For example, a ``#place`` blueprint will be created only if there are stockpiles in the blueprint area. -Options: +Options +------- - ``-c``, ``--cursor ,,`` Use the specified map coordinates instead of the current cursor position for @@ -98,7 +105,8 @@ Options: "none", which will create a standard quickfort `multi-blueprint ` file. -Output formats: +Output formats +-------------- Here are the values that can be passed to the ``--format`` flag: @@ -109,7 +117,8 @@ Here are the values that can be passed to the ``--format`` flag: Makes the blueprints in the ``.csv`` files easier to read and edit with a text editor by adding extra spacing and alignment markers. -Splitting output into multiple files: +Splitting output into multiple files +------------------------------------ The ``--splitby`` flag can take any of the following values: diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 950d1751a..78e94fba6 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -1,6 +1,9 @@ building-hacks ============== +Tags: +:dfhack-keybind:`building-hacks` + Allows mods to create and manage powered workshops. Usage:: diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 4b21da8af..62a239e3d 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -1,6 +1,9 @@ buildingplan ============ +Tags: +:dfhack-keybind:`buildingplan` + Plan building construction before you have materials. This plugin adds a planning mode for building placement. You can then place furniture, constructions, and other buildings before the required materials are available, diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 658b7d209..f863dd439 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -1,6 +1,9 @@ burrows ======= +Tags: +:dfhack-keybind:`burrows` + Quick commands for burrow control. Allows manipulating burrows and automated burrow expansion while digging. From 0dd153cc0d499c9b88eda6d81db1a873abcb10af Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 12:34:40 -0700 Subject: [PATCH 058/334] ensure we pick up the plugin help entry even when it has a command and that command is not equal to the plugin name --- library/lua/helpdb.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index a972cca24..f6f9ed864 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -293,25 +293,25 @@ local function scan_plugins(old_db, db) local plugin_names = dfhack.internal.listPlugins() for _,plugin in ipairs(plugin_names) do local commands = dfhack.internal.listCommands(plugin) - if #commands == 0 then - -- use plugin name as the command so we have something to anchor the - -- documentation to - update_db(old_db, db, - has_rendered_help(plugin) and - HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, - plugin, {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) - goto continue - end + local includes_plugin = false for _,command in ipairs(commands) do local entry_types = {[ENTRY_TYPES.COMMAND]=true} if command == plugin then entry_types[ENTRY_TYPES.PLUGIN]=true + includes_plugin = true end update_db(old_db, db, has_rendered_help(command) and HELP_SOURCES.RENDERED or HELP_SOURCES.PLUGIN, command, {entry_types=entry_types}) end + if not includes_plugin then + update_db(old_db, db, + has_rendered_help(plugin) and + HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, + plugin, {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) + goto continue + end ::continue:: end end From 0fe4bed121621815dbd430b66544714723c3acb0 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 12:42:27 -0700 Subject: [PATCH 059/334] document both burrow command and burrows plugin --- docs/plugins/burrow.rst | 49 ++++++++++++++++++++++++++++++++++++++++ docs/plugins/burrows.rst | 48 ++++++++------------------------------- 2 files changed, 58 insertions(+), 39 deletions(-) create mode 100644 docs/plugins/burrow.rst diff --git a/docs/plugins/burrow.rst b/docs/plugins/burrow.rst new file mode 100644 index 000000000..879a7cc78 --- /dev/null +++ b/docs/plugins/burrow.rst @@ -0,0 +1,49 @@ +burrow +====== + +Tags: +:dfhack-keybind:`burrow` + +Quick commands for burrow control. Allows manipulating burrows and automated +burrow expansion while digging. + +Usage: + +- ``burrows enable auto-grow`` + When a wall inside a burrow with a name ending in '+' is dug out, the burrow + will be extended to newly-revealed adjacent walls. This final '+' may be + omitted in burrow name args of other ``burrows`` commands. Note that digging + 1-wide corridors with the miner inside the burrow is SLOW. Be sure to also + run ``enable burrow`` for this feature to work. +- ``burrows disable auto-grow`` + Disables auto-grow processing. +- ``burrows clear-unit [ ...]`` + Remove all units from the named burrows. +- ``burrows clear-tiles [ ...]`` + Remove all tiles from the named burrows. +- ``burrows set-units target-burrow [ ...]`` + Clear all units from the target burrow, then add units from the named source + burrows. +- ``burrows add-units target-burrow [ ...]`` + Add units from the source burrows to the target. +- ``burrows remove-units target-burrow [ ...]`` + Remove units in source burrows from the target. +- ``burrows set-tiles target-burrow [ ...]`` + Clear target burrow tiles and adds tiles from the names source burrows. +- ``burrows add-tiles target-burrow [ ...]`` + Add tiles from the source burrows to the target. +- ``burrows remove-tiles target-burrow [ ...]`` + Remove tiles in source burrows from the target. + +In place of a source burrow, you can use one of the following keywords: + +- ``ABOVE_GROUND`` +- ``SUBTERRANEAN`` +- ``INSIDE`` +- ``OUTSIDE`` +- ``LIGHT`` +- ``DARK`` +- ``HIDDEN`` +- ``REVEALED`` + +to add tiles with the given properties. diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index f863dd439..9bf375fa8 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -4,45 +4,15 @@ burrows Tags: :dfhack-keybind:`burrows` -Quick commands for burrow control. Allows manipulating burrows and automated -burrow expansion while digging. +Auto-expand burrows as you dig. When a wall inside a burrow with a name ending +in ``+`` is dug out, the burrow will be extended to newly-revealed adjacent +walls. Note that digging 1-wide corridors with the miner inside the burrow is +SLOW. -Usage: +Usage:: -- ``enable auto-grow`` - When a wall inside a burrow with a name ending in '+' is dug out, the burrow - will be extended to newly-revealed adjacent walls. This final '+' may be - omitted in burrow name args of other ``burrows`` commands. Note that digging - 1-wide corridors with the miner inside the burrow is SLOW. -- ``disable auto-grow`` - Disables auto-grow processing. -- ``clear-unit [ ...]`` - Remove all units from the named burrows. -- ``clear-tiles [ ...]`` - Remove all tiles from the named burrows. -- ``set-units target-burrow [ ...]`` - Clear all units from the target burrow, then add units from the named source - burrows. -- ``add-units target-burrow [ ...]`` - Add units from the source burrows to the target. -- ``remove-units target-burrow [ ...]`` - Remove units in source burrows from the target. -- ``set-tiles target-burrow [ ...]`` - Clear target burrow tiles and adds tiles from the names source burrows. -- ``add-tiles target-burrow [ ...]`` - Add tiles from the source burrows to the target. -- ``remove-tiles target-burrow [ ...]`` - Remove tiles in source burrows from the target. + enable burrows + burrows enable auto-grow -In place of a source burrow, you can use one of the following keywords: - -- ``ABOVE_GROUND`` -- ``SUBTERRANEAN`` -- ``INSIDE`` -- ``OUTSIDE`` -- ``LIGHT`` -- ``DARK`` -- ``HIDDEN`` -- ``REVEALED`` - -to add tiles with the given properties. +Both of the above commands need to be run for the auto-grow functionality to +work. See the `burrow` command for more burrow-related tools. From ddd2e5003aa8b681f6a3210061b1f45851908c11 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:00:27 -0700 Subject: [PATCH 060/334] update docs for changeitem --- docs/plugins/changeitem.rst | 61 ++++++++++++++++++++++++------------- plugins/changeitem.cpp | 34 +++------------------ 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index c6da2516f..0a841511c 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -1,26 +1,43 @@ changeitem ========== -Allows changing item material and base quality. By default the item currently -selected in the UI will be changed (you can select items in the 'k' list -or inside containers/inventory). By default change is only allowed if materials -is of the same subtype (for example wood<->wood, stone<->stone etc). But since -some transformations work pretty well and may be desired you can override this -with 'force'. Note that some attributes will not be touched, possibly resulting -in weirdness. To get an idea how the RAW id should look like, check some items -with 'info'. Using 'force' might create items which are not touched by -crafters/haulers. - -Options: - -:info: Don't change anything, print some info instead. -:here: Change all items at the cursor position. Requires in-game cursor. -:material, m: Change material. Must be followed by valid material RAW id. -:quality, q: Change base quality. Must be followed by number (0-5). -:force: Ignore subtypes, force change to new material. - -Examples: - -``changeitem m INORGANIC:GRANITE here`` - Change material of all items under the cursor to granite. + +Tags: +:dfhack-keybind: + +Allows changing item material and base quality. By default, a change is only +allowed if the existing and desired item materials are of the same subtype +(for example wood -> wood, stone -> stone, etc). But since some transformations +work pretty well and may be desired you can override this with ``force``. Note +that forced changes can possibly result in items that crafters and haulers +refuse to touch. + +Usage: + +- ``changeitem info`` + Show details about the selected item. Does not change the item. You can use + this command to discover RAW ids for existing items. +- ``changeitem []`` + Change the item selected in the ``k`` list or inside a container/inventory. +- ``changeitem here []`` + Change all items at the cursor position. Requires in-game cursor. + +Options +------- + +- ``m``, ``material `` + Change material. Must be followed by valid material RAW id. +- ``s``, ``subtype `` + Change subtype. Must be followed by a valid subtype RAW id." +- ``q``, ``quality `` + Change base quality. Must be followed by number (0-5) with 0 being no quality + and 5 being masterpiece quality. +- ``force`` + Ignore subtypes and force the change to the new material. + +Examples +-------- + +``changeitem here m INORGANIC:GRANITE`` + Change material of all stone items under the cursor to granite. ``changeitem q 5`` Change currently selected item to masterpiece quality. diff --git a/plugins/changeitem.cpp b/plugins/changeitem.cpp index 52d9e7ab9..49feaec79 100644 --- a/plugins/changeitem.cpp +++ b/plugins/changeitem.cpp @@ -37,37 +37,12 @@ REQUIRE_GLOBAL(world); command_result df_changeitem(color_ostream &out, vector & parameters); -const string changeitem_help = - "Changeitem allows to change some item attributes.\n" - "By default the item currently selected in the UI will be changed\n" - "(you can select items in the 'k' list or inside containers/inventory).\n" - "By default change is only allowed if materials is of the same subtype\n" - "(for example wood<->wood, stone<->stone etc). But since some transformations\n" - "work pretty well and may be desired you can override this with 'force'.\n" - "Note that some attributes will not be touched, possibly resulting in weirdness.\n" - "To get an idea how the RAW id should look like, check some items with 'info'.\n" - "Using 'force' might create items which are not touched by crafters/haulers.\n" - "Options:\n" - " info - don't change anything, print some item info instead\n" - " here - change all items at cursor position\n" - " material, m - change material. must be followed by material RAW id\n" - " subtype, s - change subtype. must be followed by correct RAW id\n" - " quality, q - change base quality. must be followed by number (0-5)\n" - " force - ignore subtypes, force change to new material.\n" - "Example:\n" - " changeitem m INORGANIC:GRANITE here\n" - " change material of all items under the cursor to granite\n" - " changeitem q 5\n" - " change currently selected item to masterpiece quality\n"; - - DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "changeitem", "Change item attributes (material, quality).", - df_changeitem, false, - changeitem_help.c_str() - )); + "changeitem", + "Change item attributes (material, quality).", + df_changeitem)); return CR_OK; } @@ -130,8 +105,7 @@ command_result df_changeitem(color_ostream &out, vector & parameters) if (p == "help" || p == "?") { - out << changeitem_help << endl; - return CR_OK; + return CR_WRONG_USAGE; } else if (p == "here") { From fae5f0635a7f6e7523f7d072ba26a244fada0575 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:21:05 -0700 Subject: [PATCH 061/334] update docs for changelayer --- docs/plugins/changelayer.rst | 106 +++++++++++++++++++---------------- plugins/changelayer.cpp | 66 ++-------------------- 2 files changed, 63 insertions(+), 109 deletions(-) diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 87c27921c..535409e90 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -1,60 +1,72 @@ changelayer =========== -Changes material of the geology layer under cursor to the specified inorganic -RAW material. Can have impact on all surrounding regions, not only your embark! -By default changing stone to soil and vice versa is not allowed. By default -changes only the layer at the cursor position. Note that one layer can stretch -across lots of z levels. By default changes only the geology which is linked -to the biome under the cursor. That geology might be linked to other biomes -as well, though. Mineral veins and gem clusters will stay on the map. Use -`changevein` for them. - -tl;dr: You will end up with changing quite big areas in one go, especially if -you use it in lower z levels. Use with care. - -Options: - -:all_biomes: Change selected layer for all biomes on your map. - Result may be undesirable since the same layer can AND WILL - be on different z-levels for different biomes. Use the tool - 'probe' to get an idea how layers and biomes are distributed - on your map. -:all_layers: Change all layers on your map (only for the selected biome - unless 'all_biomes' is added). - Candy mountain, anyone? Will make your map quite boring, - but tidy. -:force: Allow changing stone to soil and vice versa. !!THIS CAN HAVE - WEIRD EFFECTS, USE WITH CARE!! - Note that soil will not be magically replaced with stone. - You will, however, get a stone floor after digging so it - will allow the floor to be engraved. - Note that stone will not be magically replaced with soil. - You will, however, get a soil floor after digging so it - could be helpful for creating farm plots on maps with no - soil. -:verbose: Give some details about what is being changed. -:trouble: Give some advice about known problems. - -Examples: + +Tags: +:dfhack-keybind: + +Change the material of an entire geology layer. Note that one layer can stretch +across many z-levels, and changes to the geology layer will affect all +surrounding regions, not just your embark! Mineral veins and gem clusters will +not be affected. Use `changevein` if you want to modify those. + +tl;dr: You will end up with changing large areas in one go, especially if you +use it in lower z levels. Use this command with care! + +Usage:: + + changelayer [] + +When run without options, ``changelayer`` will: + +- only affect the geology layer at the current cursor position +- only affect the biome that covers the current cursor position +- not allow changing stone to soil and vice versa + +You can use the `probe` command on various tiles around your map to find valid +material RAW ids and to get an idea how layers and biomes are distributed. + +Options +------- + +- ``all_biomes`` + Change the corresponding geology layer for all biomes on your map. Be aware + that the same geology layer can AND WILL be on different z-levels for + different biomes. +- ``all_layers`` + Change all geology layers on your map (only for the selected biome unless + ``all_biomes`` is also specified). Candy mountain, anyone? Will make your map + quite boring, but tidy. +- ``force`` + Allow changing stone to soil and vice versa. **THIS CAN HAVE WEIRD EFFECTS, + USE WITH CARE AND SAVE FIRST**. Note that soil will not be magically replaced + with stone. You will, however, get a stone floor after digging, so it will + allow the floor to be engraved. Similarly, stone will not be magically + replaced with soil, but you will get a soil floor after digging, so it could + be helpful for creating farm plots on maps with no soil. +- ``verbose`` + Output details about what is being changed. + +Examples +-------- ``changelayer GRANITE`` - Convert layer at cursor position into granite. + Convert the layer at the cursor position into granite. ``changelayer SILTY_CLAY force`` - Convert layer at cursor position into clay even if it's stone. + Convert teh layer at the cursor position into clay, even if it's stone. ``changelayer MARBLE all_biomes all_layers`` Convert all layers of all biomes which are not soil into marble. .. note:: * If you use changelayer and nothing happens, try to pause/unpause the game - for a while and try to move the cursor to another tile. Then try again. - If that doesn't help try temporarily changing some other layer, undo your - changes and try again for the layer you want to change. Saving - and reloading your map might also help. + for a while and move the cursor to another tile. Then try again. If that + doesn't help, then try to temporarily change some other layer, undo your + changes, and try again for the layer you want to change. Saving and + reloading your map also sometimes helps. * You should be fine if you only change single layers without the use - of 'force'. Still it's advisable to save your game before messing with + of 'force'. Still, it's advisable to save your game before messing with the map. - * When you force changelayer to convert soil to stone you might experience - weird stuff (flashing tiles, tiles changed all over place etc). - Try reverting the changes manually or even better use an older savegame. - You did save your game, right? + * When you force changelayer to convert soil to stone, you might see some + weird stuff (flashing tiles, tiles changed all over place etc). Try + reverting the changes manually or even better use an older savegame. You + did save your game, right? diff --git a/plugins/changelayer.cpp b/plugins/changelayer.cpp index 79081c040..347f29b95 100644 --- a/plugins/changelayer.cpp +++ b/plugins/changelayer.cpp @@ -31,66 +31,14 @@ DFHACK_PLUGIN("changelayer"); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cursor); -const string changelayer_help = - " Allows to change the material of whole geology layers.\n" - " Can have impact on all surrounding regions, not only your embark!\n" - " By default changing stone to soil and vice versa is not allowed.\n" - " By default changes only the layer at the cursor position.\n" - " Note that one layer can stretch across lots of z levels.\n" - " By default changes only the geology which is linked to the biome under the\n" - " cursor. That geology might be linked to other biomes as well, though.\n" - " Mineral veins and gem clusters will stay on the map.\n" - " Use 'changevein' for them.\n\n" - " tl;dr: You will end up with changing quite big areas in one go.\n\n" - "Options (first parameter MUST be the material id):\n" - " all_biomes - Change layer for all biomes on your map.\n" - " Result may be undesirable since the same layer\n" - " can AND WILL be on different z-levels for different biomes.\n" - " Use the tool 'probe' to get an idea how layers and biomes\n" - " are distributed on your map.\n" - " all_layers - Change all layers on your map.\n" - " Candy mountain, anyone?\n" - " Will make your map quite boring, but tidy.\n" - " force - Allow changing stone to soil and vice versa.\n" - " !!THIS CAN HAVE WEIRD EFFECTS, USE WITH CARE!!\n" - " Note that soil will not be magically replaced with stone.\n" - " You will, however, get a stone floor after digging so it\n" - " will allow the floor to be engraved.\n" - " Note that stone will not be magically replaced with soil.\n" - " You will, however, get a soil floor after digging so it\n" - " could be helpful for creating farm plots on maps with no soil.\n" - " verbose - Give some more details about what is being changed.\n" - " trouble - Give some advice for known problems.\n" - "Example:\n" - " changelayer GRANITE\n" - " Convert layer at cursor position into granite.\n" - " changelayer SILTY_CLAY force\n" - " Convert layer at cursor position into clay even if it's stone.\n" - " changelayer MARBLE allbiomes alllayers\n" - " Convert all layers of all biomes into marble.\n"; - -const string changelayer_trouble = - "Known problems with changelayer:\n\n" - " Nothing happens, the material stays the old.\n" - " Pause/unpause the game and/or move the cursor a bit. Then retry.\n" - " Try changing another layer, undo the changes and try again.\n" - " Try saving and loading the game.\n\n" - " Weird stuff happening after using the 'force' option.\n" - " Change former stone layers back to stone, soil back to soil.\n" - " If in doubt, use the 'probe' tool to find tiles with soil walls\n" - " and stone layer type or the other way round.\n"; - - command_result changelayer (color_ostream &out, std::vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "changelayer", "Change a whole geology layer.", - changelayer, false, /* true means that the command can't be used from non-interactive user interface */ - // Extended help string. Used by CR_WRONG_USAGE and the help command: - changelayer_help.c_str() - )); + "changelayer", + "Change a whole geology layer.", + changelayer)); return CR_OK; } @@ -120,13 +68,7 @@ command_result changelayer (color_ostream &out, std::vector & para { if(parameters[i] == "help" || parameters[i] == "?") { - out.print("%s",changelayer_help.c_str()); - return CR_OK; - } - if(parameters[i] == "trouble") - { - out.print("%s",changelayer_trouble.c_str()); - return CR_OK; + return CR_WRONG_USAGE; } if(parameters[i] == "force") force = true; From d3dd12c38f8f7302a288c26c052415f2556c588f Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:24:27 -0700 Subject: [PATCH 062/334] update docs for changevein --- docs/plugins/changevein.rst | 25 +++++++++++++++++++------ plugins/changevein.cpp | 3 +-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index ad7ab3d54..f4871772e 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -1,10 +1,23 @@ changevein ========== -Changes material of the vein under cursor to the specified inorganic RAW -material. Only affects tiles within the current 16x16 block - for veins and -large clusters, you will need to use this command multiple times. -Example: +Tags: +:dfhack-keybind: -``changevein NATIVE_PLATINUM`` - Convert vein at cursor position into platinum ore. +Changes the material of a mineral inclusion. You can change it to any incorganic +material RAW id. Note that this command only affects tiles within the current +16x16 block - for large veins and clusters, you will need to use this command +multiple times. + +You can use the `probe` command to discover the material RAW ids for existing +veins that you want to duplicate. + +Usage:: + + changevein + +Example +------- + +- ``changevein NATIVE_PLATINUM`` + Convert vein at cursor position into platinum ore. diff --git a/plugins/changevein.cpp b/plugins/changevein.cpp index 8612603fc..48a1e7325 100644 --- a/plugins/changevein.cpp +++ b/plugins/changevein.cpp @@ -86,8 +86,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector \n")); + df_changevein)); return CR_OK; } From 1f3c4cdd18d9d7f67476d3104ded092161dd92aa Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:26:30 -0700 Subject: [PATCH 063/334] update docs for cleanconst --- docs/plugins/cleanconst.rst | 15 +++++++++++---- plugins/cleanconst.cpp | 9 +++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index a8169d83b..c34bac6bf 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -1,7 +1,14 @@ cleanconst ========== -Cleans up construction materials. -This utility alters all constructions on the map so that they spawn their -building component when they are disassembled, allowing their actual -build items to be safely deleted. This can improve FPS in extreme situations. +Tags: +:dfhack-keybind: + +Cleans up construction materials. This tool alters all constructions on the map +so that they spawn their building component when they are disassembled, allowing +their actual build items to be safely deleted. This can improve FPS when you +have many constructions on the map. + +Usage:: + + cleanconst diff --git a/plugins/cleanconst.cpp b/plugins/cleanconst.cpp index 84659c5d7..38149abd3 100644 --- a/plugins/cleanconst.cpp +++ b/plugins/cleanconst.cpp @@ -71,12 +71,9 @@ command_result df_cleanconst(color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "cleanconst", "Cleans up construction materials.", - df_cleanconst, false, - " This utility alters all constructions on the map so that they spawn their\n" - " building component when they are disassembled, allowing their actual\n" - " build items to be safely deleted.\n" - )); + "cleanconst", + "Cleans up construction materials.", + df_cleanconst)); return CR_OK; } From 048b20ac45c8ff4fde36e32f0ee2c62315efb427 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:34:29 -0700 Subject: [PATCH 064/334] no help entries for non-enableable plugins --- library/LuaApi.cpp | 16 ++++++++++++++++ library/lua/helpdb.lua | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index c332933a2..770aab870 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3147,6 +3147,21 @@ static int internal_getCommandHelp(lua_State *L) return 1; } +static int internal_isPluginEnableable(lua_State *L) +{ + auto plugins = Core::getInstance().getPluginManager(); + + const char *name = luaL_checkstring(L, 1); + + auto plugin = plugins->getPluginByName(name); + if (plugin) + lua_pushboolean(L, plugin->can_be_enabled()); + else + lua_pushnil(L); + + return 1; +} + static int internal_threadid(lua_State *L) { std::stringstream ss; @@ -3221,6 +3236,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "listPlugins", internal_listPlugins }, { "listCommands", internal_listCommands }, { "getCommandHelp", internal_getCommandHelp }, + { "isPluginEnableable", internal_isPluginEnableable }, { "threadid", internal_threadid }, { "md5File", internal_md5file }, { NULL, NULL } diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index f6f9ed864..e35d2f125 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -305,7 +305,8 @@ local function scan_plugins(old_db, db) HELP_SOURCES.RENDERED or HELP_SOURCES.PLUGIN, command, {entry_types=entry_types}) end - if not includes_plugin then + if not includes_plugin and + dfhack.internal.isPluginEnableable(plugin) then update_db(old_db, db, has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, From e9e477c680948ea016e8c4dffe61c10ae6168ffe Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 13:51:03 -0700 Subject: [PATCH 065/334] update docs for clean and spotclean --- docs/plugins/clean.rst | 41 ++++++++++++++++++++++++++++---------- docs/plugins/spotclean.rst | 14 ++++++++++--- plugins/cleaners.cpp | 26 ++++++------------------ 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/docs/plugins/clean.rst b/docs/plugins/clean.rst index f2d0c361d..54421ce91 100644 --- a/docs/plugins/clean.rst +++ b/docs/plugins/clean.rst @@ -1,16 +1,37 @@ clean ===== -Cleans all the splatter that get scattered all over the map, items and -creatures. In an old fortress, this can significantly reduce FPS lag. It can -also spoil your !!FUN!!, so think before you use it. -Options: +Tags: +:dfhack-keybind: -:map: Clean the map tiles. By default, it leaves mud and snow alone. -:units: Clean the creatures. Will also clean hostiles. -:items: Clean all the items. Even a poisoned blade. +Removes contaminants from tiles, items, and units. More specifically, it +cleans all the splatter that get scattered all over the map and that clings to +your items and units. In an old fortress, this can significantly reduce FPS lag. +It can also spoil your !!FUN!!, so think before you use it. -Extra options for ``map``: +Usage:: -:mud: Remove mud in addition to the normal stuff. -:snow: Also remove snow coverings. + clean all|map|items|units|plants [] + +By default, cleaning the map leaves mud and snow alone. Note that cleaning units +includes hostiles, and that cleaning items removes poisons from weapons. + +Options +------- + +When cleaning the map, you can specify extra options for extra cleaning: + +- ``mud`` + Also remove mud. +- ``item`` + Also remove item spatter, like fallen leaves and flowers. +- ``snow`` + Also remove snow coverings. + +Examples +-------- + +- ``clean all`` + Clean everything that can be cleaned (except mud and snow). +- ``clean all mud item snow`` + Removes all spatter, including mud, leaves, and snow from map tiles. diff --git a/docs/plugins/spotclean.rst b/docs/plugins/spotclean.rst index 9ccdd2cc6..dcad047a4 100644 --- a/docs/plugins/spotclean.rst +++ b/docs/plugins/spotclean.rst @@ -1,6 +1,14 @@ spotclean ========= -Works like ``clean map snow mud``, but only for the tile under the cursor. Ideal -if you want to keep that bloody entrance ``clean map`` would clean up. -:dfhack-keybind:`spotclean` +Tags: +:dfhack-keybind: + +Cleans a map tile of contaminants and spatter. It works like +``clean map snow mud``, but only for the tile under the cursor. Ideal if you +just want to clean a specific tile but don't want the `clean` command to remove +all the glorious blood from your entranceway. + +Usage:: + + spotclean diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 24694c6a2..7389488f1 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -241,27 +241,13 @@ command_result clean (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "clean","Remove contaminants from tiles, items and creatures.", - clean, false, - " Removes contaminants from map tiles, items and creatures.\n" - "Options:\n" - " map - clean the map tiles\n" - " items - clean all items\n" - " units - clean all creatures\n" - " plants - clean all plants\n" - " all - clean everything.\n" - "More options for 'map':\n" - " snow - also remove snow\n" - " mud - also remove mud\n" - " item - also remove item spatters (e.g. leaves and flowers)\n" - "Example:\n" - " clean all mud snow item\n" - " Removes all spatter, including mud and snow from map tiles.\n" - )); + "clean", + "Remove contaminants from tiles, items and creatures.", + clean)); commands.push_back(PluginCommand( - "spotclean","Cleans map tile under cursor.", - spotclean,Gui::cursor_hotkey - )); + "spotclean", + "Cleans map tile under cursor.", + spotclean,Gui::cursor_hotkey)); return CR_OK; } From d9a1104473f8aa9b3eaf264cd21bfbeddd50943c Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 14:51:06 -0700 Subject: [PATCH 066/334] update docs for cleanowned --- docs/plugins/cleanowned.rst | 42 ++++++++++++++++++++++++++----------- plugins/cleanowned.cpp | 19 +++-------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst index 7f80e35cf..27e4806a2 100644 --- a/docs/plugins/cleanowned.rst +++ b/docs/plugins/cleanowned.rst @@ -1,19 +1,37 @@ cleanowned ========== -Confiscates items owned by dwarfs. By default, owned food on the floor -and rotten items are confistacted and dumped. -Options: +Tags: +:dfhack-keybind: -:all: confiscate all owned items -:scattered: confiscated and dump all items scattered on the floor -:x: confiscate/dump items with wear level 'x' and more -:X: confiscate/dump items with wear level 'X' and more -:dryrun: a dry run. combine with other options to see what will happen - without it actually happening. +Confiscates and dumps garbage owned by dwarves. This tool gets dwarves to give +up ownership of scattered items and items with heavy wear and then marks those +items for dumping. Now you can finally get your dwarves to give up their rotten +food and tattered loincloths and go get new ones! -Example: +Usage:: -``cleanowned scattered X`` - This will confiscate rotten and dropped food, garbage on the floors and any + cleanowned [] [dryrun] + +When run without parameters, ``cleanowned`` will confiscate and dump rotten +items and owned food that is left behind on the floor. Specify the ``dryrun`` +parameter to just print out what would be done, but don't actually confiscate +anything. + +You can confiscate additional types of items by adding them to the commandline: + +- ``scattered`` + Confiscate/dump all items scattered on the floor. +- ``x`` + Confiscate/dump items with wear level 'x' (lightly worn) and more. +- ``X`` + Confiscate/dump items with wear level 'X' (heavily worn) and more. + +Or you can confiscate all owned items by specifying ``all``. + +Example +------- + +- ``cleanowned scattered X`` + Confiscate and dump rotten and dropped food, garbage on the floors, and any worn items with 'X' damage and above. diff --git a/plugins/cleanowned.cpp b/plugins/cleanowned.cpp index 90b0e743d..c1e5b31d4 100644 --- a/plugins/cleanowned.cpp +++ b/plugins/cleanowned.cpp @@ -31,22 +31,9 @@ command_result df_cleanowned (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "cleanowned", "Confiscates and dumps garbage owned by dwarfs.", - df_cleanowned, false, - " This tool lets you confiscate and dump all the garbage\n" - " dwarves ultimately accumulate.\n" - " By default, only rotten and dropped food is confiscated.\n" - "Options:\n" - " dryrun - don't actually do anything, just print what would be done.\n" - " scattered - confiscate owned items on the ground\n" - " all - confiscate everything\n" - " x - confiscate & dump 'x' and worse damaged items\n" - " X - confiscate & dump 'X' and worse damaged items\n" - "Example:\n" - " cleanowned scattered X\n" - " This will confiscate rotten and dropped food, garbage on the floors\n" - " and any worn items with 'X' damage and above.\n" - )); + "cleanowned", + "Confiscates and dumps garbage owned by dwarves.", + df_cleanowned)); return CR_OK; } From 9dcb63da53a3dd1b641e1a6da9b65dac53a49b5c Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 20 Jul 2022 15:36:17 -0700 Subject: [PATCH 067/334] don't bork on no frame, set cursor to end of text --- library/lua/gui/widgets.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index aea62bf87..5765992af 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -121,12 +121,12 @@ ResizingPanel = defclass(ResizingPanel, Panel) -- adjust our frame dimensions according to positions and sizes of our subviews function ResizingPanel:postUpdateLayout(frame_body) local w, h = 0, 0 - for _,subview in ipairs(self.subviews) do - if subview.visible then - w = math.max(w, (subview.frame.l or 0) + - (subview.frame.w or frame_body.width)) - h = math.max(h, (subview.frame.t or 0) + - (subview.frame.h or frame_body.height)) + for _,s in ipairs(self.subviews) do + if s.visible then + w = math.max(w, (s.frame and s.frame.l or 0) + + (s.frame and s.frame.w or frame_body.width)) + h = math.max(h, (s.frame and s.frame.t or 0) + + (s.frame and s.frame.h or frame_body.height)) end end if not self.frame then self.frame = {} end @@ -191,8 +191,8 @@ EditField.ATTRS{ } function EditField:preinit(init_table) - local frame = init_table.frame or {} - frame.h = frame.h or 1 + init_table.frame = init_table.frame or {} + init_table.frame.h = init_table.frame.h or 1 end function EditField:init() @@ -202,7 +202,7 @@ function EditField:init() end self.start_pos = 1 - self.cursor = 1 + self.cursor = #self.text + 1 self:addviews{HotkeyLabel{frame={t=0,l=0}, key=self.key, From 68b8837a8d8b37767a604d136b622ac51733aa09 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 22 Jul 2022 00:30:36 -0400 Subject: [PATCH 068/334] Replace docs/build*.sh with more flexible build.py Notable changes: - can build any combination of output formats in series - `-E` is no longer passed by default to aid in development, but can be passed manually --- docs/build-pdf.sh | 4 +-- docs/build.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++ docs/build.sh | 5 +-- 3 files changed, 92 insertions(+), 7 deletions(-) create mode 100755 docs/build.py diff --git a/docs/build-pdf.sh b/docs/build-pdf.sh index 735ef2faa..a7527cb34 100755 --- a/docs/build-pdf.sh +++ b/docs/build-pdf.sh @@ -9,6 +9,4 @@ # https://www.sphinx-doc.org/en/master/man/sphinx-build.html cd $(dirname "$0") -cd .. - -"${SPHINX:-sphinx-build}" -M latexpdf -d build/docs/pdf . docs/pdf -w build/docs/pdf/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" +python3 build.py pdf "$@" diff --git a/docs/build.py b/docs/build.py new file mode 100755 index 000000000..2a42d0968 --- /dev/null +++ b/docs/build.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +# for help, run: python3 build.py --help + +import argparse +import os +import subprocess +import sys + +class SphinxOutputFormat: + def __init__(self, name, pre_args): + self.name = str(name) + self.pre_args = tuple(pre_args) + + @property + def args(self): + output_dir = os.path.join('docs', self.name) + artifacts_dir = os.path.join('build', 'docs', self.name) # for artifacts not part of the final documentation + return [ + *self.pre_args, + '.', # source dir + output_dir, + '-d', artifacts_dir, + '-w', os.path.join(artifacts_dir, 'sphinx-warnings.txt'), + ] + +OUTPUT_FORMATS = { + 'html': SphinxOutputFormat('html', pre_args=['-b', 'html']), + 'text': SphinxOutputFormat('text', pre_args=['-b', 'text']), + 'pdf': SphinxOutputFormat('pdf', pre_args=['-M', 'latexpdf']), +} + +def _parse_known_args(parser, source_args): + # pass along any arguments after '--' + ignored_args = [] + if '--' in source_args: + source_args, ignored_args = source_args[:source_args.index('--')], source_args[source_args.index('--')+1:] + args, forward_args = parser.parse_known_args(source_args) + forward_args += ignored_args + return args, forward_args + +def parse_args(source_args): + def output_format(s): + if s in OUTPUT_FORMATS: + return s + raise ValueError + + parser = argparse.ArgumentParser(usage='%(prog)s [{} ...] [options] [--] [sphinx_options]'.format('|'.join(OUTPUT_FORMATS.keys())), description=''' + DFHack wrapper around sphinx-build. + + Any unrecognized options are passed directly to sphinx-build, as well as any + options following a '--' argument, if specified. + ''') + parser.add_argument('format', nargs='*', type=output_format, action='append', + help='Documentation format(s) to build - choose from {}'.format(', '.join(OUTPUT_FORMATS.keys()))) + parser.add_argument('-E', '--clean', action='store_true', + help='Re-read all input files') + parser.add_argument('--sphinx', type=str, default=os.environ.get('SPHINX', 'sphinx-build'), + help='Sphinx executable to run [environment variable: SPHINX; default: "sphinx-build"]') + parser.add_argument('-j', '--jobs', type=str, default=os.environ.get('JOBS', 'auto'), + help='Number of Sphinx threads to run [environment variable: JOBS; default: "auto"]') + parser.add_argument('--debug', action='store_true', + help='Log commands that are run, etc.') + args, forward_args = _parse_known_args(parser, source_args) + + # work around weirdness with list args + args.format = args.format[0] + if not args.format: + args.format = ['html'] + + return args, forward_args + +if __name__ == '__main__': + os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + if not os.path.isfile('conf.py'): + print('Could not find conf.py', file=sys.stderr) + exit(1) + + args, forward_args = parse_args(sys.argv[1:]) + for format_name in args.format: + command = [args.sphinx] + OUTPUT_FORMATS[format_name].args + ['-j', args.jobs] + if args.clean: + command += ['-E'] + command += forward_args + + if args.debug: + print('Building:', format_name) + print('Running:', command) + subprocess.call(command) + print('') diff --git a/docs/build.sh b/docs/build.sh index 28182d9c0..7a6bd0b33 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -9,7 +9,4 @@ # https://www.sphinx-doc.org/en/master/man/sphinx-build.html cd $(dirname "$0") -cd .. - -"${SPHINX:-sphinx-build}" -E -b html -d build/docs/html . docs/html -w build/docs/html/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" -"${SPHINX:-sphinx-build}" -E -b text -d build/docs/text . docs/text -w build/docs/text/_sphinx-warnings.txt -j "${JOBS:-auto}" "$@" +python3 build.py html text "$@" From 4b0b0e02f87956e42ed19de72b93c00853196b0a Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 22 Jul 2022 00:46:44 -0400 Subject: [PATCH 069/334] Update Documentation.rst for new workflow and output formats --- docs/Documentation.rst | 48 ++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index acfffdbbb..c035a0366 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -261,30 +261,45 @@ ways to do this: it to add the flag. You can also run ``cmake`` on the command line, similar to other platforms. -The generated documentation will be stored in ``docs/html`` in the root DFHack -folder, and will be installed to ``hack/docs`` when you next install DFHack in a -DF folder. +By default, both HTML and text docs are built by CMake. The generated +documentation is stored in ``docs/html`` and ``docs/text`` (respectively) in the +root DFHack folder, and will be installed to ``hack/docs`` when you install +DFHack. Running Sphinx manually ----------------------- You can also build the documentation without running CMake - this is faster if -you only want to rebuild the documentation regardless of any code changes. There -is a ``docs/build.sh`` script provided for Linux and macOS that will run -essentially the same command that CMake runs when building the docs - see the -script for additional options. +you only want to rebuild the documentation regardless of any code changes. The +``docs/build.py`` script will build the documentation in any specified formats +(HTML only by default) using essentially the same command that CMake runs when +building the docs. Run the script with ``--help`` to see additional options. -To build the documentation with default options, run the following command from -the root DFHack folder:: +Examples: + +* ``docs/build.py`` + Build just the HTML docs + +* ``docs/build.py html text`` + Build both the HTML and text docs + +* ``docs/build.py --clean`` + Build HTML and force a clean build (all source files are re-read) + +The resulting documentation will be stored in ``docs/html`` and/or ``docs/text``. + +Alternatively, you can run Sphinx manually with:: sphinx-build . docs/html -The resulting documentation will be stored in ``docs/html`` (you can specify -a different path when running ``sphinx-build`` manually, but be warned that -Sphinx may overwrite existing files in this folder). +or, to build plain-text output:: + + sphinx-build -b text . docs/text Sphinx has many options to enable clean builds, parallel builds, logging, and -more - run ``sphinx-build --help`` for details. +more - run ``sphinx-build --help`` for details. If you specify a different +output path, be warned that Sphinx may overwrite existing files in the output +folder. Building a PDF version ---------------------- @@ -295,10 +310,11 @@ want to build a PDF version locally, you will need ``pdflatex``, which is part of a TeX distribution. The following command will then build a PDF, located in ``docs/pdf/latex/DFHack.pdf``, with default options:: - sphinx-build -M latexpdf . docs/pdf + docs/build.py pdf -There is a ``docs/build-pdf.sh`` script provided for Linux and macOS that runs -this command for convenience - see the script for additional options. +Alternatively, you can run Sphinx manually with:: + + sphinx-build -M latexpdf . docs/pdf .. _build-changelog: From 5521a5a45d29c60bd39ee7231dfebc93946d9932 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 22 Jul 2022 00:47:33 -0400 Subject: [PATCH 070/334] Remove old build*.sh scripts --- docs/build-pdf.sh | 12 ------------ docs/build.sh | 12 ------------ 2 files changed, 24 deletions(-) delete mode 100755 docs/build-pdf.sh delete mode 100755 docs/build.sh diff --git a/docs/build-pdf.sh b/docs/build-pdf.sh deleted file mode 100755 index a7527cb34..000000000 --- a/docs/build-pdf.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# usage: -# ./build-pdf.sh -# SPHINX=/path/to/sphinx-build ./build-pdf.sh -# JOBS=3 ./build-pdf.sh ... -# all command-line arguments are passed directly to sphinx-build - run -# ``sphinx-build --help`` for a list, or see -# https://www.sphinx-doc.org/en/master/man/sphinx-build.html - -cd $(dirname "$0") -python3 build.py pdf "$@" diff --git a/docs/build.sh b/docs/build.sh deleted file mode 100755 index 7a6bd0b33..000000000 --- a/docs/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# usage: -# ./build.sh -# SPHINX=/path/to/sphinx-build ./build.sh -# JOBS=3 ./build.sh ... -# all command-line arguments are passed directly to sphinx-build - run -# ``sphinx-build --help`` for a list, or see -# https://www.sphinx-doc.org/en/master/man/sphinx-build.html - -cd $(dirname "$0") -python3 build.py html text "$@" From 2ce7518562b31116feb9bd0cb925838e07f25d40 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 21 Jul 2022 22:33:43 -0700 Subject: [PATCH 071/334] read plugin command docs from single plugin file --- library/LuaApi.cpp | 65 ++++++---- library/lua/helpdb.lua | 273 +++++++++++++++++++++++------------------ 2 files changed, 196 insertions(+), 142 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 770aab870..4ee1a4d1c 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3109,41 +3109,58 @@ static int internal_listCommands(lua_State *L) } return 1; } -static int internal_getCommandHelp(lua_State *L) + +static const PluginCommand * getPluginCommand(const char * command) { auto plugins = Core::getInstance().getPluginManager(); - - const char *name = luaL_checkstring(L, 1); - - auto plugin = plugins->getPluginByCommand(name); + auto plugin = plugins->getPluginByCommand(command); if (!plugin) { - lua_pushnil(L); - return 1; + return NULL; } size_t num_commands = plugin->size(); for (size_t i = 0; i < num_commands; ++i) { - if ((*plugin)[i].name == name) - { - const auto &pc = (*plugin)[i]; - std::string help = pc.description; - if (help.size() && help[help.size()-1] != '.') - { - help += "."; - } - if (pc.usage.size()) - { - help += "\n" + pc.usage; - } - lua_pushstring(L, help.c_str()); - return 1; - } + if ((*plugin)[i].name == command) + return &(*plugin)[i]; } // not found (somehow) - lua_pushnil(L); + return NULL; +} + +static int internal_getCommandHelp(lua_State *L) +{ + const PluginCommand *pc = getPluginCommand(luaL_checkstring(L, 1)); + if (!pc) + { + lua_pushnil(L); + return 1; + } + + std::string help = pc->description; + if (help.size() && help[help.size()-1] != '.') + help += "."; + if (pc->usage.size()) + help += "\n" + pc->usage; + lua_pushstring(L, help.c_str()); + return 1; +} + +static int internal_getCommandDescription(lua_State *L) +{ + const PluginCommand *pc = getPluginCommand(luaL_checkstring(L, 1)); + if (!pc) + { + lua_pushnil(L); + return 1; + } + + std::string help = pc->description; + if (help.size() && help[help.size()-1] != '.') + help += "."; + lua_pushstring(L, help.c_str()); return 1; } @@ -3218,6 +3235,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "getAddress", internal_getAddress }, { "setAddress", internal_setAddress }, { "getVTable", internal_getVTable }, + { "adjustOffset", internal_adjustOffset }, { "getMemRanges", internal_getMemRanges }, { "patchMemory", internal_patchMemory }, @@ -3236,6 +3254,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "listPlugins", internal_listPlugins }, { "listCommands", internal_listCommands }, { "getCommandHelp", internal_getCommandHelp }, + { "getCommandDescription", internal_getCommandDescription }, { "isPluginEnableable", internal_isPluginEnableable }, { "threadid", internal_threadid }, { "md5File", internal_md5file }, diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index e35d2f125..647d08996 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -32,58 +32,75 @@ local ENTRY_TYPES = { } local HELP_SOURCES = { - STUB='stub', - RENDERED='rendered', - PLUGIN='plugin', - SCRIPT='script', + RENDERED='rendered', -- from the installed, rendered help text + PLUGIN='plugin', -- from the plugin source code + SCRIPT='script', -- from the script source code + STUB='stub', -- from a generated stub } --- builtins +-- builtin command names, with aliases mapped to their canonical form local BUILTINS = { - 'alias', - 'clear', - 'cls', - 'devel/dump-rpc', - 'die', - 'dir', - 'disable', - 'enable', - 'fpause', - 'help', - 'hide', - 'keybinding', - 'kill-lua', - 'load', - 'ls', - 'man', - 'plug', - 'reload', - 'script', - 'sc-script', - 'show', - 'tags', - 'type', - 'unload', + alias=true, + clear='cls', + cls=true, + ['devel/dump-rpc']=true, + die=true, + dir='ls', + disable=true, + enable=true, + fpause=true, + help=true, + hide=true, + keybinding=true, + ['kill-lua']=true, + ['load']=true, + ls=true, + man='help', + plug=true, + reload=true, + script=true, + ['sc-script']=true, + show=true, + tags=true, + ['type']=true, + unload=true, } +--------------------------------------------------------------------------- +-- data structures +--------------------------------------------------------------------------- + +-- help text database, keys are a subset of the entry database -- entry name -> { --- entry_types (set of ENTRY_TYPES), +-- help_source (element of HELP_SOURCES), -- short_help (string), -- long_help (string), -- tags (set), --- help_source (element of HELP_SOURCES), -- source_timestamp (mtime, 0 for non-files), -- source_path (string, nil for non-files) -- } +textdb = textdb or {} + +-- entry database, points to text in textdb +-- entry name -> { +-- entry_types (set of ENTRY_TYPES), +-- short_help (string, if not nil then overrides short_help in text_entry), +-- text_entry (string) +-- } -- -- entry_types is a set because plugin commands can also be the plugin names. -db = db or {} +entrydb = entrydb or {} + -- tag name -> list of entry names -- Tags defined in the TAG_DEFINITIONS file that have no associated db entries -- will have an empty list. tag_index = tag_index or {} +--------------------------------------------------------------------------- +-- data ingestion +--------------------------------------------------------------------------- + local function get_rendered_path(entry_name) return RENDERED_PATH .. entry_name .. '.txt' end @@ -98,18 +115,16 @@ local DEFAULT_HELP_TEMPLATE = [[ No help available. ]] -local function make_default_entry(entry_name, entry_types, source, - source_timestamp, source_path) +local function make_default_entry(entry_name, help_source, kwargs) local default_long_help = DEFAULT_HELP_TEMPLATE:format( entry_name, ('*'):rep(#entry_name)) return { - entry_types=entry_types, + help_source=help_source, short_help='No help available.', long_help=default_long_help, tags={}, - help_source=source, - source_timestamp=source_timestamp or 0, - source_path=source_path} + source_timestamp=kwargs.source_timestamp or 0, + source_path=kwargs.source_path} end -- updates the short_text, the long_text, and the tags in the given entry based @@ -129,7 +144,8 @@ local function update_entry(entry, iterator, opts) local lines = {} local first_line_is_short_help = opts.first_line_is_short_help local begin_marker_found,header_found = not opts.begin_marker,opts.no_header - local tags_found, short_help_found, in_short_help = false, false, false + local tags_found, short_help_found = false, opts.skip_short_help + local in_short_help = false for line in iterator do if not short_help_found and first_line_is_short_help then line = line:trim() @@ -193,31 +209,30 @@ local function update_entry(entry, iterator, opts) end -- create db entry based on parsing sphinx-rendered help text -local function make_rendered_entry(old_entry, entry_name, entry_types) - local rendered_path = get_rendered_path(entry_name) - local source_timestamp = dfhack.filesystem.mtime(rendered_path) - if old_entry and old_entry.source == HELP_SOURCES.RENDERED and +local function make_rendered_entry(old_entry, entry_name, kwargs) + local source_path = get_rendered_path(entry_name) + local source_timestamp = dfhack.filesystem.mtime(source_path) + if old_entry and old_entry.help_source == HELP_SOURCES.RENDERED and old_entry.source_timestamp >= source_timestamp then -- we already have the latest info return old_entry end - local entry = make_default_entry(entry_name, entry_types, - HELP_SOURCES.RENDERED, source_timestamp, rendered_path) - update_entry(entry, io.lines(rendered_path)) + kwargs.source_path, kwargs.source_timestamp = source_path, source_timestamp + local entry = make_default_entry(entry_name, HELP_SOURCES.RENDERED, kwargs) + update_entry(entry, io.lines(source_path)) return entry end -- create db entry based on the help text in the plugin source (used by -- out-of-tree plugins) -local function make_plugin_entry(old_entry, entry_name, entry_types) +local function make_plugin_entry(old_entry, entry_name, kwargs) if old_entry and old_entry.source == HELP_SOURCES.PLUGIN then -- we can't tell when a plugin is reloaded, so we can either choose to -- always refresh or never refresh. let's go with never for now for -- performance. return old_entry end - local entry = make_default_entry(entry_name, entry_types, - HELP_SOURCES.PLUGIN) + local entry = make_default_entry(entry_name, HELP_SOURCES.PLUGIN, kwargs) local long_help = dfhack.internal.getCommandHelp(entry_name) if long_help and #long_help:trim() > 0 then update_entry(entry, long_help:trim():gmatch('[^\n]*'), {no_header=true}) @@ -227,19 +242,22 @@ end -- create db entry based on the help text in the script source (used by -- out-of-tree scripts) -local function make_script_entry(old_entry, entry_name, script_source_path) - local source_timestamp = dfhack.filesystem.mtime(script_source_path) +local function make_script_entry(old_entry, entry_name, kwargs) + local source_path = kwargs.source_path + local source_timestamp = dfhack.filesystem.mtime(source_path) if old_entry and old_entry.source == HELP_SOURCES.SCRIPT and - old_entry.script_source_path == script_source_path and + old_entry.source_path == source_path and old_entry.source_timestamp >= source_timestamp then -- we already have the latest info return old_entry end - local entry = make_default_entry(entry_name, {[ENTRY_TYPES.COMMAND]=true}, - HELP_SOURCES.SCRIPT, source_timestamp, script_source_path) - local ok, lines = pcall(io.lines, script_source_path) - if not ok then return entry end - local is_rb = script_source_path:endswith('.rb') + kwargs.source_timestamp, kwargs.entry_type = source_timestamp + local entry = make_default_entry(entry_name, HELP_SOURCES.SCRIPT, kwargs) + local ok, lines = pcall(io.lines, source_path) + if not ok then + return entry + end + local is_rb = source_path:endswith('.rb') update_entry(entry, lines, {begin_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_BEGIN), end_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_END), @@ -247,78 +265,81 @@ local function make_script_entry(old_entry, entry_name, script_source_path) return entry end --- updates the db (and associated tag index) with a new entry if the entry_name --- doesn't already exist in the db. -local function update_db(old_db, db, source, entry_name, kwargs) - if db[entry_name] then +-- updates the dbs (and associated tag index) with a new entry if the entry_name +-- doesn't already exist in the dbs. +local function update_db(old_db, entry_name, text_entry, help_source, kwargs) + if entrydb[entry_name] then -- already in db (e.g. from a higher-priority script dir); skip return end - local entry, old_entry = nil, old_db[entry_name] - if source == HELP_SOURCES.RENDERED then - entry = make_rendered_entry(old_entry, entry_name, kwargs.entry_types) - elseif source == HELP_SOURCES.PLUGIN then - entry = make_plugin_entry(old_entry, entry_name, kwargs.entry_types) - elseif source == HELP_SOURCES.SCRIPT then - entry = make_script_entry(old_entry, entry_name, kwargs.script_source) - elseif source == HELP_SOURCES.STUB then - entry = make_default_entry(entry_name, kwargs.entry_types, - HELP_SOURCES.STUB) - else - error('unhandled help source: ' .. source) + entrydb[entry_name] = { + entry_types=kwargs.entry_types, + short_help=kwargs.short_help, + text_entry=text_entry + } + if entry_name ~= text_entry then + return end - db[entry_name] = entry - for tag in pairs(entry.tags) do - -- ignore unknown tags - if tag_index[tag] then - table.insert(tag_index[tag], entry_name) - end + + local text_entry, old_entry = nil, old_db[entry_name] + if help_source == HELP_SOURCES.RENDERED then + text_entry = make_rendered_entry(old_entry, entry_name, kwargs) + elseif help_source == HELP_SOURCES.PLUGIN then + text_entry = make_plugin_entry(old_entry, entry_name, kwargs) + elseif help_source == HELP_SOURCES.SCRIPT then + text_entry = make_script_entry(old_entry, entry_name, kwargs) + elseif help_source == HELP_SOURCES.STUB then + text_entry = make_default_entry(entry_name, HELP_SOURCES.STUB, kwargs) + else + error('unhandled help source: ' .. help_source) end + textdb[entry_name] = text_entry end -- add the builtin commands to the db -local function scan_builtins(old_db, db) +local function scan_builtins(old_db) local entry_types = {[ENTRY_TYPES.BUILTIN]=true, [ENTRY_TYPES.COMMAND]=true} - for _,builtin in ipairs(BUILTINS) do - update_db(old_db, db, - has_rendered_help(builtin) and - HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, - builtin, - {entry_types=entry_types}) + for builtin,canonical in pairs(BUILTINS) do + if canonical == true then canonical = builtin end + update_db(old_db, builtin, canonical, + has_rendered_help(canonical) and + HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, + {entry_types=entry_types}) end end --- scan for plugins and plugin-provided commands and add their help to the db -local function scan_plugins(old_db, db) +-- scan for enableable plugins and plugin-provided commands and add their help +-- to the db +local function scan_plugins(old_db) local plugin_names = dfhack.internal.listPlugins() for _,plugin in ipairs(plugin_names) do local commands = dfhack.internal.listCommands(plugin) - local includes_plugin = false + local includes_plugin, has_commands = false, false for _,command in ipairs(commands) do - local entry_types = {[ENTRY_TYPES.COMMAND]=true} + local kwargs = {entry_types={[ENTRY_TYPES.COMMAND]=true}} if command == plugin then - entry_types[ENTRY_TYPES.PLUGIN]=true + kwargs.entry_types[ENTRY_TYPES.PLUGIN]=true includes_plugin = true end - update_db(old_db, db, - has_rendered_help(command) and + kwargs.short_help = dfhack.internal.getCommandDescription(command) + update_db(old_db, command, plugin, + has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.PLUGIN, - command, {entry_types=entry_types}) + kwargs) + has_commands = true end - if not includes_plugin and - dfhack.internal.isPluginEnableable(plugin) then - update_db(old_db, db, + if not includes_plugin and (has_commands or + dfhack.internal.isPluginEnableable(plugin)) then + update_db(old_db, plugin, plugin, has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, - plugin, {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) - goto continue + {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) end - ::continue:: end end -- scan for scripts and add their help to the db -local function scan_scripts(old_db, db) +local function scan_scripts(old_db) local entry_types = {[ENTRY_TYPES.COMMAND]=true} for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( @@ -333,12 +354,11 @@ local function scan_scripts(old_db, db) end local dot_index = f.path:find('%.[^.]*$') local entry_name = f.path:sub(1, dot_index - 1) - local script_source = script_path .. '/' .. f.path - update_db(old_db, db, + local source_path = script_path .. '/' .. f.path + update_db(old_db, entry_name, entry_name, has_rendered_help(entry_name) and HELP_SOURCES.RENDERED or HELP_SOURCES.SCRIPT, - entry_name, - {entry_types=entry_types, script_source=script_source}) + {entry_types=entry_types, source_path=source_path}) ::continue:: end ::skip_path:: @@ -370,6 +390,17 @@ local function initialize_tags() end end +local function index_tags() + for entry_name,entry in pairs(entrydb) do + for tag in pairs(textdb[entry.text_entry].tags) do + -- ignore unknown tags + if tag_index[tag] then + table.insert(tag_index[tag], entry_name) + end + end + end +end + -- ensures the db is up to date by scanning all help sources. does not do -- anything if it has already been run within the last 60 seconds. last_refresh_ms = last_refresh_ms or 0 @@ -378,13 +409,14 @@ local function ensure_db() if now_ms - last_refresh_ms < 60000 then return end last_refresh_ms = now_ms - local old_db = db - db, tag_index = {}, {} + local old_db = textdb + textdb, entrydb, tag_index = {}, {}, {} initialize_tags() - scan_builtins(old_db, db) - scan_plugins(old_db, db) - scan_scripts(old_db, db) + scan_builtins(old_db) + scan_plugins(old_db) + scan_scripts(old_db) + index_tags() end --------------------------------------------------------------------------- @@ -415,15 +447,16 @@ end -- returns whether the given string (or list of strings) is an entry in the db function is_entry(str) - return has_keys(str, db) + return has_keys(str, entrydb) end local function get_db_property(entry_name, property) ensure_db() - if not db[entry_name] then + if not entrydb[entry_name] then error(('helpdb entry not found: "%s"'):format(entry_name)) end - return db[entry_name][property] + return entrydb[entry_name][property] or + textdb[entrydb[entry_name].text_entry][property] end -- returns the ~54 char summary blurb associated with the entry @@ -504,11 +537,11 @@ local function sort_by_basename(a, b) end local function matches(entry_name, filter) - local db_entry = db[entry_name] if filter.tag then local matched = false + local tags = get_db_property(entry_name, 'tags') for _,tag in ipairs(filter.tag) do - if db_entry.tags[tag] then + if tags[tag] then matched = true break end @@ -519,8 +552,9 @@ local function matches(entry_name, filter) end if filter.types then local matched = false + local etypes = get_db_property(entry_name, 'entry_types') for _,etype in ipairs(filter.types) do - if db_entry.entry_types[etype] then + if etypes[etype] then matched = true break end @@ -577,7 +611,7 @@ function search_entries(include, exclude) include = normalize_filter(include) exclude = normalize_filter(exclude) local entries = {} - for entry in pairs(db) do + for entry in pairs(entrydb) do if (not include or matches(entry, include)) and (not exclude or not matches(entry, exclude)) then table.insert(entries, entry) @@ -595,7 +629,8 @@ end function is_builtin(command) ensure_db() - return db[command] and db[command].entry_types[ENTRY_TYPES.BUILTIN] + return entrydb[command] and + get_db_property(entry_name, 'entry_types')[ENTRY_TYPES.BUILTIN] end --------------------------------------------------------------------------- @@ -605,7 +640,7 @@ end -- implements the 'help' builtin command function help(entry) ensure_db() - if not db[entry] then + if not entrydb[entry] then dfhack.printerr(('No help entry found for "%s"'):format(entry)) return end From 4b1696f783e46196bf0e5fbb5a2f71551305367a Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 21 Jul 2022 22:36:17 -0700 Subject: [PATCH 072/334] add '?' alias for help --- library/lua/helpdb.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 647d08996..c660a37dd 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -40,6 +40,7 @@ local HELP_SOURCES = { -- builtin command names, with aliases mapped to their canonical form local BUILTINS = { + ['?']='help', alias=true, clear='cls', cls=true, From b3679bef25991fc141cef82db598d0c7af95600d Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 21 Jul 2022 23:21:56 -0700 Subject: [PATCH 073/334] enable index building and update builtin docs --- conf.py | 4 ++-- docs/builtins/alias.rst | 9 +++++---- docs/builtins/clear.rst | 7 ------- docs/builtins/cls.rst | 5 +++-- docs/builtins/devel/dump-rpc.rst | 3 ++- docs/builtins/die.rst | 3 ++- docs/builtins/dir.rst | 7 ------- docs/builtins/disable.rst | 5 +++-- docs/builtins/enable.rst | 11 ++++++----- docs/builtins/fpause.rst | 4 ++-- docs/builtins/help.rst | 4 +++- docs/builtins/hide.rst | 3 ++- docs/builtins/keybinding.rst | 9 +++++---- docs/builtins/kill-lua.rst | 5 +++-- docs/builtins/load.rst | 5 +++-- docs/builtins/ls.rst | 8 ++++---- docs/builtins/man.rst | 7 ------- docs/builtins/plug.rst | 3 ++- docs/builtins/reload.rst | 5 +++-- docs/builtins/sc-script.rst | 6 ++++-- docs/builtins/script.rst | 7 ++++--- docs/builtins/show.rst | 9 +++++---- docs/builtins/tags.rst | 5 +++-- docs/builtins/type.rst | 7 ++++--- docs/builtins/unload.rst | 3 ++- 25 files changed, 72 insertions(+), 72 deletions(-) delete mode 100644 docs/builtins/clear.rst delete mode 100644 docs/builtins/dir.rst delete mode 100644 docs/builtins/man.rst diff --git a/conf.py b/conf.py index 7cf4a3a32..0488a2afe 100644 --- a/conf.py +++ b/conf.py @@ -346,8 +346,8 @@ html_sidebars = { # If false, no module index is generated. html_domain_indices = False -# If false, no index is generated. -html_use_index = False +# If false, no genindex.html is generated. +html_use_index = True html_css_files = [ 'dfhack.css', diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 325bb9dd7..59669fa39 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -4,10 +4,11 @@ alias Tags: system :dfhack-keybind:`alias` -Configure helper aliases for other DFHack commands. Aliases are resolved -immediately after built-in commands, which means that an alias cannot override -a built-in command, but can override a command implemented by a plugin or -script. +:index:`Configure helper aliases for other DFHack commands. +` Aliases are +resolved immediately after built-in commands, which means that an alias cannot +override a built-in command, but can override a command implemented by a plugin +or script. Usage: diff --git a/docs/builtins/clear.rst b/docs/builtins/clear.rst deleted file mode 100644 index 44c4f679a..000000000 --- a/docs/builtins/clear.rst +++ /dev/null @@ -1,7 +0,0 @@ -clear -===== - -Tags: system -:dfhack-keybind:`clear` - -Clear the terminal screen. This command is an alias for `cls`. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index 0514353bd..bc22b54fc 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -4,5 +4,6 @@ cls Tags: system :dfhack-keybind:`cls` -Clear the terminal screen. Can also be invoked as `clear`. Note that this -command does not delete command history. It just clears the text on the screen. +:index:`Clear the terminal screen. ` Can also +be invoked as ``clear``. Note that this command does not delete command history. +It just clears the text on the screen. diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index e6a710409..7babfb6a7 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -4,7 +4,8 @@ devel/dump-rpc Tags: system :dfhack-keybind:`devel/dump-rpc` -Writes RPC endpoint information to the specified file. +:index:`Writes RPC endpoint information to the specified file. +` Usage:: diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index 7a7414b8c..8f20a825f 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -4,4 +4,5 @@ die Tags: system :dfhack-keybind:`die` -Instantly exits DF without saving. +:index:`Instantly exits DF without saving. +` diff --git a/docs/builtins/dir.rst b/docs/builtins/dir.rst deleted file mode 100644 index 2eca9218c..000000000 --- a/docs/builtins/dir.rst +++ /dev/null @@ -1,7 +0,0 @@ -dir -=== - -Tags: system -:dfhack-keybind:`dir` - -List available DFHack commands. This is an alias of the `ls` command. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 413b8325f..3fb989328 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -4,8 +4,9 @@ disable Tags: system :dfhack-keybind:`disable` -Deactivate a DFHack tool that has some persistent effect. See the `enable` -command for more info. +:index:`Deactivate a DFHack tool that has some persistent effect. +` See the +`enable` command for more info. Usage:: diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 78534a98c..3a4164485 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -4,11 +4,12 @@ enable Tags: system :dfhack-keybind:`enable` -Activate a DFHack tool that has some persistent effect. Many plugins and scripts -can be in a distinct enabled or disabled state. Some of them activate and -deactivate automatically depending on the contents of the world raws. Others -store their state in world data. However a number of them have to be enabled -globally, and the init file is the right place to do it. +:index:`Activate a DFHack tool that has some persistent effect. +` Many plugins +and scripts can be in a distinct enabled or disabled state. Some of them +activate and deactivate automatically depending on the contents of the world +raws. Others store their state in world data. However a number of them have to +be enabled globally, and the init file is the right place to do it. Most such plugins or scripts support the built-in ``enable`` and `disable` commands. Calling them at any time without arguments prints a list of enabled diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index 60459848e..2e797129b 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -4,8 +4,8 @@ fpause Tags: system :dfhack-keybind:`fpause` -Forces DF to pause. This is useful when your FPS drops below 1 and you lose -control of the game. +:index:`Forces DF to pause. ` This is useful when +your FPS drops below 1 and you lose control of the game. Usage:: diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 49c010fec..9a1f3f1b8 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -4,7 +4,9 @@ help Tags: system :dfhack-keybind:`help` -Display help about a command or plugin. +:index:`Display help about a command or plugin. +` Can also be invoked as ``?`` +or ``man`` (short for "manual"). Usage:: diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 6b391972f..51da0cc4f 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -4,7 +4,8 @@ hide Tags: system :dfhack-keybind:`hide` -Hides the DFHack terminal window. You can show it again with the `show` +:index:`Hides the DFHack terminal window. +` You can show it again with the `show` command, though you'll need to use it from a `keybinding` set beforehand or the in-game `command-prompt`. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index 72ceaaf71..f591ece34 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -4,12 +4,13 @@ keybinding Tags: system :dfhack-keybind:`keybinding` -Create hotkeys that will run DFHack commands. Like any other command it can be -used at any time from the console, but bindings are not remembered between runs -of the game unless re-created in `dfhack.init`. +:index:`Create hotkeys that will run DFHack commands. +` Like any other +command, it can be used at any time from the console, but bindings are not +remembered between runs of the game unless re-created in `dfhack.init`. Hotkeys can be any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or -``\```. +``\``` (the key below the ``Esc`` key. Usage: diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 875376952..998277f96 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -4,8 +4,9 @@ kill-lua Tags: system :dfhack-keybind:`kill-lua` -Gracefully stops any currently-running Lua scripts. Use this command to stop -a misbehaving script that appears to be stuck. +:index:`Gracefully stops any currently-running Lua scripts. +` Use this +command to stop a misbehaving script that appears to be stuck. Usage:: diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index cac760c2a..e84afe7d9 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -4,8 +4,9 @@ load Tags: system :dfhack-keybind:`load` -Load and register a plugin library. Also see `unload` and `reload` for related -actions. +:index:`Load and register a plugin library. +` Also see `unload` and `reload` for +related actions. Usage:: diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 6875083f5..a6d2e637e 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -4,10 +4,10 @@ ls Tags: system :dfhack-keybind:`ls` -List available DFHack commands. In order to group related commands, each command -is associated with a list of tags. You can filter the listed commands by a tag -or a substring of the command name. The `dir` command is an alias of this -command. +:index:`List available DFHack commands. ` +In order to group related commands, each command is associated with a list of +tags. You can filter the listed commands by a tag or a substring of the +command name. Can also be invoked as ``dir``. Usage: diff --git a/docs/builtins/man.rst b/docs/builtins/man.rst deleted file mode 100644 index 3b5b52515..000000000 --- a/docs/builtins/man.rst +++ /dev/null @@ -1,7 +0,0 @@ -man -=== - -Tags: system -:dfhack-keybind:`man` - -An alias for the `help` command. diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 8f714b7cf..38b37aa84 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -4,7 +4,8 @@ plug Tags: system :dfhack-keybind:`plug` -Lists available plugins and whether they are enabled. +:index:`Lists available plugins and whether they are enabled. +` Usage: diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index d61e5c2fd..2fa817e3d 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -4,8 +4,9 @@ reload Tags: system :dfhack-keybind:`reload` -Reload a loaded plugin. Developer use this command to reload a plugin that they -are actively modifying. Also see `load` and `unload` for related actions. +:index:`Reload a loaded plugin. ` Developers +use this command to reload a plugin that they are actively modifying. Also see +`load` and `unload` for related actions. Usage:: diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index 2c80e1130..eac65b463 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -4,8 +4,10 @@ sc-script Tags: system :dfhack-keybind:`sc-script` -Runs commands when game state changes occur. This is similar to the static -`init-files` but is slightly more flexible since it can be set dynamically. +:index:`Run commands when game state changes occur. +` This is similar to +the static `init-files` but is slightly more flexible since it can be set +dynamically. Usage: diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index d69f3bbc7..17797ade7 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -4,9 +4,10 @@ script Tags: system :dfhack-keybind:`script` -Executes a batch file of DFHack commands. It reads a text file and runs each -line as a DFHack command as if it had been typed in by the user - treating the -input like `an init file `. +:index:`Executes a batch file of DFHack commands. +` It reads a text file and +runs each line as a DFHack command as if it had been typed in by the user -- +treating the input like `an init file `. Some other tools, such as `autobutcher` and `workflow`, export their settings as the commands to create them - which can later be reloaded with ``script``. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index e192f857c..81fda6943 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -4,10 +4,11 @@ show Tags: system :dfhack-keybind:`show` -Unhides the DFHack terminal window. Useful if you have hidden the terminal with -`hide` and you want it back. Since the terminal window won't be available to run -this command, you'll need to use it from a `keybinding` set beforehand or the -in-game `command-prompt`. +:index:`Unhides the DFHack terminal window. +` Useful if you have hidden the +terminal with `hide` and you want it back. Since the terminal window won't be +available to run this command, you'll need to use it from a `keybinding` set +beforehand or the in-game `command-prompt`. Only available on Windows. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index 3d7933fca..ad8f03b28 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -4,8 +4,9 @@ tags Tags: system :dfhack-keybind:`tags` -List the strings that DFHack tools can be tagged with. You can find groups of -related tools by passing the tag name to the `ls` command. +:index:`List the strings that DFHack tools can be tagged with. +` You can find +groups of related tools by passing the tag name to the `ls` command. Usage:: diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 16325c90f..2c16a3078 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -4,9 +4,10 @@ type Tags: system :dfhack-keybind:`type` -Describes how a command is implemented. DFHack commands can be provided by -plugins, scripts, or by the core library itself. The ``type`` command can tell -you which is the source of a particular command. +:index:`Describes how a command is implemented. +` DFHack commands can be provided +by plugins, scripts, or by the core library itself. The ``type`` command can +tell you which is the source of a particular command. Usage:: diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index 99eed500c..15a0fa789 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -4,7 +4,8 @@ unload Tags: system :dfhack-keybind:`unload` -Unload a plugin from memory. Also see `load` and `reload` for related actions. +:index:`Unload a plugin from memory. ` +Also see `load` and `reload` for related actions. Usage:: From 6b9803daaf2c985d5f7e0943925dc8b08f9d9818 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 00:05:53 -0700 Subject: [PATCH 074/334] update docs up to the end of the b's --- docs/plugins/3dveins.rst | 8 ++-- docs/plugins/add-spatter.rst | 1 - docs/plugins/autochop.rst | 1 - docs/plugins/autoclothing.rst | 5 ++- docs/plugins/autodump-destroy-here.rst | 13 ------- docs/plugins/autodump-destroy-item.rst | 16 -------- docs/plugins/autodump.rst | 37 ++++++++++++------ docs/plugins/autofarm.rst | 9 +++-- docs/plugins/autogems-reload.rst | 12 ------ docs/plugins/autogems.rst | 16 +++++--- docs/plugins/autohauler.rst | 9 +++-- docs/plugins/autolabor.rst | 6 ++- docs/plugins/automaterial.rst | 1 - docs/plugins/automelt.rst | 1 - docs/plugins/autotrade.rst | 1 - docs/plugins/blueprint.rst | 7 ++-- docs/plugins/building-hacks.rst | 1 - docs/plugins/buildingplan.rst | 13 ++++--- docs/plugins/burrow.rst | 49 ------------------------ docs/plugins/burrows.rst | 52 +++++++++++++++++++++++--- docs/plugins/digl.rst | 20 ---------- docs/plugins/diglx.rst | 20 ---------- docs/plugins/digv.rst | 20 ---------- docs/plugins/digvx.rst | 20 ---------- 24 files changed, 116 insertions(+), 222 deletions(-) delete mode 100644 docs/plugins/autodump-destroy-here.rst delete mode 100644 docs/plugins/autodump-destroy-item.rst delete mode 100644 docs/plugins/autogems-reload.rst delete mode 100644 docs/plugins/burrow.rst delete mode 100644 docs/plugins/digl.rst delete mode 100644 docs/plugins/diglx.rst delete mode 100644 docs/plugins/digv.rst delete mode 100644 docs/plugins/digvx.rst diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 89c241df6..ff230679b 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -4,9 +4,11 @@ Tags: :dfhack-keybind:`3dveins` -Rewrites layer veins to expand in 3D space. Existing, flat veins are removed -and new 3D veins that naturally span z-levels are generated in their place. -The transformation preserves the mineral counts reported by `prospect`. +:index:`Rewrites layer veins to expand in 3D space. +<3dveins; Rewrites layer veins to expand in 3D space.>` Existing, flat veins +are removed and new 3D veins that naturally span z-levels are generated in +their place. The transformation preserves the mineral counts reported by +`prospect`. Usage:: diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 92808017c..0c5c551b3 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -2,7 +2,6 @@ add-spatter =========== Tags: -:dfhack-keybind:`add-spatter` Make tagged reactions produce contaminants. The plugin is intended to give some use to all those poisons that can be bought from caravans. It automatically diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 704b97143..3fc65144e 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -2,7 +2,6 @@ autochop ======== Tags: -:dfhack-keybind:`autochop` Auto-harvest trees when low on stockpiled logs. This plugin can designate trees for chopping when your stocks are low on logs. diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index a8cc001dd..df937c338 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -4,8 +4,9 @@ autoclothing Tags: :dfhack-keybind:`autoclothing` -Automatically manage clothing work orders. It allows you to set how many of each -clothing type every citizen should have. +:index:`Automatically manage clothing work orders. +` It allows you to +set how many of each clothing type every citizen should have. Usage:: diff --git a/docs/plugins/autodump-destroy-here.rst b/docs/plugins/autodump-destroy-here.rst deleted file mode 100644 index 35356166c..000000000 --- a/docs/plugins/autodump-destroy-here.rst +++ /dev/null @@ -1,13 +0,0 @@ -autodump-destroy-here -===================== - -Tags: -:dfhack-keybind:`autodump-destroy-here` - -Destroy items marked for dumping under cursor. If called again before the game -is resumed, cancels destruction of the items. This is an alias for the -`autodump` command ``autodump destroy-here``, intended for use as a keybinding. - -Usage:: - - autodump-destroy-here diff --git a/docs/plugins/autodump-destroy-item.rst b/docs/plugins/autodump-destroy-item.rst deleted file mode 100644 index 9447590d0..000000000 --- a/docs/plugins/autodump-destroy-item.rst +++ /dev/null @@ -1,16 +0,0 @@ -autodump-destroy-item -===================== - -Tags: -:dfhack-keybind:`autodump-destroy-item` - -Destroy the selected item. The item may be selected in the :kbd:`k` list or in -the container item list. If called again before the game is resumed, cancels -destruction of the item. - -This command is intended for use as a keybinding. See the `autodump` command -for other dumping/destroying options. - -Usage:: - - autodump-destroy-item diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 5ac0d1cd5..45f5f90e1 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -3,15 +3,17 @@ autodump Tags: :dfhack-keybind:`autodump` +:dfhack-keybind:`autodump-destroy-here` +:dfhack-keybind:`autodump-destroy-item` -Quickly designate or teleport items to be dumped. When `enabled `, this -plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autodump`` -option is selected for the stockpile, any items placed in the stockpile will -automatically be designated to be dumped. +Automatically set items in a stockpile to be dumped. When `enabled `, +this plugin adds an option to the :kbd:`q` menu for stockpiles. When the +``autodump`` option is selected for the stockpile, any items placed in the +stockpile will automatically be designated to be dumped. -When invoked as a command, it can instantly move all items designated to be -dumped to the tile under the cursor. After moving the items, the dump flag is -unset and the forbid flag is set, just as if it had been dumped normally. Be +When invoked as a command, it can instantly move all unforbidden items marked +for dumping to the tile under the cursor. After moving the items, the dump flag +is unset and the forbid flag is set, just as if it had been dumped normally. Be aware that dwarves that are en route to pick up the item for dumping may still come and move the item to your dump zone. @@ -21,6 +23,15 @@ Usage:: enable autodump autodump [] + autodump-destroy-here + autodump-destroy-item + +``autodump-destroy-here`` is an alias for ``autodump destroy-here`` and is +intended for use as a keybinding. + +``autodump-destroy-item`` destroys only the selected item. The item may be +selected in the :kbd:`k` list or in the container item list. If called again +before the game is resumed, cancels destruction of the item. Options ------- @@ -28,10 +39,10 @@ Options - ``destroy`` Destroy instead of dumping. Doesn't require a cursor. If ``autodump`` is called again with this option before the game is resumed, it cancels - the destroy action. + pending destroy actions. - ``destroy-here`` - As ``destroy``, but only the selected item in the :kbd:`k` list, or inside a - container. + :index:`Destroy items marked for dumping under the cursor. + ` - ``visible`` Only process items that are not hidden. - ``hidden`` @@ -43,6 +54,10 @@ Examples -------- - ``autodump`` - Teleports all unforbidden items marked for dumping to the cursor position. + :index:`Teleports items marked for dumping to the cursor position. + ` - ``autodump destroy`` Destroys all unforbidden items marked for dumping +- ``autodump-destroy-item`` + :index:`Destroys the selected item. + ` diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index 9a3e16d43..0294b574b 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -4,10 +4,11 @@ autofarm Tags: :dfhack-keybind:`autofarm` -Automatically manage farm crop selection. This plugin periodically scans your -plant stocks and assigns crops to your farm plots based on which plant stocks -are low (as long as you have the appropriate seeds). The target threshold for -each crop type is configurable. +:index:`Automatically manage farm crop selection. +` This plugin periodically +scans your plant stocks and assigns crops to your farm plots based on which +plant stocks are low (as long as you have the appropriate seeds). The target +threshold for each crop type is configurable. Usage: diff --git a/docs/plugins/autogems-reload.rst b/docs/plugins/autogems-reload.rst deleted file mode 100644 index 0988496cb..000000000 --- a/docs/plugins/autogems-reload.rst +++ /dev/null @@ -1,12 +0,0 @@ -autogems-reload -=============== - -Tags: -:dfhack-keybind:`autogems-reload` - -Reloads the autogems configuration file. You might need to do this if you have -manually modified the contents while the game is running. - -Usage:: - - autogems-reload diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 724752217..215c3b69f 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -2,14 +2,20 @@ autogems ======== Tags: -:dfhack-keybind:`autogems` +:dfhack-keybind:`autogems-reload` Automatically cut rough gems. This plugin periodically scans your stocks of rough gems and creates manager orders for cutting them at a Jeweler's Workshop. -Usage:: +Usage: - enable autogems - -Run `gui/autogems` for a configuration UI, or access the new ``Auto Cut Gems`` +- ``enable autogems`` + Enables the plugin +- ``autogems-reload`` + :index:`Reloads the autogems configuration file. + ` You might need + to do this if you have manually modified the contents while the game is + running. + +Run `gui/autogems` for a configuration UI, or access the ``Auto Cut Gems`` option from the Current Workshop Orders screen (:kbd:`o`-:kbd:`W`). diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index f28044479..1dce8a692 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -4,10 +4,11 @@ autohauler Tags: :dfhack-keybind:`autohauler` -Automatically manage hauling labors. Similar to `autolabor`, but instead of -managing all labors, ``autohauler`` only addresses hauling labors, leaving the -assignment of skilled labors entirely up to you. You can use the in-game -`manipulator` UI or an external tool like Dwarf Therapist to do so. +:index:`Automatically manage hauling labors. +` Similar to `autolabor`, but +instead of managing all labors, ``autohauler`` only addresses hauling labors, +leaving the assignment of skilled labors entirely up to you. You can use the +in-game `manipulator` UI or an external tool like Dwarf Therapist to do so. Idle dwarves who are not on active military duty will be assigned the hauling labors; everyone else (including those currently hauling) will have the hauling diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index f402703c8..7696efe71 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -4,8 +4,10 @@ autolabor Tags: :dfhack-keybind:`autolabor` -Automatically manage dwarf labors. Autolabor attempts to keep as many dwarves as -possible busy while allowing dwarves to specialize in specific skills. +:index:`Automatically manage dwarf labors. +` Autolabor attempts to keep as +many dwarves as possible busy while allowing dwarves to specialize in specific +skills. Autolabor frequently checks how many jobs of each type are available and sets labors proportionally in order to get them all done quickly. Labors with diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 429a25d70..6c5e94c12 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -2,7 +2,6 @@ automaterial ============ Tags: -:dfhack-keybind:`automaterial` Sorts building materials by recent usage. This makes building constructions (walls, floors, fortifications, etc) much easier by saving you from having to diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index b16368ac5..38788ee93 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -2,7 +2,6 @@ automelt ======== Tags: -:dfhack-keybind:`automelt` Quickly designate items to be melted. When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` option is diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 405621b2b..5a5053b8f 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -2,7 +2,6 @@ autotrade ========= Tags: -:dfhack-keybind:`autotrade` Quickly designate items to be traded. When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autotrade`` option is diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 9d74fa47a..3825de7b2 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -4,9 +4,10 @@ blueprint Tags: :dfhack-keybind:`blueprint` -Record a live game map in a quickfort blueprint. With ``blueprint``, you can -export the structure of a portion of your fortress in a blueprint file that you -(or anyone else) can later play back with `quickfort`. +:index:`Record a live game map in a quickfort blueprint. +` With +``blueprint``, you can export the structure of a portion of your fortress in a +blueprint file that you (or anyone else) can later play back with `quickfort`. Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` subdirectory of your DF folder. The map area to turn into a blueprint is either diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 78e94fba6..0c20a474f 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -2,7 +2,6 @@ building-hacks ============== Tags: -:dfhack-keybind:`building-hacks` Allows mods to create and manage powered workshops. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 62a239e3d..ac73acd49 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -4,12 +4,13 @@ buildingplan Tags: :dfhack-keybind:`buildingplan` -Plan building construction before you have materials. This plugin adds a -planning mode for building placement. You can then place furniture, -constructions, and other buildings before the required materials are available, -and they will be created in a suspended state. Buildingplan will periodically -scan for appropriate items, and the jobs will be unsuspended when the items are -available. +:index:`Plan building construction before you have materials. +` This +plugin adds a planning mode for building placement. You can then place +furniture, constructions, and other buildings before the required materials are +available, and they will be created in a suspended state. Buildingplan will +periodically scan for appropriate items, and the jobs will be unsuspended when +the items are available. This is very useful when combined with manager work orders or `workflow` -- you can set a constraint to always have one or two doors/beds/tables/chairs/etc. diff --git a/docs/plugins/burrow.rst b/docs/plugins/burrow.rst deleted file mode 100644 index 879a7cc78..000000000 --- a/docs/plugins/burrow.rst +++ /dev/null @@ -1,49 +0,0 @@ -burrow -====== - -Tags: -:dfhack-keybind:`burrow` - -Quick commands for burrow control. Allows manipulating burrows and automated -burrow expansion while digging. - -Usage: - -- ``burrows enable auto-grow`` - When a wall inside a burrow with a name ending in '+' is dug out, the burrow - will be extended to newly-revealed adjacent walls. This final '+' may be - omitted in burrow name args of other ``burrows`` commands. Note that digging - 1-wide corridors with the miner inside the burrow is SLOW. Be sure to also - run ``enable burrow`` for this feature to work. -- ``burrows disable auto-grow`` - Disables auto-grow processing. -- ``burrows clear-unit [ ...]`` - Remove all units from the named burrows. -- ``burrows clear-tiles [ ...]`` - Remove all tiles from the named burrows. -- ``burrows set-units target-burrow [ ...]`` - Clear all units from the target burrow, then add units from the named source - burrows. -- ``burrows add-units target-burrow [ ...]`` - Add units from the source burrows to the target. -- ``burrows remove-units target-burrow [ ...]`` - Remove units in source burrows from the target. -- ``burrows set-tiles target-burrow [ ...]`` - Clear target burrow tiles and adds tiles from the names source burrows. -- ``burrows add-tiles target-burrow [ ...]`` - Add tiles from the source burrows to the target. -- ``burrows remove-tiles target-burrow [ ...]`` - Remove tiles in source burrows from the target. - -In place of a source burrow, you can use one of the following keywords: - -- ``ABOVE_GROUND`` -- ``SUBTERRANEAN`` -- ``INSIDE`` -- ``OUTSIDE`` -- ``LIGHT`` -- ``DARK`` -- ``HIDDEN`` -- ``REVEALED`` - -to add tiles with the given properties. diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 9bf375fa8..bc80e5bb1 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -2,17 +2,57 @@ burrows ======= Tags: -:dfhack-keybind:`burrows` +:dfhack-keybind:`burrow` Auto-expand burrows as you dig. When a wall inside a burrow with a name ending in ``+`` is dug out, the burrow will be extended to newly-revealed adjacent walls. Note that digging 1-wide corridors with the miner inside the burrow is SLOW. -Usage:: +You can also use the ``burrow`` command to +:index:`Quickly add units/tiles to burrows. +` - enable burrows - burrows enable auto-grow +Usage: -Both of the above commands need to be run for the auto-grow functionality to -work. See the `burrow` command for more burrow-related tools. +- ``enable burrows`` + Enable the plugin for the auto-grow feature (see + ``burrow enable auto-grow`` below) +- ``burrow enable auto-grow`` + When a wall inside a burrow with a name ending in '+' is dug out, the burrow + will be extended to newly-revealed adjacent walls. This final '+' may be + omitted in burrow name args of other ``burrows`` commands. Note that digging + 1-wide corridors with the miner inside the burrow is SLOW. Be sure to also + run ``enable burrows`` for this feature to work. +- ``burrow disable auto-grow`` + Disables auto-grow processing. +- ``burrow clear-unit [ ...]`` + Remove all units from the named burrows. +- ``burrow clear-tiles [ ...]`` + Remove all tiles from the named burrows. +- ``burrow set-units target-burrow [ ...]`` + Clear all units from the target burrow, then add units from the named source + burrows. +- ``burrow add-units target-burrow [ ...]`` + Add units from the source burrows to the target. +- ``burrow remove-units target-burrow [ ...]`` + Remove units in source burrows from the target. +- ``burrow set-tiles target-burrow [ ...]`` + Clear target burrow tiles and adds tiles from the names source burrows. +- ``burrow add-tiles target-burrow [ ...]`` + Add tiles from the source burrows to the target. +- ``burrow remove-tiles target-burrow [ ...]`` + Remove tiles in source burrows from the target. + +In place of a source burrow, you can use one of the following keywords: + +- ``ABOVE_GROUND`` +- ``SUBTERRANEAN`` +- ``INSIDE`` +- ``OUTSIDE`` +- ``LIGHT`` +- ``DARK`` +- ``HIDDEN`` +- ``REVEALED`` + +to add tiles with the given properties. diff --git a/docs/plugins/digl.rst b/docs/plugins/digl.rst deleted file mode 100644 index f09626d0c..000000000 --- a/docs/plugins/digl.rst +++ /dev/null @@ -1,20 +0,0 @@ -dig -=== -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - -:dfhack-keybind:`digv` - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. diff --git a/docs/plugins/diglx.rst b/docs/plugins/diglx.rst deleted file mode 100644 index f09626d0c..000000000 --- a/docs/plugins/diglx.rst +++ /dev/null @@ -1,20 +0,0 @@ -dig -=== -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - -:dfhack-keybind:`digv` - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. diff --git a/docs/plugins/digv.rst b/docs/plugins/digv.rst deleted file mode 100644 index f09626d0c..000000000 --- a/docs/plugins/digv.rst +++ /dev/null @@ -1,20 +0,0 @@ -dig -=== -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - -:dfhack-keybind:`digv` - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. diff --git a/docs/plugins/digvx.rst b/docs/plugins/digvx.rst deleted file mode 100644 index f09626d0c..000000000 --- a/docs/plugins/digvx.rst +++ /dev/null @@ -1,20 +0,0 @@ -dig -=== -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - -:dfhack-keybind:`digv` - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. From 53cdf57043d34e78f599e682198b93caa2bef8a4 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 00:08:03 -0700 Subject: [PATCH 075/334] add missing digv label --- docs/plugins/dig.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index f09626d0c..2b1e96a18 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -1,3 +1,5 @@ +.. _digv: + dig === This plugin makes many automated or complicated dig patterns easy. From 3522b89f5ce66b92d7f92538f9b8e640bdfc8ada Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 14:20:01 +0000 Subject: [PATCH 076/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/plugins/autogems.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 215c3b69f..e1dd294e3 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -16,6 +16,6 @@ Usage: ` You might need to do this if you have manually modified the contents while the game is running. - + Run `gui/autogems` for a configuration UI, or access the ``Auto Cut Gems`` option from the Current Workshop Orders screen (:kbd:`o`-:kbd:`W`). From 9c7731f072f4998d7f530e0c14b316b3e3ddde9f Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 09:19:11 -0700 Subject: [PATCH 077/334] remove reference to dfhack.init-example --- docs/Installing.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/Installing.rst b/docs/Installing.rst index 5b564f45f..05db198b8 100644 --- a/docs/Installing.rst +++ b/docs/Installing.rst @@ -96,13 +96,14 @@ When you `download DFHack `, you will end up with a release archive operating system should have built-in utilities capable of extracting files from these archives. -The release archives contain several files and folders, including a ``hack`` -folder, a ``dfhack-config`` folder, and a ``dfhack.init-example`` file. To -install DFHack, copy all of the files from the DFHack archive into the root DF -folder, which should already include a ``data`` folder and a ``raw`` folder, -among other things. Some packs and other redistributions of Dwarf Fortress may -place DF in another folder, so ensure that the ``hack`` folder ends up next to -the ``data`` folder. +The release archives contain several folders, including a ``hack`` folder where +DFHack binary and system data is stored, a ``dfhack-config`` folder where user +data and configuration is stored, and a ``blueprints`` folder where `quickfort` +blueprints are stored. To install DFHack, copy all of the files from the DFHack +archive into the root DF folder, which should already include a ``data`` folder +and a ``raw`` folder, among other things. Some packs and other redistributions +of Dwarf Fortress may place DF in another folder, so ensure that the ``hack`` +folder ends up next to the ``data`` folder. .. note:: From bd581581267a57865b24fa73d58b4efc428bdd88 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 09:56:23 -0700 Subject: [PATCH 078/334] get rid of tool TOC, use genindex also add a stub role for dfhack-tag --- conf.py | 13 +++++++++++++ docs/Introduction.rst | 6 ++++++ docs/Tags.rst | 42 +++++++++++++++++++++++------------------- docs/index-tools.rst | 15 --------------- index.rst | 29 +++++------------------------ 5 files changed, 47 insertions(+), 58 deletions(-) delete mode 100644 docs/index-tools.rst diff --git a/conf.py b/conf.py index 0488a2afe..abbd1dbb3 100644 --- a/conf.py +++ b/conf.py @@ -82,7 +82,18 @@ def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, return [newnode], [] +# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments +def dfhack_tag_role_func(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """Custom role parser for DFHack tags.""" + roles.set_classes(options) + # TODO: link to generated tag index page + newnode = nodes.inline(text, text) + return [newnode], [] + + roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func) +roles.register_canonical_role('dfhack-tag', dfhack_tag_role_func) # -- Autodoc for DFhack plugins and scripts ------------------------------- @@ -128,6 +139,7 @@ def write_tool_docs(): footer. """ for k in DOC_ALL_DIRS: + header = ':orphan:\n' label = ('.. _{name}:\n\n').format(name=k[0]) # TODO: can we autogenerate the :dfhack-keybind: line? it would go beneath # the tool header, which is currently in the middle of the included file. @@ -143,6 +155,7 @@ def write_tool_docs(): os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), mode=0o755, exist_ok=True) with open('docs/tools/{}.rst'.format(k[0]), mode) as outfile: + outfile.write(header) if k[0] != 'search' and k[0] != 'stonesense': outfile.write(label) outfile.write(include) diff --git a/docs/Introduction.rst b/docs/Introduction.rst index f0dc7a4b6..449f2d38a 100644 --- a/docs/Introduction.rst +++ b/docs/Introduction.rst @@ -59,6 +59,12 @@ used by the DFHack console. * Finally, some commands are persistent once enabled, and will sit in the background managing or changing some aspect of the game if you `enable` them. +.. note:: + In order to avoid user confusion, as a matter of policy all GUI tools + display the word :guilabel:`DFHack` on the screen somewhere while active. + + When that is not appropriate because they merely add keybinding hints to + existing DF screens, they deliberately use red instead of green for the key. Getting help ============ diff --git a/docs/Tags.rst b/docs/Tags.rst index 56e5af650..de4e4900b 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,23 +1,27 @@ .. _tags: -Tags -==== +Tool categories +=============== -This is the list of tags and their descriptions. +Related DFHack tools are grouped by tags to make them easier to find. If you'd +like to see the full list of commands, please refer to the `index `. -- adventure: Tools relevant to adventure mode -- fort: Tools relevant to fort mode -- legends: Tools relevant to legends mode -- items: Tools that create or modify in-game items -- units: Tools that create or modify units -- jobs: Tools that create or modify jobs -- labors: Tools that deal with labor assignment -- auto: Tools that automatically manage some aspect of your fortress -- map: Map modification -- system: Tools related to working with DFHack commands or the core DFHack library -- productivity: Tools that help you do things that you could do manually, but using the tool is better and faster -- animals: Tools that help you manage animals -- fix: Tools that fix specific bugs -- inspection: Tools that let you inspect game data -- buildings/furniture: Tools that help you work wtih placing or configuring buildings and furniture -- quickfort: Tools that are involved in creating and playing back blueprints +These are the DFHack tool categories. Note that a tool may belong to more than +one category. + +- :dfhack-tag:`adventure`: Tools relevant to adventure mode +- :dfhack-tag:`fort`: Tools relevant to fort mode +- :dfhack-tag:`legends`: Tools relevant to legends mode +- :dfhack-tag:`items`: Tools that create or modify in-game items +- :dfhack-tag:`units`: Tools that create or modify units +- :dfhack-tag:`jobs`: Tools that create or modify jobs +- :dfhack-tag:`labors`: Tools that deal with labor assignment +- :dfhack-tag:`auto`: Tools that automatically manage some aspect of your fortress +- :dfhack-tag:`map`: Map modification +- :dfhack-tag:`system`: Tools related to working with DFHack commands or the core DFHack library +- :dfhack-tag:`productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster +- :dfhack-tag:`animals`: Tools that help you manage animals +- :dfhack-tag:`fix`: Tools that fix specific bugs +- :dfhack-tag:`inspection`: Tools that let you inspect game data +- :dfhack-tag:`buildings/furniture`: Tools that help you work wtih placing or configuring buildings and furniture +- :dfhack-tag:`quickfort`: Tools that are involved in creating and playing back blueprints diff --git a/docs/index-tools.rst b/docs/index-tools.rst deleted file mode 100644 index a9c540d66..000000000 --- a/docs/index-tools.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _tools-index: - -================== -DFHack Tools Index -================== - -These pages contain information about the plugins, scripts, and built-in -commands distributed with DFHack. - -.. toctree:: - :titlesonly: - :glob: - - /docs/tools/* - /docs/tools/*/* diff --git a/index.rst b/index.rst index 3e5cdec5d..843797e4e 100644 --- a/index.rst +++ b/index.rst @@ -15,10 +15,9 @@ Quick Links * `Downloads `_ * `Installation guide ` -* `Source code `_ - (**important:** read `compile` before attempting to build from source) -* `Bay 12 forums thread `_ -* `Bug tracker `_ +* `Getting help ` +* :source:`Source code <>` + (**important:** read `compile` before attempting to build from source.) User Manual =========== @@ -30,25 +29,7 @@ User Manual /docs/Installing /docs/Support /docs/Core + /docs/Tags /docs/guides/index - /docs/index-about /docs/index-dev - -Tools -===== - -DFHack commands, plugins, and scripts are grouped by tags to make it easier to -find groups of related tools. - -.. note:: - In order to avoid user confusion, as a matter of policy all GUI tools - display the word :guilabel:`DFHack` on the screen somewhere while active. - - When that is not appropriate because they merely add keybinding hints to - existing DF screens, they deliberately use red instead of green for the key. - -.. toctree:: - :maxdepth: 1 - - /docs/Tags - /docs/index-tools + /docs/index-about From 3175e8b33d3cce98f775c0f7348e0064a5907704 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 10:01:04 -0700 Subject: [PATCH 079/334] update tags and text for builtins --- docs/builtins/alias.rst | 2 +- docs/builtins/cls.rst | 2 +- docs/builtins/devel/dump-rpc.rst | 4 ++-- docs/builtins/die.rst | 6 +++--- docs/builtins/disable.rst | 2 +- docs/builtins/enable.rst | 2 +- docs/builtins/fpause.rst | 2 +- docs/builtins/help.rst | 2 +- docs/builtins/hide.rst | 6 +++--- docs/builtins/keybinding.rst | 2 +- docs/builtins/kill-lua.rst | 2 +- docs/builtins/load.rst | 2 +- docs/builtins/ls.rst | 2 +- docs/builtins/plug.rst | 6 +++--- docs/builtins/reload.rst | 2 +- docs/builtins/sc-script.rst | 2 +- docs/builtins/script.rst | 6 +++--- docs/builtins/show.rst | 2 +- docs/builtins/tags.rst | 2 +- docs/builtins/type.rst | 6 +++--- docs/builtins/unload.rst | 2 +- 21 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 59669fa39..6de550875 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,7 +1,7 @@ alias ===== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`alias` :index:`Configure helper aliases for other DFHack commands. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index bc22b54fc..5c1189951 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,7 +1,7 @@ cls === -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`cls` :index:`Clear the terminal screen. ` Can also diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index 7babfb6a7..05f1d4e9e 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -1,10 +1,10 @@ devel/dump-rpc ============== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`devel/dump-rpc` -:index:`Writes RPC endpoint information to the specified file. +:index:`Write RPC endpoint information to the specified file. ` Usage:: diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index 8f20a825f..c2ea083fd 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,8 +1,8 @@ die === -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`die` -:index:`Instantly exits DF without saving. -` +:index:`Instantly exit DF without saving. +` diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 3fb989328..1e2336545 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,7 +1,7 @@ disable ======= -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`disable` :index:`Deactivate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 3a4164485..4630c2004 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,7 +1,7 @@ enable ====== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`enable` :index:`Activate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index 2e797129b..927206e8d 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,7 +1,7 @@ fpause ====== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`fpause` :index:`Forces DF to pause. ` This is useful when diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 9a1f3f1b8..6249bdbe9 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,7 +1,7 @@ help ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`help` :index:`Display help about a command or plugin. diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 51da0cc4f..3c79f9ff0 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,11 +1,11 @@ hide ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`hide` -:index:`Hides the DFHack terminal window. -` You can show it again with the `show` +:index:`Hide the DFHack terminal window. +` You can show it again with the `show` command, though you'll need to use it from a `keybinding` set beforehand or the in-game `command-prompt`. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index f591ece34..27ab86da6 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,7 +1,7 @@ keybinding ========== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`keybinding` :index:`Create hotkeys that will run DFHack commands. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 998277f96..3f4bbf3c4 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,7 +1,7 @@ kill-lua ======== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`kill-lua` :index:`Gracefully stops any currently-running Lua scripts. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index e84afe7d9..22e8067f3 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,7 +1,7 @@ load ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`load` :index:`Load and register a plugin library. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index a6d2e637e..775ccfc63 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,7 +1,7 @@ ls == -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`ls` :index:`List available DFHack commands. ` diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 38b37aa84..7dbbf0115 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,11 +1,11 @@ plug ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`plug` -:index:`Lists available plugins and whether they are enabled. -` +:index:`List available plugins and whether they are enabled. +` Usage: diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index 2fa817e3d..a5ab60859 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,7 +1,7 @@ reload ====== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`reload` :index:`Reload a loaded plugin. ` Developers diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index eac65b463..feabe4c6f 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,7 +1,7 @@ sc-script ========= -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`sc-script` :index:`Run commands when game state changes occur. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index 17797ade7..b83f19171 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,11 +1,11 @@ script ====== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`script` -:index:`Executes a batch file of DFHack commands. -` It reads a text file and +:index:`Execute a batch file of DFHack commands. +` It reads a text file and runs each line as a DFHack command as if it had been typed in by the user -- treating the input like `an init file `. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index 81fda6943..e337bbbb6 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,7 +1,7 @@ show ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`show` :index:`Unhides the DFHack terminal window. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index ad8f03b28..cc5c86a4b 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,7 +1,7 @@ tags ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`tags` :index:`List the strings that DFHack tools can be tagged with. diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 2c16a3078..33ef7562f 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,11 +1,11 @@ type ==== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`type` -:index:`Describes how a command is implemented. -` DFHack commands can be provided +:index:`Describe how a command is implemented. +` DFHack commands can be provided by plugins, scripts, or by the core library itself. The ``type`` command can tell you which is the source of a particular command. diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index 15a0fa789..3d1575c20 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,7 +1,7 @@ unload ====== -Tags: system +Tags: :dfhack-tag:`system` :dfhack-keybind:`unload` :index:`Unload a plugin from memory. ` From d637c874963f6f9e1045d8946730aefdbc28e00b Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 10:14:50 -0700 Subject: [PATCH 080/334] ensure all plugins are indexed --- docs/plugins/3dveins.rst | 6 ++++-- docs/plugins/add-spatter.rst | 9 +++++---- docs/plugins/autochop.rst | 5 +++-- docs/plugins/autodump.rst | 9 +++++---- docs/plugins/autogems.rst | 5 +++-- docs/plugins/automaterial.rst | 7 ++++--- docs/plugins/automelt.rst | 9 +++++---- docs/plugins/autotrade.rst | 9 +++++---- docs/plugins/building-hacks.rst | 3 ++- docs/plugins/burrows.rst | 11 ++++++----- 10 files changed, 42 insertions(+), 31 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index ff230679b..3c1276b46 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -4,8 +4,8 @@ Tags: :dfhack-keybind:`3dveins` -:index:`Rewrites layer veins to expand in 3D space. -<3dveins; Rewrites layer veins to expand in 3D space.>` Existing, flat veins +:index:`Rewrite layer veins to expand in 3D space. +<3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in their place. The transformation preserves the mineral counts reported by `prospect`. @@ -19,6 +19,8 @@ The ``verbose`` option prints out extra information to the console. Example ------- +:: + 3dveins New veins are generated using 3D Perlin noise in order to produce a layout that diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 0c5c551b3..38a9f91f0 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -3,9 +3,10 @@ add-spatter Tags: -Make tagged reactions produce contaminants. The plugin is intended to give some -use to all those poisons that can be bought from caravans. It automatically -enables itself when you load a world with reactions that include names starting -with ``SPATTER_ADD_``. These reactions will then produce contaminants on items +:index:`Make tagged reactions produce contaminants. +` Give some use to all +those poisons that can be bought from caravans! The plugin automatically enables +itself when you load a world with reactions that include names starting with +``SPATTER_ADD_``. These reactions will then produce contaminants on items instead of improvements. The contaminants are immune to being washed away by water or destroyed by `clean`. diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 3fc65144e..3a2284aef 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -3,8 +3,9 @@ autochop Tags: -Auto-harvest trees when low on stockpiled logs. This plugin can designate trees -for chopping when your stocks are low on logs. +:index:`Auto-harvest trees when low on stockpiled logs. +` This plugin can +designate trees for chopping when your stocks are low on logs. Usage:: diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 45f5f90e1..a47878c34 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -6,10 +6,11 @@ Tags: :dfhack-keybind:`autodump-destroy-here` :dfhack-keybind:`autodump-destroy-item` -Automatically set items in a stockpile to be dumped. When `enabled `, -this plugin adds an option to the :kbd:`q` menu for stockpiles. When the -``autodump`` option is selected for the stockpile, any items placed in the -stockpile will automatically be designated to be dumped. +:index:`Automatically set items in a stockpile to be dumped. +` When +`enabled `, this plugin adds an option to the :kbd:`q` menu for +stockpiles. When the ``autodump`` option is selected for the stockpile, any +items placed in the stockpile will automatically be designated to be dumped. When invoked as a command, it can instantly move all unforbidden items marked for dumping to the tile under the cursor. After moving the items, the dump flag diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index e1dd294e3..bdfd51864 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -4,8 +4,9 @@ autogems Tags: :dfhack-keybind:`autogems-reload` -Automatically cut rough gems. This plugin periodically scans your stocks of -rough gems and creates manager orders for cutting them at a Jeweler's Workshop. +:index:`Automatically cut rough gems. ` +This plugin periodically scans your stocks of rough gems and creates manager +orders for cutting them at a Jeweler's Workshop. Usage: diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 6c5e94c12..e51276772 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -3,9 +3,10 @@ automaterial Tags: -Sorts building materials by recent usage. This makes building constructions -(walls, floors, fortifications, etc) much easier by saving you from having to -trawl through long lists of materials each time you place one. +:index:`Sorts building materials by recent usage. +; Sorts building materials by recent usage.>` This makes building +constructions (walls, floors, fortifications, etc) much easier by saving you +from having to trawl through long lists of materials each time you place one. It moves the last used material for a given construction type to the top of the list, if there are any left. So if you build a wall with chalk blocks, the next diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 38788ee93..b51a431c0 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -3,10 +3,11 @@ automelt Tags: -Quickly designate items to be melted. When `enabled `, this plugin adds -an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` option is -selected for the stockpile, any items placed in the stockpile will automatically -be designated to be melted. +:index:`Quickly designate items to be melted. +` When `enabled `, this +plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` +option is selected for the stockpile, any items placed in the stockpile will +automatically be designated to be melted. Usage:: diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 5a5053b8f..ed2d65bc5 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -3,10 +3,11 @@ autotrade Tags: -Quickly designate items to be traded. When `enabled `, this plugin adds -an option to the :kbd:`q` menu for stockpiles. When the ``autotrade`` option is -selected for the stockpile, any items placed in the stockpile will automatically -be designated to be traded. +:index:`Quickly designate items to be traded. +` When `enabled `, +this plugin adds an option to the :kbd:`q` menu for stockpiles. When the +``autotrade`` option is selected for the stockpile, any items placed in the +stockpile will automatically be designated to be traded. Usage:: diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 0c20a474f..abd6ac4b4 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,8 @@ building-hacks Tags: -Allows mods to create and manage powered workshops. +:index:`Allows mods to create and manage powered workshops. +` Usage:: diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index bc80e5bb1..6ab26caa2 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -4,13 +4,14 @@ burrows Tags: :dfhack-keybind:`burrow` -Auto-expand burrows as you dig. When a wall inside a burrow with a name ending -in ``+`` is dug out, the burrow will be extended to newly-revealed adjacent -walls. Note that digging 1-wide corridors with the miner inside the burrow is -SLOW. +:index:`Auto-expand burrows as you dig. +` When a wall inside a burrow +with a name ending in ``+`` is dug out, the burrow will be extended to +newly-revealed adjacent walls. Note that digging 1-wide corridors with the miner +inside the burrow is **SLOW**. You can also use the ``burrow`` command to -:index:`Quickly add units/tiles to burrows. +:index:`quickly add units/tiles to burrows. ` Usage: From b38ccfe03d1493a10b9d0c339468e25f871e8d80 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 10:18:32 -0700 Subject: [PATCH 081/334] fix typo in automaterial docs --- docs/plugins/automaterial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index e51276772..7f8355b49 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -4,7 +4,7 @@ automaterial Tags: :index:`Sorts building materials by recent usage. -; Sorts building materials by recent usage.>` This makes building +` This makes building constructions (walls, floors, fortifications, etc) much easier by saving you from having to trawl through long lists of materials each time you place one. From 6a31b316dc694e6a7838b001d649074ee61532b5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 14:37:24 -0700 Subject: [PATCH 082/334] add structure for category indices --- conf.py | 35 +++++++++++++++++++++++++++++++---- docs/Categories.rst | 13 +++++++++++++ docs/Tags.rst | 20 ++++++-------------- 3 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 docs/Categories.rst diff --git a/conf.py b/conf.py index abbd1dbb3..e80b5a027 100644 --- a/conf.py +++ b/conf.py @@ -126,10 +126,38 @@ def doc_all_dirs(): DOC_ALL_DIRS = doc_all_dirs() + +def get_tags(): + tags = [] + tag_re = re.compile(r'- :dfhack-tag:`([^`]+)`: (.*)') + with open('docs/Tags.rst') as f: + lines = f.readlines() + for line in lines: + m = re.match(tag_re, line.strip()) + if m: + tags.append((m.group(1), m.group(2))) + return tags + + +def get_open_mode(): + return 'w' if sys.version_info.major > 2 else 'wb' + + def generate_tag_indices(): - #TODO: generate docs/tags/.rst with the tag description and links to the - # tools that have that tag os.makedirs('docs/tags', mode=0o755, exist_ok=True) + with open('docs/tags/index.rst', get_open_mode()) as topidx: + #topidx.write(':orphan:\n\n') + for tag_tuple in get_tags(): + tag = tag_tuple[0] + with open(('docs/tags/{name}.rst').format(name=tag), + get_open_mode()) as tagidx: + #tagidx.write(':orphan:\n\n') + tagidx.write('TODO: add links to the tools that have this tag') + topidx.write(('.. _tag/{name}:\n\n').format(name=tag)) + topidx.write(('{name}\n').format(name=tag)) + topidx.write(('{underline}\n').format(underline='-'*len(tag))) + topidx.write(('{desc}\n\n').format(desc=tag_tuple[1])) + topidx.write(('.. include:: /docs/tags/{name}.rst\n\n').format(name=tag)) def write_tool_docs(): @@ -151,10 +179,9 @@ def write_tool_docs(): # TODO: generate a footer with links to tools that share at least one # tag with this tool. Just the tool names, strung across the bottom of # the page in one long wrapped line, similar to how the wiki does it - mode = 'w' if sys.version_info.major > 2 else 'wb' os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), mode=0o755, exist_ok=True) - with open('docs/tools/{}.rst'.format(k[0]), mode) as outfile: + with open('docs/tools/{}.rst'.format(k[0]), get_open_mode()) as outfile: outfile.write(header) if k[0] != 'search' and k[0] != 'stonesense': outfile.write(label) diff --git a/docs/Categories.rst b/docs/Categories.rst new file mode 100644 index 000000000..541f89195 --- /dev/null +++ b/docs/Categories.rst @@ -0,0 +1,13 @@ +.. _categories: + +Tool categories +=============== + +DFHack tools are grouped to make them easier to find. Note that a tool can +belong to more than one category. If you'd like to see the full list of tools, +please refer to the `index `. + +.. contents:: Contents + :local: + +.. include:: tags/index.rst diff --git a/docs/Tags.rst b/docs/Tags.rst index de4e4900b..7afbe0a02 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,17 +1,8 @@ -.. _tags: +:orphan: -Tool categories -=============== - -Related DFHack tools are grouped by tags to make them easier to find. If you'd -like to see the full list of commands, please refer to the `index `. - -These are the DFHack tool categories. Note that a tool may belong to more than -one category. - -- :dfhack-tag:`adventure`: Tools relevant to adventure mode -- :dfhack-tag:`fort`: Tools relevant to fort mode -- :dfhack-tag:`legends`: Tools relevant to legends mode +- :dfhack-tag:`adventure`: Tools that are useful while in adventure mode +- :dfhack-tag:`fort`: Tools that are useful while in fort mode +- :dfhack-tag:`legends`: Tools that are useful while in legends mode - :dfhack-tag:`items`: Tools that create or modify in-game items - :dfhack-tag:`units`: Tools that create or modify units - :dfhack-tag:`jobs`: Tools that create or modify jobs @@ -23,5 +14,6 @@ one category. - :dfhack-tag:`animals`: Tools that help you manage animals - :dfhack-tag:`fix`: Tools that fix specific bugs - :dfhack-tag:`inspection`: Tools that let you inspect game data -- :dfhack-tag:`buildings/furniture`: Tools that help you work wtih placing or configuring buildings and furniture +- :dfhack-tag:`buildings`: Tools that help you work wtih placing or configuring buildings and furniture - :dfhack-tag:`quickfort`: Tools that are involved in creating and playing back blueprints +- :dfhack-tag:`dev`: Tools useful for develpers and modders From 2fd6d528ce084c87ebe1c2c923a734470a05cf7c Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 14:37:47 -0700 Subject: [PATCH 083/334] move support docs to the intro page but leave an orphan link to the new section in case external links still point to the old page --- docs/Introduction.rst | 34 ++++++++++++++++++++++++++++++++-- docs/Support.rst | 35 ++--------------------------------- index.rst | 3 +-- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/docs/Introduction.rst b/docs/Introduction.rst index 449f2d38a..ad24f8f43 100644 --- a/docs/Introduction.rst +++ b/docs/Introduction.rst @@ -21,7 +21,7 @@ enhancements by default, and more can be enabled. There are also many tools You can even add third-party scripts and plugins to do almost anything! For modders, DFHack makes many things possible. Custom reactions, new -interactions, magic creature abilities, and more can be set through `DFHack tools ` +interactions, magic creature abilities, and more can be set through `DFHack tools ` and custom raws. Non-standard DFHack scripts and inits can be stored in the raw directory, making raws or saves fully self-contained for distribution - or for coexistence in a single DF install, even with incompatible components. @@ -66,6 +66,36 @@ used by the DFHack console. When that is not appropriate because they merely add keybinding hints to existing DF screens, they deliberately use red instead of green for the key. +.. _support: + Getting help ============ -There are several support channels available - see `support` for details. + +DFHack has several ways to get help online, including: + +- The `DFHack Discord server `__ +- The ``#dfhack`` IRC channel on `Libera `__ +- GitHub: + - for bugs, use the :issue:`issue tracker <>` + - for more open-ended questions, use the `discussion board + `__. Note that this is a + relatively-new feature as of 2021, but maintainers should still be + notified of any discussions here. +- The `DFHack thread on the Bay 12 Forum `__ + +Some additional, but less DFHack-specific, places where questions may be answered include: + +- The `/r/dwarffortress `_ questions thread on Reddit +- If you are using a starter pack, the relevant thread on the `Bay 12 Forum `__ - + see the :wiki:`DF Wiki ` for a list of these threads + +When reaching out to any support channels regarding problems with DFHack, please +remember to provide enough details for others to identify the issue. For +instance, specific error messages (copied text or screenshots) are helpful, as +well as any steps you can follow to reproduce the problem. Sometimes, log output +from ``stderr.log`` in the DF folder can point to the cause of issues as well. + +Some common questions may also be answered in documentation, including: + +- This documentation (`online here `__; search functionality available `here `) +- :wiki:`The DF wiki <>` diff --git a/docs/Support.rst b/docs/Support.rst index 00e055baa..af8bd583c 100644 --- a/docs/Support.rst +++ b/docs/Support.rst @@ -1,34 +1,3 @@ -.. _support: +:orphan: -=============== -Getting Support -=============== - -DFHack has several ways to get help online, including: - -- The `DFHack Discord server `__ -- The ``#dfhack`` IRC channel on `Libera `__ -- GitHub: - - for bugs, use the :issue:`issue tracker <>` - - for more open-ended questions, use the `discussion board - `__. Note that this is a - relatively-new feature as of 2021, but maintainers should still be - notified of any discussions here. -- The `DFHack thread on the Bay 12 Forum `__ - -Some additional, but less DFHack-specific, places where questions may be answered include: - -- The `/r/dwarffortress `_ questions thread on Reddit -- If you are using a starter pack, the relevant thread on the `Bay 12 Forum `__ - - see the :wiki:`DF Wiki ` for a list of these threads - -When reaching out to any support channels regarding problems with DFHack, please -remember to provide enough details for others to identify the issue. For -instance, specific error messages (copied text or screenshots) are helpful, as -well as any steps you can follow to reproduce the problem. Sometimes, log output -from ``stderr.log`` in the DF folder can point to the cause of issues as well. - -Some common questions may also be answered in documentation, including: - -- This documentation (`online here `__; search functionality available `here `) -- :wiki:`The DF wiki <>` +Please continue to the new `support` page. diff --git a/index.rst b/index.rst index 843797e4e..d627267f0 100644 --- a/index.rst +++ b/index.rst @@ -27,9 +27,8 @@ User Manual /docs/Introduction /docs/Installing - /docs/Support /docs/Core - /docs/Tags + /docs/Categories /docs/guides/index /docs/index-dev /docs/index-about From 67520258482bdd94729982f05b94ce6db2899f3b Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 14:38:16 -0700 Subject: [PATCH 084/334] fix up some index links --- docs/Core.rst | 4 ++-- docs/Dev-intro.rst | 9 ++++----- docs/Memory-research.rst | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index f7ba0824b..dd00885d0 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -26,7 +26,7 @@ DFHack commands can be implemented in any of three ways: more flexible about versions, and easier to distribute. Most third-party DFHack addons are scripts. -All tools distributed with DFHack are documented `here `. +All tools distributed with DFHack are documented `here `. Using DFHack Commands ===================== @@ -185,7 +185,7 @@ where ``*`` can be any string, including the empty string. A world being loaded can mean a fortress, an adventurer, or legends mode. These files are best used for non-persistent commands, such as setting -a `fix ` script to run on `repeat`. +a :dfhack-tag:`fix` script to run on `repeat`. .. _onMapLoad.init: diff --git a/docs/Dev-intro.rst b/docs/Dev-intro.rst index 6f46d86c5..09a68a0f9 100644 --- a/docs/Dev-intro.rst +++ b/docs/Dev-intro.rst @@ -51,11 +51,10 @@ is more complete and currently better-documented, however. Referring to existing scripts as well as the API documentation can be helpful when developing new scripts. -`Scripts included in DFHack ` live in a separate -`scripts repository `_. This can be found in -the ``scripts`` submodule if you have -`cloned DFHack `, or the ``hack/scripts`` folder -of an installed copy of DFHack. +Scripts included in DFHack live in a separate +:source-scripts:`scripts repository <>`. This can be found in the ``scripts`` +submodule if you have `cloned DFHack `, or the +``hack/scripts`` folder of an installed copy of DFHack. Core ---- diff --git a/docs/Memory-research.rst b/docs/Memory-research.rst index bc7b45fd7..091f1c8e7 100644 --- a/docs/Memory-research.rst +++ b/docs/Memory-research.rst @@ -63,7 +63,7 @@ are not built by default, but can be built by setting the ``BUILD_DEVEL`` Scripts ~~~~~~~ -Several `development scripts ` can be useful for memory research. +Several :dfhack-tag:`development tools ` can be useful for memory research. These include (but are not limited to): - `devel/dump-offsets` From e8ffa55dfe98ecd8003514eada5531fa30d35c03 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 15:16:57 -0700 Subject: [PATCH 085/334] use a label instead of a custom role since I can't seem to figure out how to inject a link to a label via the role processing code --- conf.py | 13 +----------- docs/Categories.rst | 4 ++-- docs/Core.rst | 2 +- docs/Memory-research.rst | 2 +- docs/Tags.rst | 34 ++++++++++++++++---------------- docs/builtins/alias.rst | 2 +- docs/builtins/cls.rst | 2 +- docs/builtins/devel/dump-rpc.rst | 2 +- docs/builtins/die.rst | 2 +- docs/builtins/disable.rst | 2 +- docs/builtins/enable.rst | 2 +- docs/builtins/fpause.rst | 2 +- docs/builtins/help.rst | 2 +- docs/builtins/hide.rst | 2 +- docs/builtins/keybinding.rst | 2 +- docs/builtins/kill-lua.rst | 2 +- docs/builtins/load.rst | 2 +- docs/builtins/ls.rst | 2 +- docs/builtins/plug.rst | 2 +- docs/builtins/reload.rst | 2 +- docs/builtins/sc-script.rst | 2 +- docs/builtins/script.rst | 2 +- docs/builtins/show.rst | 2 +- docs/builtins/tags.rst | 2 +- docs/builtins/type.rst | 2 +- docs/builtins/unload.rst | 2 +- 26 files changed, 43 insertions(+), 54 deletions(-) diff --git a/conf.py b/conf.py index e80b5a027..854693ad0 100644 --- a/conf.py +++ b/conf.py @@ -82,18 +82,7 @@ def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, return [newnode], [] -# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments -def dfhack_tag_role_func(role, rawtext, text, lineno, inliner, - options={}, content=[]): - """Custom role parser for DFHack tags.""" - roles.set_classes(options) - # TODO: link to generated tag index page - newnode = nodes.inline(text, text) - return [newnode], [] - - roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func) -roles.register_canonical_role('dfhack-tag', dfhack_tag_role_func) # -- Autodoc for DFhack plugins and scripts ------------------------------- @@ -129,7 +118,7 @@ DOC_ALL_DIRS = doc_all_dirs() def get_tags(): tags = [] - tag_re = re.compile(r'- :dfhack-tag:`([^`]+)`: (.*)') + tag_re = re.compile(r'- `tag/([^`]+)`: (.*)') with open('docs/Tags.rst') as f: lines = f.readlines() for line in lines: diff --git a/docs/Categories.rst b/docs/Categories.rst index 541f89195..46fc71bdf 100644 --- a/docs/Categories.rst +++ b/docs/Categories.rst @@ -4,8 +4,8 @@ Tool categories =============== DFHack tools are grouped to make them easier to find. Note that a tool can -belong to more than one category. If you'd like to see the full list of tools, -please refer to the `index `. +belong to more than one category. If you'd like to see the full list of tools +in one flat list, please refer to the `index `. .. contents:: Contents :local: diff --git a/docs/Core.rst b/docs/Core.rst index dd00885d0..025e951e6 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -185,7 +185,7 @@ where ``*`` can be any string, including the empty string. A world being loaded can mean a fortress, an adventurer, or legends mode. These files are best used for non-persistent commands, such as setting -a :dfhack-tag:`fix` script to run on `repeat`. +a `tag/fix` script to run on `repeat`. .. _onMapLoad.init: diff --git a/docs/Memory-research.rst b/docs/Memory-research.rst index 091f1c8e7..d9c0833e3 100644 --- a/docs/Memory-research.rst +++ b/docs/Memory-research.rst @@ -63,7 +63,7 @@ are not built by default, but can be built by setting the ``BUILD_DEVEL`` Scripts ~~~~~~~ -Several :dfhack-tag:`development tools ` can be useful for memory research. +Several `development tools ` can be useful for memory research. These include (but are not limited to): - `devel/dump-offsets` diff --git a/docs/Tags.rst b/docs/Tags.rst index 7afbe0a02..432632ba1 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,19 +1,19 @@ :orphan: -- :dfhack-tag:`adventure`: Tools that are useful while in adventure mode -- :dfhack-tag:`fort`: Tools that are useful while in fort mode -- :dfhack-tag:`legends`: Tools that are useful while in legends mode -- :dfhack-tag:`items`: Tools that create or modify in-game items -- :dfhack-tag:`units`: Tools that create or modify units -- :dfhack-tag:`jobs`: Tools that create or modify jobs -- :dfhack-tag:`labors`: Tools that deal with labor assignment -- :dfhack-tag:`auto`: Tools that automatically manage some aspect of your fortress -- :dfhack-tag:`map`: Map modification -- :dfhack-tag:`system`: Tools related to working with DFHack commands or the core DFHack library -- :dfhack-tag:`productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster -- :dfhack-tag:`animals`: Tools that help you manage animals -- :dfhack-tag:`fix`: Tools that fix specific bugs -- :dfhack-tag:`inspection`: Tools that let you inspect game data -- :dfhack-tag:`buildings`: Tools that help you work wtih placing or configuring buildings and furniture -- :dfhack-tag:`quickfort`: Tools that are involved in creating and playing back blueprints -- :dfhack-tag:`dev`: Tools useful for develpers and modders +- `tag/adventure`: Tools that are useful while in adventure mode +- `tag/fort`: Tools that are useful while in fort mode +- `tag/legends`: Tools that are useful while in legends mode +- `tag/items`: Tools that create or modify in-game items +- `tag/units`: Tools that create or modify units +- `tag/jobs`: Tools that create or modify jobs +- `tag/labors`: Tools that deal with labor assignment +- `tag/auto`: Tools that automatically manage some aspect of your fortress +- `tag/map`: Map modification +- `tag/system`: Tools related to working with DFHack commands or the core DFHack library +- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster +- `tag/animals`: Tools that help you manage animals +- `tag/fix`: Tools that fix specific bugs +- `tag/inspection`: Tools that let you inspect game data +- `tag/buildings`: Tools that help you work wtih placing or configuring buildings and furniture +- `tag/quickfort`: Tools that are involved in creating and playing back blueprints +- `tag/dev`: Tools useful for develpers and modders diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 6de550875..234f8ae4a 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,7 +1,7 @@ alias ===== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`alias` :index:`Configure helper aliases for other DFHack commands. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index 5c1189951..2f5387c7f 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,7 +1,7 @@ cls === -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`cls` :index:`Clear the terminal screen. ` Can also diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index 05f1d4e9e..2f798c79e 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -1,7 +1,7 @@ devel/dump-rpc ============== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`devel/dump-rpc` :index:`Write RPC endpoint information to the specified file. diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index c2ea083fd..df53c94bd 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,7 +1,7 @@ die === -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`die` :index:`Instantly exit DF without saving. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 1e2336545..d17706486 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,7 +1,7 @@ disable ======= -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`disable` :index:`Deactivate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 4630c2004..1a714f602 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,7 +1,7 @@ enable ====== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`enable` :index:`Activate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index 927206e8d..f40f6d55c 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,7 +1,7 @@ fpause ====== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`fpause` :index:`Forces DF to pause. ` This is useful when diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 6249bdbe9..290d62e4c 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,7 +1,7 @@ help ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`help` :index:`Display help about a command or plugin. diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 3c79f9ff0..e4f91abaa 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,7 +1,7 @@ hide ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`hide` :index:`Hide the DFHack terminal window. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index 27ab86da6..2ad9e1542 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,7 +1,7 @@ keybinding ========== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`keybinding` :index:`Create hotkeys that will run DFHack commands. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 3f4bbf3c4..4b93b8734 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,7 +1,7 @@ kill-lua ======== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`kill-lua` :index:`Gracefully stops any currently-running Lua scripts. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index 22e8067f3..8e51847dd 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,7 +1,7 @@ load ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`load` :index:`Load and register a plugin library. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 775ccfc63..e68b740c7 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,7 +1,7 @@ ls == -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`ls` :index:`List available DFHack commands. ` diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 7dbbf0115..47efe66b3 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,7 +1,7 @@ plug ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`plug` :index:`List available plugins and whether they are enabled. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index a5ab60859..cde113ac8 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,7 +1,7 @@ reload ====== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`reload` :index:`Reload a loaded plugin. ` Developers diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index feabe4c6f..bcedb610e 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,7 +1,7 @@ sc-script ========= -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`sc-script` :index:`Run commands when game state changes occur. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index b83f19171..ef1aab64a 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,7 +1,7 @@ script ====== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`script` :index:`Execute a batch file of DFHack commands. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index e337bbbb6..b22d4bab9 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,7 +1,7 @@ show ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`show` :index:`Unhides the DFHack terminal window. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index cc5c86a4b..b601a73a1 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,7 +1,7 @@ tags ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`tags` :index:`List the strings that DFHack tools can be tagged with. diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 33ef7562f..755548da9 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,7 +1,7 @@ type ==== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`type` :index:`Describe how a command is implemented. diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index 3d1575c20..1fc8c311e 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,7 +1,7 @@ unload ====== -Tags: :dfhack-tag:`system` +Tags: `tag/system` :dfhack-keybind:`unload` :index:`Unload a plugin from memory. ` From f9c8d36e0357b2f6f48eba1db9fb87bedf4d44d8 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 15:21:16 -0700 Subject: [PATCH 086/334] mark the generated index as an orphan --- conf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conf.py b/conf.py index 854693ad0..b36e8aaad 100644 --- a/conf.py +++ b/conf.py @@ -135,12 +135,11 @@ def get_open_mode(): def generate_tag_indices(): os.makedirs('docs/tags', mode=0o755, exist_ok=True) with open('docs/tags/index.rst', get_open_mode()) as topidx: - #topidx.write(':orphan:\n\n') + topidx.write(':orphan:\n\n') for tag_tuple in get_tags(): tag = tag_tuple[0] with open(('docs/tags/{name}.rst').format(name=tag), get_open_mode()) as tagidx: - #tagidx.write(':orphan:\n\n') tagidx.write('TODO: add links to the tools that have this tag') topidx.write(('.. _tag/{name}:\n\n').format(name=tag)) topidx.write(('{name}\n').format(name=tag)) From 47af2ef396c175beedf1bf90ea5c2d737a719494 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 15:28:09 -0700 Subject: [PATCH 087/334] exclude the generated included tags/ files --- conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conf.py b/conf.py index b36e8aaad..b2c5b3008 100644 --- a/conf.py +++ b/conf.py @@ -295,6 +295,7 @@ exclude_patterns = [ 'build*', 'depends/*', 'docs/html/*', + 'docs/tags/*', 'docs/text/*', 'docs/builtins/*', 'docs/plugins/*', From 93923e12f4a74c2dc0dc7e220e65569fe4266901 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 15:31:58 -0700 Subject: [PATCH 088/334] no need to mark the included file as an orphan --- conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conf.py b/conf.py index b2c5b3008..814c7c2cb 100644 --- a/conf.py +++ b/conf.py @@ -135,7 +135,6 @@ def get_open_mode(): def generate_tag_indices(): os.makedirs('docs/tags', mode=0o755, exist_ok=True) with open('docs/tags/index.rst', get_open_mode()) as topidx: - topidx.write(':orphan:\n\n') for tag_tuple in get_tags(): tag = tag_tuple[0] with open(('docs/tags/{name}.rst').format(name=tag), From 19a49059337b56bfcea47e4c4f3a364c91ebee79 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 16:42:36 -0700 Subject: [PATCH 089/334] update docs for "c" plugins --- docs/plugins/changeitem.rst | 5 ++- docs/plugins/changelayer.rst | 9 +++-- docs/plugins/changevein.rst | 9 +++-- docs/plugins/clean.rst | 37 ------------------ docs/plugins/cleanconst.rst | 11 +++--- docs/plugins/cleaners.rst | 46 +++++++++++++++++++++++ docs/plugins/cleanowned.rst | 11 +++--- docs/plugins/command-prompt.rst | 19 ++++++---- docs/plugins/confirm.rst | 22 +++++++---- docs/plugins/createitem.rst | 66 +++++++++++++++++---------------- docs/plugins/cromulate.rst | 14 ++++--- docs/plugins/cursecheck.rst | 66 ++++++++++++++++++++------------- docs/plugins/spotclean.rst | 14 ------- plugins/changelayer.cpp | 2 +- plugins/changevein.cpp | 2 +- plugins/cleaners.cpp | 4 +- plugins/command-prompt.cpp | 9 ++--- plugins/confirm.cpp | 9 +---- plugins/createitem.cpp | 25 ++----------- plugins/cromulate.cpp | 4 +- plugins/cursecheck.cpp | 5 ++- 21 files changed, 199 insertions(+), 190 deletions(-) delete mode 100644 docs/plugins/clean.rst create mode 100644 docs/plugins/cleaners.rst delete mode 100644 docs/plugins/spotclean.rst diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index 0a841511c..5dd537a60 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -4,8 +4,9 @@ changeitem Tags: :dfhack-keybind: -Allows changing item material and base quality. By default, a change is only -allowed if the existing and desired item materials are of the same subtype +:index:`Change item material and base quality. +` By default, a change is +only allowed if the existing and desired item materials are of the same subtype (for example wood -> wood, stone -> stone, etc). But since some transformations work pretty well and may be desired you can override this with ``force``. Note that forced changes can possibly result in items that crafters and haulers diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 535409e90..76d631cdf 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -4,10 +4,11 @@ changelayer Tags: :dfhack-keybind: -Change the material of an entire geology layer. Note that one layer can stretch -across many z-levels, and changes to the geology layer will affect all -surrounding regions, not just your embark! Mineral veins and gem clusters will -not be affected. Use `changevein` if you want to modify those. +:index:`Change the material of an entire geology layer. +` Note that one +layer can stretch across many z-levels, and changes to the geology layer will +affect all surrounding regions, not just your embark! Mineral veins and gem +clusters will not be affected. Use `changevein` if you want to modify those. tl;dr: You will end up with changing large areas in one go, especially if you use it in lower z levels. Use this command with care! diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index f4871772e..7de94a3f0 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -4,10 +4,11 @@ changevein Tags: :dfhack-keybind: -Changes the material of a mineral inclusion. You can change it to any incorganic -material RAW id. Note that this command only affects tiles within the current -16x16 block - for large veins and clusters, you will need to use this command -multiple times. +:index:`Change the material of a mineral inclusion. +` You can change it to +any incorganic material RAW id. Note that this command only affects tiles within +the current 16x16 block - for large veins and clusters, you will need to use +this command multiple times. You can use the `probe` command to discover the material RAW ids for existing veins that you want to duplicate. diff --git a/docs/plugins/clean.rst b/docs/plugins/clean.rst deleted file mode 100644 index 54421ce91..000000000 --- a/docs/plugins/clean.rst +++ /dev/null @@ -1,37 +0,0 @@ -clean -===== - -Tags: -:dfhack-keybind: - -Removes contaminants from tiles, items, and units. More specifically, it -cleans all the splatter that get scattered all over the map and that clings to -your items and units. In an old fortress, this can significantly reduce FPS lag. -It can also spoil your !!FUN!!, so think before you use it. - -Usage:: - - clean all|map|items|units|plants [] - -By default, cleaning the map leaves mud and snow alone. Note that cleaning units -includes hostiles, and that cleaning items removes poisons from weapons. - -Options -------- - -When cleaning the map, you can specify extra options for extra cleaning: - -- ``mud`` - Also remove mud. -- ``item`` - Also remove item spatter, like fallen leaves and flowers. -- ``snow`` - Also remove snow coverings. - -Examples --------- - -- ``clean all`` - Clean everything that can be cleaned (except mud and snow). -- ``clean all mud item snow`` - Removes all spatter, including mud, leaves, and snow from map tiles. diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index c34bac6bf..286069b63 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -2,12 +2,13 @@ cleanconst ========== Tags: -:dfhack-keybind: +:dfhack-keybind:`cleanconst` -Cleans up construction materials. This tool alters all constructions on the map -so that they spawn their building component when they are disassembled, allowing -their actual build items to be safely deleted. This can improve FPS when you -have many constructions on the map. +:index:`Cleans up construction materials. +` This tool alters all +constructions on the map so that they spawn their building component when they +are disassembled, allowing their actual build items to be safely deleted. This +can improve FPS when you have many constructions on the map. Usage:: diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst new file mode 100644 index 000000000..b290a73a4 --- /dev/null +++ b/docs/plugins/cleaners.rst @@ -0,0 +1,46 @@ +cleaners +======== + +Tags: +:dfhack-keybind:`clean` +:dfhack-keybind:`spotclean` + +:index:`Removes contaminants from tiles, items, and units. +` More specifically, +it cleans all the splatter that get scattered all over the map and that clings +to your items and units. In an old fortress, this can significantly reduce FPS +lag. It can also spoil your !!FUN!!, so think before you use it. + +Usage:: + + clean all|map|items|units|plants [] + spotclean + +By default, cleaning the map leaves mud and snow alone. Note that cleaning units +includes hostiles, and that cleaning items removes poisons from weapons. + +``spotclean`` works like ``clean map snow mud``, +:index:`removing all contaminants from the tile under the cursor. +` This is ideal +if you just want to clean a specific tile but don't want the `clean` command to +remove all the glorious blood from your entranceway. + +Options +------- + +When cleaning the map, you can specify extra options for extra cleaning: + +- ``mud`` + Also remove mud. +- ``item`` + Also remove item spatter, like fallen leaves and flowers. +- ``snow`` + Also remove snow coverings. + +Examples +-------- + +- ``clean all`` + Clean everything that can be cleaned (except mud and snow). +- ``clean all mud item snow`` + Removes all spatter, including mud, leaves, and snow from map tiles. diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst index 27e4806a2..e827989c9 100644 --- a/docs/plugins/cleanowned.rst +++ b/docs/plugins/cleanowned.rst @@ -2,12 +2,13 @@ cleanowned ========== Tags: -:dfhack-keybind: +:dfhack-keybind:`cleanowned` -Confiscates and dumps garbage owned by dwarves. This tool gets dwarves to give -up ownership of scattered items and items with heavy wear and then marks those -items for dumping. Now you can finally get your dwarves to give up their rotten -food and tattered loincloths and go get new ones! +:index:`Confiscates and dumps garbage owned by dwarves. +` This tool gets +dwarves to give up ownership of scattered items and items with heavy wear and +then marks those items for dumping. Now you can finally get your dwarves to give +up their rotten food and tattered loincloths and go get new ones! Usage:: diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 061b405ef..6e3d2a829 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -1,16 +1,21 @@ command-prompt ============== -An in-game DFHack terminal, where you can enter other commands. +Tags: :dfhack-keybind:`command-prompt` -Usage: ``command-prompt [entry]`` +:index:`An in-game DFHack terminal, where you can enter other commands. +` -If called with an entry, it starts with that text filled in. -Most useful for developers, who can set a keybinding to open -a laungage interpreter for lua or Ruby by starting with the -`:lua ` or `:rb ` commands. +Usage:: -Otherwise somewhat similar to `gui/quickcmd`. + command-prompt [entry] + +If called with parameters, it starts with that text in the command edit area. +This is most useful for developers, who can set a keybinding to open a laungage +interpreter for lua or Ruby by starting with the `:lua ` or `:rb ` +portions of the command already filled in. + +Also see `gui/quickcmd` for a different take on running commands from the UI. .. image:: ../images/command-prompt.png diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index c7fb89bb0..25cc04956 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -1,12 +1,20 @@ confirm ======= -Implements several confirmation dialogs for potentially destructive actions -(for example, seizing goods from traders or deleting hauling routes). + +Tags: +:dfhack-keybind:`confirm` + +:index:`Adds confirmation dialogs for destructive actions. +` Now you can get +the chance to avoid seizing goods from traders or deleting a hauling route in +case you hit the key accidentally. Usage: -:enable confirm: Enable all confirmations; alias ``confirm enable all``. - Replace with ``disable`` to disable. -:confirm help: List available confirmation dialogues. -:confirm enable option1 [option2...]: - Enable (or disable) specific confirmation dialogues. +- ``enable confirm``, ``confirm enable all`` + Enable all confirmation options. Replace with ``disable`` to disable all. +- ``confirm enable option1 [option2...]`` + Enable (or ``disable``) specific confirmation dialogs. + +When run without parameters, ``confirm`` will report which confirmation dialogs +are currently enabled. diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index f37154f80..13ecc2e58 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -1,48 +1,52 @@ createitem ========== -Allows creating new items of arbitrary types and made of arbitrary materials. A -unit must be selected in-game to use this command. By default, items created are -spawned at the feet of the selected unit. -Specify the item and material information as you would indicate them in -custom reaction raws, with the following differences: +Tags: +:dfhack-keybind:`createitem` + +:index:`Create arbitrary items. ` You can +create new items of any type and made of any material. A unit must be selected +in-game to use this command. By default, items created are spawned at the feet +of the selected unit. + +Specify the item and material information as you would indicate them in custom +reaction raws, with the following differences: * Separate the item and material with a space rather than a colon * If the item has no subtype, the ``:NONE`` can be omitted -* If the item is ``REMAINS``, ``FISH``, ``FISH_RAW``, ``VERMIN``, ``PET``, or ``EGG``, - specify a ``CREATURE:CASTE`` pair instead of a material token. +* If the item is ``REMAINS``, ``FISH``, ``FISH_RAW``, ``VERMIN``, ``PET``, or + ``EGG``, then specify a ``CREATURE:CASTE`` pair instead of a material token. * If the item is a ``PLANT_GROWTH``, specify a ``PLANT_ID:GROWTH_ID`` pair instead of a material token. Corpses, body parts, and prepared meals cannot be created using this tool. -To obtain the item and material tokens of an existing item, run -``createitem inspect``. Its output can be passed directly as arguments to -``createitem`` to create new matching items, as long as the item type is -supported. - -Examples: - -* Create 2 pairs of steel gauntlets:: +Usage: - createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2 +- ``createitem []`` + Create copies (default is 1) of the specified item made out of the + specified material. +- ``createitem inspect`` + Obtain the item and material tokens of an existing item. Its output can be + used directly as arguments to ``createitem`` to create new matching items + (as long as the item type is supported). +- ``createitem floor|item|building`` + Subsequently created items will be placed on the floor beneath the selected + unit's, inside the selected item, or as part of the selected building. -* Create tower-cap logs:: +.. note:: - createitem WOOD PLANT_MAT:TOWER_CAP:WOOD + ``createitem building`` is good for loading traps, but if you use it with + workshops, you will have to deconstruct the workshop to access the item. -* Create bilberries:: - - createitem PLANT_GROWTH BILBERRY:FRUIT - -For more examples, :wiki:`see this wiki page `. - -To change where new items are placed, first run the command with a -destination type while an appropriate destination is selected. +Examples: -Options: +- ``createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2`` + Create 2 pairs of steel gauntlets (that is, 2 left gauntlets and 2 right + gauntlets). +- ``createitem WOOD PLANT_MAT:TOWER_CAP:WOOD 100`` + Create 100 tower-cap logs. +- ``createitem PLANT_GROWTH BILBERRY:FRUIT`` + Create a single bilberry. -:floor: Subsequent items will be placed on the floor beneath the selected unit's feet. -:item: Subsequent items will be stored inside the currently selected item. -:building: Subsequent items will become part of the currently selected building. - Good for loading traps; do not use with workshops (or deconstruct to use the item). +For more examples, :wiki:`the wiki `. diff --git a/docs/plugins/cromulate.rst b/docs/plugins/cromulate.rst index ca07ae0ca..b025fe877 100644 --- a/docs/plugins/cromulate.rst +++ b/docs/plugins/cromulate.rst @@ -1,12 +1,14 @@ cromulate ========= -Tags: productivity, unit, adventure +Tags: `tag/productivity`, `tag/unit`, `tag/adventure` :dfhack-keybind:`cromulate` -Collects all widgets into a frobozz electric cromufiler. You might want to do -this if you discover that your widgets have become decromulated. It is safe to -run this command periodically even if you are unsure if that's the case. +:index:`Collects all widgets into a frobozz electric cromufiler. +` You might +want to do this if you discover that your widgets have become decromulated. It +is safe to run this command periodically even if you are unsure if that's the +case. Usage:: @@ -18,9 +20,9 @@ collect those under the cursor. Options: -:``-d``, ``--destroy``: +- ``-d``, ``--destroy`` Destroy the widgets instead of collecting them into the cromufiler. -:``-q``, ``--quiet``: +- ``-q``, ``--quiet`` Don't display any informational output. Errors will still be printed to the console. diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index 1bf1803fa..79da6d621 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -1,42 +1,56 @@ cursecheck ========== -Checks a single map tile or the whole map/world for cursed creatures (ghosts, -vampires, necromancers, werebeasts, zombies). -With an active in-game cursor only the selected tile will be observed. -Without a cursor the whole map will be checked. +Tags: +:dfhack-keybind:`cursecheck` -By default cursed creatures will be only counted in case you just want to find -out if you have any of them running around in your fort. Dead and passive -creatures (ghosts who were put to rest, killed vampires, ...) are ignored. -Undead skeletons, corpses, bodyparts and the like are all thrown into the curse -category "zombie". Anonymous zombies and resurrected body parts will show -as "unnamed creature". +:index:`Check for cursed creatures. ` +This command checks a single map tile (or the whole map/world) for cursed +creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). + +With an active in-game cursor, only the selected tile will be checked. Without a +cursor, the whole map will be checked. + +By default, you will just see the count of cursed creatures in case you just +want to find out if you have any of them running around in your fort. Dead and +passive creatures (ghosts who were put to rest, killed vampires, etc.) are +ignored. Undead skeletons, corpses, bodyparts and the like are all thrown into +the curse category "zombie". Anonymous zombies and resurrected body parts will +show as "unnamed creature". + +Usage:: + + cursecheck [] Options: -:detail: Print full name, date of birth, date of curse and some status - info (some vampires might use fake identities in-game, though). -:ids: Print the creature and race IDs. -:nick: Set the type of curse as nickname (does not always show up - in-game, some vamps don't like nicknames). -:all: Include dead and passive cursed creatures (can result in a quite - long list after having FUN with necromancers). -:verbose: Print all curse tags (if you really want to know it all). +- ``detail`` + Print full name, date of birth, date of curse, and some status info (some + vampires might use fake identities in-game, though). +- ``nick`` + Set the type of curse as nickname (does not always show up in-game; some + vamps don't like nicknames). +- ``ids`` + Print the creature and race IDs. +- ``all`` + Include dead and passive cursed creatures (this can result in quite a long + list after having !!FUN!! with necromancers). +- ``verbose`` + Print all curse tags (if you really want to know it all). Examples: -``cursecheck detail all`` - Give detailed info about all cursed creatures including deceased ones (no - in-game cursor). -``cursecheck nick`` - Give a nickname all living/active cursed creatures on the map(no in-game - cursor). +- ``cursecheck`` + Display a count of cursed creatures on the map (or under the cursor). +- ``cursecheck detail all`` + Give detailed info about all cursed creatures including deceased ones. +- ``cursecheck nick`` + Give a nickname all living/active cursed creatures. .. note:: If you do a full search (with the option "all") former ghosts will show up with the cursetype "unknown" because their ghostly flag is not set. - Please report any living/active creatures with cursetype "unknown" - - this is most likely with mods which introduce new types of curses. + If you see any living/active creatures with a cursetype of "unknown", then + it is most likely a new type of curse introduced by a mod. diff --git a/docs/plugins/spotclean.rst b/docs/plugins/spotclean.rst deleted file mode 100644 index dcad047a4..000000000 --- a/docs/plugins/spotclean.rst +++ /dev/null @@ -1,14 +0,0 @@ -spotclean -========= - -Tags: -:dfhack-keybind: - -Cleans a map tile of contaminants and spatter. It works like -``clean map snow mud``, but only for the tile under the cursor. Ideal if you -just want to clean a specific tile but don't want the `clean` command to remove -all the glorious blood from your entranceway. - -Usage:: - - spotclean diff --git a/plugins/changelayer.cpp b/plugins/changelayer.cpp index 347f29b95..7520ac932 100644 --- a/plugins/changelayer.cpp +++ b/plugins/changelayer.cpp @@ -37,7 +37,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("changevein", - "Changes the material of a mineral inclusion.", + "Change the material of a mineral inclusion.", df_changevein)); return CR_OK; } diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 7389488f1..00075e9d7 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -242,11 +242,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "command-prompt", "Shows a command prompt on window.", - show_prompt, hotkey_allow_all, - "command-prompt [entry] - shows a cmd prompt in df window." - " Entry is used for default prefix (e.g. ':lua')" - )); + "command-prompt", + "An in-game DFHack terminal.", + show_prompt, + hotkey_allow_all)); return CR_OK; } diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 32da4e1f8..5cef6376f 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -547,13 +547,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("createitem", "Create arbitrary items.", df_createitem, false, - "Syntax: createitem [count]\n" - " - Item token for what you wish to create, as specified in custom\n" - " reactions. If the item has no subtype, omit the :NONE.\n" - " - The material you want the item to be made of, as specified\n" - " in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n" - " PET, and EGG, replace this with a creature ID and caste.\n" - " For PLANT_GROWTH, replace this with a plant ID and growth ID.\n" - " [count] - How many of the item you wish to create.\n" - "\n" - "To obtain the item and material of an existing item, run \n" - "'createitem inspect' with that item selected in-game.\n" - "\n" - "To use this command, you must select which unit will create the items.\n" - "By default, items created will be placed at that unit's feet.\n" - "To change this, run 'createitem '.\n" - "Valid destinations:\n" - "* floor - Place items on floor beneath maker's feet.\n" - "* item - Place items inside selected container.\n" - "* building - Place items inside selected building.\n" - )); + commands.push_back( + PluginCommand("createitem", + "Create arbitrary items.", + df_createitem)); return CR_OK; } diff --git a/plugins/cromulate.cpp b/plugins/cromulate.cpp index 9f7fdb9f9..7ed9ed115 100644 --- a/plugins/cromulate.cpp +++ b/plugins/cromulate.cpp @@ -13,9 +13,7 @@ command_result cromulate (color_ostream &out, std::vector & parame DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("cromulate", "in-cpp plugin short desc", //to use one line in the ``[DFHack]# ls`` output - cromulate, - false, - "in-cpp plugin long help")); + cromulate)); return CR_OK; } diff --git a/plugins/cursecheck.cpp b/plugins/cursecheck.cpp index d20c645ab..007c81228 100644 --- a/plugins/cursecheck.cpp +++ b/plugins/cursecheck.cpp @@ -79,9 +79,10 @@ command_result cursecheck (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("cursecheck", + commands.push_back(PluginCommand( + "cursecheck", "Check for cursed creatures (undead, necromancers...)", - cursecheck, false )); + cursecheck)); return CR_OK; } From 65b3ce6e9698abbb2e634f72fcf15ab926f84ddc Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:45:23 -0700 Subject: [PATCH 090/334] remove unattached docs and plugins --- docs/plugins/alltraffic.rst | 11 -- docs/plugins/autobutcher.rst | 91 ---------- docs/plugins/autonestbox.rst | 19 --- docs/plugins/digcircle.rst | 35 ---- docs/plugins/digexp.rst | 31 ---- docs/plugins/digtype.rst | 19 --- plugins/dfstream.cpp | 316 ----------------------------------- 7 files changed, 522 deletions(-) delete mode 100644 docs/plugins/alltraffic.rst delete mode 100644 docs/plugins/autobutcher.rst delete mode 100644 docs/plugins/autonestbox.rst delete mode 100644 docs/plugins/digcircle.rst delete mode 100644 docs/plugins/digexp.rst delete mode 100644 docs/plugins/digtype.rst delete mode 100644 plugins/dfstream.cpp diff --git a/docs/plugins/alltraffic.rst b/docs/plugins/alltraffic.rst deleted file mode 100644 index 1e9116954..000000000 --- a/docs/plugins/alltraffic.rst +++ /dev/null @@ -1,11 +0,0 @@ -alltraffic -========== -Set traffic designations for every single tile of the map - useful for resetting -traffic designations. See also `filltraffic`, `restrictice`, and `restrictliquids`. - -Options: - -:H: High Traffic -:N: Normal Traffic -:L: Low Traffic -:R: Restricted Traffic diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst deleted file mode 100644 index d761cbdbe..000000000 --- a/docs/plugins/autobutcher.rst +++ /dev/null @@ -1,91 +0,0 @@ -autobutcher -=========== -Assigns lifestock for slaughter once it reaches a specific count. Requires that -you add the target race(s) to a watch list. Only tame units will be processed. - -Units will be ignored if they are: - -* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool - individually, or `zone` ``nick`` for groups) -* Caged, if and only if the cage is defined as a room (to protect zoos) -* Trained for war or hunting - -Creatures who will not reproduce (because they're not interested in the -opposite sex or have been gelded) will be butchered before those who will. -Older adults and younger children will be butchered first if the population -is above the target (default 1 male, 5 female kids and adults). Note that -you may need to set a target above 1 to have a reliable breeding population -due to asexuality etc. See `fix-ster` if this is a problem. - -Options: - -:example: Print some usage examples. -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep : Changes the timer to sleep X frames between runs. -:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, - BIRD_TURKEY, etc) or a list of ids seperated by spaces or - the keyword 'all' which affects all races on your current - watchlist. -:unwatch R: Stop watching race(s). The current target settings will be - remembered. R can be a list of ids or the keyword 'all'. -:forget R: Stop watching race(s) and forget it's/their target settings. - R can be a list of ids or the keyword 'all'. -:autowatch: Automatically adds all new races (animals you buy from merchants, - tame yourself or get from migrants) to the watch list using - default target count. -:noautowatch: Stop auto-adding new races to the watchlist. -:list: Print the current status and watchlist. -:list_export: Print the commands needed to set up status and watchlist, - which can be used to import them to another save (see notes). -:target : - Set target count for specified race(s). The first four arguments - are the number of female and male kids, and female and male adults. - R can be a list of spceies ids, or the keyword ``all`` or ``new``. - ``R = 'all'``: change target count for all races on watchlist - and set the new default for the future. ``R = 'new'``: don't touch - current settings on the watchlist, only set the new default - for future entries. -:list_export: Print the commands required to rebuild your current settings. - -.. note:: - - Settings and watchlist are stored in the savegame, so that you can have - different settings for each save. If you want to copy your watchlist to - another savegame you must export the commands required to recreate your settings. - - To export, open an external terminal in the DF directory, and run - ``dfhack-run autobutcher list_export > filename.txt``. To import, load your - new save and run ``script filename.txt`` in the DFHack terminal. - - -Examples: - -You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, -1 male) of the race alpaca. Once the kids grow up the oldest adults will get -slaughtered. Excess kids will get slaughtered starting with the youngest -to allow that the older ones grow into adults. Any unnamed cats will -be slaughtered as soon as possible. :: - - autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY - autobutcher target 0 0 0 0 CAT - autobutcher watch ALPACA BIRD_TURKEY CAT - autobutcher start - -Automatically put all new races onto the watchlist and mark unnamed tame units -for slaughter as soon as they arrive in your fort. Settings already made -for specific races will be left untouched. :: - - autobutcher target 0 0 0 0 new - autobutcher autowatch - autobutcher start - -Stop watching the races alpaca and cat, but remember the target count -settings so that you can use 'unwatch' without the need to enter the -values again. Note: 'autobutcher unwatch all' works, but only makes sense -if you want to keep the plugin running with the 'autowatch' feature or manually -add some new races with 'watch'. If you simply want to stop it completely use -'autobutcher stop' instead. :: - - autobutcher unwatch ALPACA CAT diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst deleted file mode 100644 index f19cad42c..000000000 --- a/docs/plugins/autonestbox.rst +++ /dev/null @@ -1,19 +0,0 @@ -autonestbox -=========== -Assigns unpastured female egg-layers to nestbox zones. Requires that you create -pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox -must be in the top left corner. Only 1 unit will be assigned per pen, regardless -of the size. The age of the units is currently not checked, most birds grow up -quite fast. Egglayers who are also grazers will be ignored, since confining them -to a 1x1 pasture is not a good idea. Only tame and domesticated own units are -processed since pasturing half-trained wild egglayers could destroy your neat -nestbox zones when they revert to wild. When called without options autonestbox -will instantly run once. - -Options: - -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep: Must be followed by number X. Changes the timer to sleep X - frames between runs. diff --git a/docs/plugins/digcircle.rst b/docs/plugins/digcircle.rst deleted file mode 100644 index fc31b65df..000000000 --- a/docs/plugins/digcircle.rst +++ /dev/null @@ -1,35 +0,0 @@ -digcircle -========= -A command for easy designation of filled and hollow circles. -It has several types of options. - -Shape: - -:hollow: Set the circle to hollow (default) -:filled: Set the circle to filled -:#: Diameter in tiles (default = 0, does nothing) - -Action: - -:set: Set designation (default) -:unset: Unset current designation -:invert: Invert designations already present - -Designation types: - -:dig: Normal digging designation (default) -:ramp: Ramp digging -:ustair: Staircase up -:dstair: Staircase down -:xstair: Staircase up/down -:chan: Dig channel - -After you have set the options, the command called with no options -repeats with the last selected parameters. - -Examples: - -``digcircle filled 3`` - Dig a filled circle with diameter = 3. -``digcircle`` - Do it again. diff --git a/docs/plugins/digexp.rst b/docs/plugins/digexp.rst deleted file mode 100644 index e4412d051..000000000 --- a/docs/plugins/digexp.rst +++ /dev/null @@ -1,31 +0,0 @@ -digexp -====== -This command is for :wiki:`exploratory mining `. - -There are two variables that can be set: pattern and filter. - -Patterns: - -:diag5: diagonals separated by 5 tiles -:diag5r: diag5 rotated 90 degrees -:ladder: A 'ladder' pattern -:ladderr: ladder rotated 90 degrees -:clear: Just remove all dig designations -:cross: A cross, exactly in the middle of the map. - -Filters: - -:all: designate whole z-level -:hidden: designate only hidden tiles of z-level (default) -:designated: Take current designation and apply pattern to it. - -After you have a pattern set, you can use ``expdig`` to apply it again. - -Examples: - -``expdig diag5 hidden`` - Designate the diagonal 5 patter over all hidden tiles -``expdig`` - Apply last used pattern and filter -``expdig ladder designated`` - Take current designations and replace them with the ladder pattern diff --git a/docs/plugins/digtype.rst b/docs/plugins/digtype.rst deleted file mode 100644 index 1c2d49eaa..000000000 --- a/docs/plugins/digtype.rst +++ /dev/null @@ -1,19 +0,0 @@ -digtype -======= -For every tile on the map of the same vein type as the selected tile, -this command designates it to have the same designation as the -selected tile. If the selected tile has no designation, they will be -dig designated. -If an argument is given, the designation of the selected tile is -ignored, and all appropriate tiles are set to the specified -designation. - -Options: - -:dig: -:channel: -:ramp: -:updown: up/down stairs -:up: up stairs -:down: down stairs -:clear: clear designation diff --git a/plugins/dfstream.cpp b/plugins/dfstream.cpp deleted file mode 100644 index eb98c181b..000000000 --- a/plugins/dfstream.cpp +++ /dev/null @@ -1,316 +0,0 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" - -#include "DataDefs.h" -#include "df/graphic.h" -#include "df/enabler.h" -#include "df/renderer.h" - -#include -#include -#include "PassiveSocket.h" -#include "tinythread.h" - -using namespace DFHack; -using namespace df::enums; - -using std::string; -using std::vector; - -DFHACK_PLUGIN("dfstream"); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(enabler); - -// Owns the thread that accepts TCP connections and forwards messages to clients; -// has a mutex -class client_pool { - typedef tthread::mutex mutex; - - mutex clients_lock; - std::vector clients; - - // TODO - delete this at some point - tthread::thread * accepter; - - static void accept_clients(void * client_pool_pointer) { - client_pool * p = reinterpret_cast(client_pool_pointer); - CPassiveSocket socket; - socket.Initialize(); - if (socket.Listen((const uint8_t *)"0.0.0.0", 8008)) { - std::cout << "Listening on a socket" << std::endl; - } else { - std::cout << "Not listening: " << socket.GetSocketError() << std::endl; - std::cout << socket.DescribeError() << std::endl; - } - while (true) { - CActiveSocket * client = socket.Accept(); - if (client != 0) { - lock l(*p); - p->clients.push_back(client); - } - } - } - -public: - class lock { - tthread::lock_guard l; - public: - lock(client_pool & p) - : l(p.clients_lock) - { - } - }; - friend class client_pool::lock; - - client_pool() { - accepter = new tthread::thread(accept_clients, this); - } - - // MUST have lock - bool has_clients() { - return !clients.empty(); - } - - // MUST have lock - void add_client(CActiveSocket * sock) { - clients.push_back(sock); - } - - // MUST have lock - void broadcast(const std::string & message) { - unsigned int sz = htonl(message.size()); - for (size_t i = 0; i < clients.size(); ++i) { - clients[i]->Send(reinterpret_cast(&sz), sizeof(sz)); - clients[i]->Send((const uint8_t *) message.c_str(), message.size()); - } - } -}; - -// A decorator (in the design pattern sense) of the DF renderer class. -// Sends the screen contents to a client_pool. -class renderer_decorator : public df::renderer { - // the renderer we're decorating - df::renderer * inner; - - // how many frames have passed since we last sent a frame - int framesNotPrinted; - - // set to false in the destructor - bool * alive; - - // clients to which we send the frame - client_pool clients; - - // The following three methods facilitate copying of state to the inner object - void set_to_null() { - screen = NULL; - screentexpos = NULL; - screentexpos_addcolor = NULL; - screentexpos_grayscale = NULL; - screentexpos_cf = NULL; - screentexpos_cbr = NULL; - screen_old = NULL; - screentexpos_old = NULL; - screentexpos_addcolor_old = NULL; - screentexpos_grayscale_old = NULL; - screentexpos_cf_old = NULL; - screentexpos_cbr_old = NULL; - } - - void copy_from_inner() { - screen = inner->screen; - screentexpos = inner->screentexpos; - screentexpos_addcolor = inner->screentexpos_addcolor; - screentexpos_grayscale = inner->screentexpos_grayscale; - screentexpos_cf = inner->screentexpos_cf; - screentexpos_cbr = inner->screentexpos_cbr; - screen_old = inner->screen_old; - screentexpos_old = inner->screentexpos_old; - screentexpos_addcolor_old = inner->screentexpos_addcolor_old; - screentexpos_grayscale_old = inner->screentexpos_grayscale_old; - screentexpos_cf_old = inner->screentexpos_cf_old; - screentexpos_cbr_old = inner->screentexpos_cbr_old; - } - - void copy_to_inner() { - inner->screen = screen; - inner->screentexpos = screentexpos; - inner->screentexpos_addcolor = screentexpos_addcolor; - inner->screentexpos_grayscale = screentexpos_grayscale; - inner->screentexpos_cf = screentexpos_cf; - inner->screentexpos_cbr = screentexpos_cbr; - inner->screen_old = screen_old; - inner->screentexpos_old = screentexpos_old; - inner->screentexpos_addcolor_old = screentexpos_addcolor_old; - inner->screentexpos_grayscale_old = screentexpos_grayscale_old; - inner->screentexpos_cf_old = screentexpos_cf_old; - inner->screentexpos_cbr_old = screentexpos_cbr_old; - } - -public: - renderer_decorator(df::renderer * inner, bool * alive) - : inner(inner) - , framesNotPrinted(0) - , alive(alive) - { - copy_from_inner(); - } - virtual void update_tile(int x, int y) { - copy_to_inner(); - inner->update_tile(x, y); - } - virtual void update_all() { - copy_to_inner(); - inner->update_all(); - } - virtual void render() { - copy_to_inner(); - inner->render(); - - ++framesNotPrinted; - int gfps = enabler->calculated_gfps; - if (gfps == 0) gfps = 1; - // send a frame roughly every 128 mibiseconds (1 second = 1024 mibiseconds) - if ((framesNotPrinted * 1024) / gfps <= 128) return; - - client_pool::lock lock(clients); - if (!clients.has_clients()) return; - framesNotPrinted = 0; - std::stringstream frame; - frame << gps->dimx << ' ' << gps->dimy << " 0 0 " << gps->dimx << ' ' << gps->dimy << '\n'; - unsigned char * sc_ = gps->screen; - for (int y = 0; y < gps->dimy; ++y) { - unsigned char * sc = sc_; - for (int x = 0; x < gps->dimx; ++x) { - unsigned char ch = sc[0]; - unsigned char bold = (sc[3] != 0) * 8; - unsigned char translate[] = - { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 }; - unsigned char fg = translate[(sc[1] + bold) % 16]; - unsigned char bg = translate[sc[2] % 16]*16; - frame.put(ch); - frame.put(fg+bg); - sc += 4*gps->dimy; - } - sc_ += 4; - } - clients.broadcast(frame.str()); - } - virtual void set_fullscreen() { inner->set_fullscreen(); } - virtual void zoom(df::zoom_commands cmd) { - copy_to_inner(); - inner->zoom(cmd); - } - virtual void resize(int w, int h) { - copy_to_inner(); - inner->resize(w, h); - copy_from_inner(); - } - virtual void grid_resize(int w, int h) { - copy_to_inner(); - inner->grid_resize(w, h); - copy_from_inner(); - } - virtual ~renderer_decorator() { - *alive = false; - if (inner) { - copy_to_inner(); - delete inner; - inner = 0; - } - set_to_null(); - } - virtual bool get_mouse_coords(int *x, int *y) { return inner->get_mouse_coords(x, y); } - virtual bool uses_opengl() { return inner->uses_opengl(); } - - static renderer_decorator * hook(df::renderer *& ptr, bool * alive) { - renderer_decorator * r = new renderer_decorator(ptr, alive); - ptr = r; - return r; - } - - static void unhook(df::renderer *& ptr, renderer_decorator * dec, color_ostream & out) { - dec->copy_to_inner(); - ptr = dec->inner; - dec->inner = 0; - delete dec; - } -}; - -inline df::renderer *& active_renderer() { - return enabler->renderer; -} - -// This class is a smart pointer around a renderer_decorator. -// It should only be assigned r_d pointers that use the alive-pointer of this -// instance. -// If the r_d has been deleted by an external force, this smart pointer doesn't -// redelete it. -class auto_renderer_decorator { - renderer_decorator * p; -public: - // pass this member to the ctor of renderer_decorator - bool alive; - - auto_renderer_decorator() - : p(0) - { - } - - ~auto_renderer_decorator() { - reset(); - } - - void reset() { - if (*this) { - delete p; - p = 0; - } - } - - operator bool() { - return (p != 0) && alive; - } - - auto_renderer_decorator & operator=(renderer_decorator *p) { - reset(); - this->p = p; - return *this; - } - - renderer_decorator * get() { - return p; - } - - renderer_decorator * operator->() { - return get(); - } -}; - -auto_renderer_decorator decorator; - -DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) -{ - if (!df::renderer::_identity.can_instantiate()) - { - out.printerr("Cannot allocate a renderer\n"); - return CR_OK; - } - if (!decorator) { - decorator = renderer_decorator::hook(active_renderer(), &decorator.alive); - } - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - if (decorator && active_renderer() == decorator.get()) - { - renderer_decorator::unhook(active_renderer(), decorator.get(), out); - } - decorator.reset(); - return CR_OK; -} -// vim:set sw=4 sts=4 et: From 0858b95c408a07318b17e0e8ad1b84c3cfc9f104 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:45:48 -0700 Subject: [PATCH 091/334] print help from helpdb on CR_WRONG_USAGE --- library/Core.cpp | 6 +++++- library/PluginManager.cpp | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 9b4b065a1..12675054c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1197,7 +1197,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v else { res = plug_mgr->InvokeCommand(con, first, parts); - if(res == CR_NOT_IMPLEMENTED) + if (res == CR_WRONG_USAGE) + { + help_helper(con, first); + } + else if (res == CR_NOT_IMPLEMENTED) { string completed; string filename = findScript(first + ".lua"); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index a1b1bd293..223abfeac 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -503,8 +503,6 @@ command_result Plugin::invoke(color_ostream &out, const std::string & command, s { cr = cmd.function(out, parameters); } - if (cr == CR_WRONG_USAGE && !cmd.usage.empty()) - out << "Usage:\n" << cmd.usage << flush; break; } } From 0f3811b93393bb754c6c558140f7d2c9e40e7c64 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:46:11 -0700 Subject: [PATCH 092/334] update debugfilter docs --- docs/plugins/debug.rst | 149 ++++++++++++++++++----------------------- plugins/debug.cpp | 121 +++++---------------------------- 2 files changed, 81 insertions(+), 189 deletions(-) diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst index 2bef7d28a..331e675ab 100644 --- a/docs/plugins/debug.rst +++ b/docs/plugins/debug.rst @@ -1,89 +1,72 @@ debug ===== -Manager for DFHack runtime debug prints. Debug prints are grouped by plugin name, -category name and print level. Levels are ``trace``, ``debug``, ``info``, -``warning`` and ``error``. + +Tags: +:dfhack-keybind:`debugfilter` + +:index:`Configure verbosity of DFHack debug output. +` Debug output is +grouped by plugin name, category name, and verbosity level. + +The verbosity levels are: + +- ``Trace`` + Possibly very noisy messages which can be printed many times per second. +- ``Debug`` + Messages that happen often but they should happen only a couple of times per + second. +- ``Info`` + Important state changes that happen rarely during normal execution. +- ``Warning`` + Enabled by default. Shows warnings about unexpected events which code + managed to handle correctly. +- ``Error`` + Enabled by default. Shows errors which code can't handle without user + intervention. The runtime message printing is controlled using filters. Filters set the -visible messages of all matching categories. Matching uses regular expression syntax, -which allows listing multiple alternative matches or partial name matches. -This syntax is a C++ version of the ECMA-262 grammar (Javascript regular expressions). -Details of differences can be found at +visible messages of all matching categories. Matching uses regular expression +syntax, which allows listing multiple alternative matches or partial name +matches. This syntax is a C++ version of the ECMA-262 grammar (Javascript +regular expressions). Details of differences can be found at https://en.cppreference.com/w/cpp/regex/ecmascript -Persistent filters are stored in ``dfhack-config/runtime-debug.json``. -Oldest filters are applied first. That means a newer filter can override the -older printing level selection. - -Usage: ``debugfilter [subcommand] [parameters...]`` - -The following subcommands are supported: - -help ----- -Give overall help or a detailed help for a subcommand. - -Usage: ``debugfilter help [subcommand]`` - -category --------- -List available debug plugin and category names. - -Usage: ``debugfilter category [plugin regex] [category regex]`` - -The list can be filtered using optional regex parameters. If filters aren't -given then the it uses ``"."`` regex which matches any character. The regex -parameters are good way to test regex before passing them to ``set``. - -filter ------- -List active and passive debug print level changes. - -Usage: ``debugfilter filter [id]`` - -Optional ``id`` parameter is the id listed as first column in the filter list. -If id is given then the command shows information for the given filter only in -multi line format that is better format if filter has long regex. - -set ---- -Creates a new debug filter to set category printing levels. - -Usage: ``debugfilter set [level] [plugin regex] [category regex]`` - -Adds a filter that will be deleted when DF process exists or plugin is unloaded. - -Usage: ``debugfilter set persistent [level] [plugin regex] [category regex]`` - -Stores the filter in the configuration file to until ``unset`` is used to remove -it. - -Level is the minimum debug printing level to show in log. - -* ``trace``: Possibly very noisy messages which can be printed many times per second - -* ``debug``: Messages that happen often but they should happen only a couple of times per second - -* ``info``: Important state changes that happen rarely during normal execution - -* ``warning``: Enabled by default. Shows warnings about unexpected events which code managed to handle correctly. - -* ``error``: Enabled by default. Shows errors which code can't handle without user intervention. - -unset ------ -Delete a space separated list of filters - -Usage: ``debugfilter unset [id...]`` - -disable -------- -Disable a space separated list of filters but keep it in the filter list - -Usage: ``debugfilter disable [id...]`` - -enable ------- -Enable a space sperate list of filters - -Usage: ``debugfilter enable [id...]`` +Persistent filters are stored in ``dfhack-config/runtime-debug.json``. Oldest +filters are applied first. That means a newer filter can override the older +printing level selection. + +Usage: + +- ``debugfilter category [] []`` + List available debug plugin and category names. If filters aren't givenm + then all plugins/categories are matched. This command is a good way to test + regex parameters before you pass them to ``set``. +- ``debugfilter filter []`` + List active and passive debug print level changes. The optional ``id`` + parameter is the id listed as first column in the filter list. If ``id`` is + given, then the command shows extended information for the given filter + only. +- ``debugfilter set [] [] []`` + Create a new debug filter to set category verbosity levels. This filter + will not be saved when the DF process exists or the plugin is unloaded. +- ``debugfilter set persistent [] [] []`` + Store the filter in the configuration file to until ``unset`` is used to + remove it. +- ``debugfilter unset [ ...]`` + Delete a space separated list of filters. +- ``debugfilter disable [ ...]`` + Disable a space separated list of filters but keep it in the filter list. +- ``debugfilter enable [ ...]`` + Enable a space sperate list of filters. +- ``debugfilter header [enable] | [disable] [ ...]`` + Control which header metadata is shown along with each log message. Run it + without parameters to see the list of configurable elements. Include an + ``enable`` or ``disable`` keyword to change whether specific elements are + shown. + +Examples: + +- ``debugfilter set Warning core script`` + Hide script execution log messages (e.g. "Loading script: + dfhack-config/dfhack.init"), which are normally output at Info verbosity + in the "core" plugin with the "script" category. diff --git a/plugins/debug.cpp b/plugins/debug.cpp index 4fbc48759..f4a22b8d1 100644 --- a/plugins/debug.cpp +++ b/plugins/debug.cpp @@ -104,85 +104,13 @@ JsonArchive& operator>>(JsonArchive& ar, const serialization::nvp& target) static constexpr auto defaultRegex = std::regex::optimize | std::regex::nosubs | std::regex::collate; -static const char* const commandHelp = - " Manage runtime debug print filters.\n" - "\n" - " debugfilter category [ []]\n" - " List categories matching regular expressions.\n" - " debugfilter filter []\n" - " List active filters or show detailed information for a filter.\n" - " debugfilter set [persistent] [ []]\n" - " Set a filter level to categories matching regular expressions.\n" - " debugfilter unset [ ...]\n" - " Unset filters matching space separated list of ids from 'filter'.\n" - " debugfilter disable [ ...]\n" - " Disable filters matching space separated list of ids from 'filter'.\n" - " debugfilter enable [ ...]\n" - " Enable filters matching space separated list of ids from 'filter'.\n" - " debugfilter header [enable] | [disable] [ ...]\n" - " Control which header metadata is shown along with each log message.\n" - " debugfilter help []\n" - " Show detailed help for a command or this help.\n"; -static const char* const commandCategory = - " category [ []]\n" - " List categories with optional filters. Parameters are passed to\n" - " std::regex to limit which once are shown. The first regular\n" - " expression is used to match the category and the second is used\n" - " to match the plugin name.\n"; -static const char* const commandSet = - " set [persistent] [ []]\n" - " Set filtering level for matching categories. 'level' must be one of\n" - " trace, debug, info, warning and error. The 'level' parameter sets\n" - " the lowest message level that will be shown. The command doesn't\n" - " allow filters to disable any error messages.\n" - " Default filter life time is until Dwarf Fortress process exists or\n" - " plugin is unloaded. Passing 'persistent' as second parameter tells\n" - " the plugin to store the filter to dfhack-config. Stored filters\n" - " will be active until always when the plugin is loaded. 'unset'\n" - " command can be used to remove persistent filters.\n" - " Filters are applied FIFO order. The latest filter will override any\n" - " older filter that also matches.\n"; -static const char* const commandFilters = - " filter []\n" - " Show the list of active filters. The first column is 'id' which can\n" - " be used to deactivate filters using 'unset' command.\n" - " Filters are printed in same order as applied - the oldest first.\n"; -static const char* const commandUnset = - " unset [ ...]\n" - " 'unset' takes space separated list of filter ids from 'filter'.\n" - " It will reset any matching category back to the default 'warning'\n" - " level or any other still active matching filter level.\n"; -static const char* const commandDisable = - " disable [ ...]\n" - " 'disable' takes space separated list of filter ids from 'filter'.\n" - " It will reset any matching category back to the default 'warning'\n" - " level or any other still active matching filter level.\n" - " 'disable' will print red filters that were already disabled.\n"; -static const char* const commandEnable = - " enable [ ...]\n" - " 'enable' takes space separated list of filter ids from 'filter'.\n" - " It will reset any matching category back to the default 'warning'\n" - " level or any other still active matching filter level.\n" - " 'enable' will print red filters that were already enabled.\n"; -static const char* const commandHeader = - " header [enable] | [disable] [ ...]\n" - " 'header' allows you to customize what metadata is displayed with\n" - " each log message. Run it without parameters to see the list of\n" - " configurable elements. Include an 'enable' or 'disable' keyword to\n" - " change specific elements.\n"; -static const char* const commandHelpDetails = - " help []\n" - " Show help for any of subcommands. Without any parameters it shows\n" - " short help for all subcommands.\n"; - //! Helper type to hold static dispatch table for subcommands struct CommandDispatch { //! Store handler function pointer and help message for commands struct Command { using handler_t = command_result(*)(color_ostream&,std::vector&); - Command(handler_t handler, const char* help) : - handler_(handler), - help_(help) + Command(handler_t handler) : + handler_(handler) {} command_result operator()(color_ostream& out, @@ -193,12 +121,8 @@ struct CommandDispatch { handler_t handler() const noexcept { return handler_; } - - const char* help() const noexcept - { return help_; } private: handler_t handler_; - const char* help_; }; using dispatch_t = const std::map; //! Name to handler function and help message mapping @@ -1125,28 +1049,14 @@ static command_result configureHeader(color_ostream& out, using DFHack::debugPlugin::CommandDispatch; -static command_result printHelp(color_ostream& out, - std::vector& parameters) -{ - const char* help = commandHelp; - auto iter = CommandDispatch::dispatch.end(); - if (1u < parameters.size()) - iter = CommandDispatch::dispatch.find(parameters[1]); - if (iter != CommandDispatch::dispatch.end()) - help = iter->second.help(); - out << help << std::flush; - return CR_OK; -} - CommandDispatch::dispatch_t CommandDispatch::dispatch { - {"category", {listCategories,commandCategory}}, - {"filter", {listFilters,commandFilters}}, - {"set", {setFilter,commandSet}}, - {"unset", {unsetFilter,commandUnset}}, - {"enable", {enableFilter,commandEnable}}, - {"disable", {disableFilter,commandDisable}}, - {"header", {configureHeader,commandHeader}}, - {"help", {printHelp,commandHelpDetails}}, + {"category", {listCategories}}, + {"filter", {listFilters}}, + {"set", {setFilter}}, + {"unset", {unsetFilter}}, + {"enable", {enableFilter}}, + {"disable", {disableFilter}}, + {"header", {configureHeader}}, }; //! Dispatch command handling to the subcommand or help @@ -1156,13 +1066,14 @@ static command_result commandDebugFilter(color_ostream& out, DEBUG(command,out).print("debugfilter %s, parameter count %zu\n", parameters.size() > 0 ? parameters[0].c_str() : "", parameters.size()); - auto handler = printHelp; auto iter = CommandDispatch::dispatch.end(); if (0u < parameters.size()) iter = CommandDispatch::dispatch.find(parameters[0]); - if (iter != CommandDispatch::dispatch.end()) - handler = iter->second.handler(); - return (handler)(out, parameters); + if (iter != CommandDispatch::dispatch.end()) { + iter->second.handler()(out, parameters); + return CR_OK; + } + return CR_WRONG_USAGE; } } } /* namespace debug */ @@ -1173,9 +1084,7 @@ DFhackCExport DFHack::command_result plugin_init(DFHack::color_ostream& out, commands.emplace_back( "debugfilter", "Manage runtime debug print filters", - DFHack::debugPlugin::commandDebugFilter, - false, - DFHack::debugPlugin::commandHelp); + DFHack::debugPlugin::commandDebugFilter); auto& filMan = DFHack::debugPlugin::FilterManager::getInstance(); DFHack::command_result rv = DFHack::CR_OK; if ((rv = filMan.loadConfig(out)) != DFHack::CR_OK) From 675f2edee2ef414089c8076fec8041c3124fcd3d Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:46:22 -0700 Subject: [PATCH 093/334] update deramp docs --- docs/plugins/deramp.rst | 15 +++++++++++---- plugins/deramp.cpp | 12 +++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index a6b944381..7489e506e 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -1,6 +1,13 @@ deramp ====== -Removes all ramps designated for removal from the map. This is useful for -replicating the old channel digging designation. It also removes any and -all 'down ramps' that can remain after a cave-in (you don't have to designate -anything for that to happen). + +Tags: +:dfhack-keybind:`deramp` + +:index:`Removes all ramps designated for removal from the map. +` It also +removes any "floating" down ramps that can remain after a cave-in. + +Usage:: + + deramp diff --git a/plugins/deramp.cpp b/plugins/deramp.cpp index 62679cae0..1c397242b 100644 --- a/plugins/deramp.cpp +++ b/plugins/deramp.cpp @@ -109,13 +109,11 @@ command_result df_deramp (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand( - "deramp", "Replace all ramps marked for removal with floors.", - df_deramp, false, - " If there are any ramps designated for removal, they will be instantly\n" - " removed. Any ramps that don't have their counterpart will also be removed\n" - " (fixes bugs with caveins)\n" - )); + commands.push_back( + PluginCommand( + "deramp", + "Removes all ramps designated for removal from the map.", + df_deramp)); return CR_OK; } From 4cb57d25f1fe89012c0d54ad7aa0dee24cde8aff Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:46:33 -0700 Subject: [PATCH 094/334] update dig-now docs --- docs/plugins/dig-now.rst | 42 +++++++++++++++++++++------------------- plugins/dig-now.cpp | 23 ++++++---------------- plugins/lua/dig-now.lua | 31 ----------------------------- 3 files changed, 28 insertions(+), 68 deletions(-) diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index b6f4d64f7..4628f10f5 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -1,10 +1,15 @@ dig-now ======= -Instantly completes non-marker dig designations, modifying tile shapes and -creating boulders, ores, and gems as if a miner were doing the mining or -engraving. By default, the entire map is processed and boulder generation -follows standard game rules, but the behavior is configurable. +Tags: +:dfhack-keybind:`dig-now` + +:index:`Instantly complete dig designations. +` This tool will magically +complete non-marker dig designations, modifying tile shapes and creating +boulders, ores, and gems as if a miner were doing the mining or engraving. By +default, the entire map is processed and boulder generation follows standard +game rules, but the behavior is configurable. Note that no units will get mining or engraving experience for the dug/engraved tiles. @@ -25,37 +30,34 @@ that coordinate is processed. Any ```` parameters can either be an ``,,`` triple (e.g. ``35,12,150``) or the string ``here``, which means the position of the active -game cursor should be used. +game cursor should be used. You can use the `position` command to get the +current cursor position if you need it. Examples: -``dig-now`` +- ``dig-now`` Dig designated tiles according to standard game rules. - -``dig-now --clean`` - Dig designated tiles, but don't generate any boulders, ores, or gems. - -``dig-now --dump here`` - Dig tiles and dump all generated boulders, ores, and gems at the tile under - the game cursor. +- ``dig-now --clean`` + Dig all designated tiles, but don't generate any boulders, ores, or gems. +- ``dig-now --dump here`` + Dig tiles and teleport all generated boulders, ores, and gems to the tile + under the game cursor. Options: -:``-c``, ``--clean``: +- ``-c``, ``--clean`` Don't generate any boulders, ores, or gems. Equivalent to ``--percentages 0,0,0,0``. -:``-d``, ``--dump ``: +- ``-d``, ``--dump `` Dump any generated items at the specified coordinates. If the tile at those coordinates is open space or is a wall, items will be generated on the closest walkable tile below. -:``-e``, ``--everywhere``: +- ``-e``, ``--everywhere`` Generate a boulder, ore, or gem for every tile that can produce one. Equivalent to ``--percentages 100,100,100,100``. -:``-h``, ``--help``: - Show quick usage help text. -:``-p``, ``--percentages ,,,``: +- ``-p``, ``--percentages ,,,`` Set item generation percentages for each of the tile categories. The ``vein`` category includes both the large oval clusters and the long stringy mineral veins. Default is ``25,33,100,100``. -:``-z``, ``--cur-zlevel``: +- ``-z``, ``--cur-zlevel`` Restricts the bounds to the currently visible z-level. diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 78b2b1522..bbcabde65 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -838,17 +838,6 @@ static bool get_options(color_ostream &out, return true; } -static void print_help(color_ostream &out) { - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 1) || - !Lua::PushModulePublic(out, L, "plugins.dig-now", "print_help") || - !Lua::SafeCall(out, L, 0, 0)) { - out.printerr("Failed to load dig-now Lua code\n"); - } -} - bool dig_now_impl(color_ostream &out, const dig_now_options &options) { if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); @@ -880,18 +869,18 @@ command_result dig_now(color_ostream &out, std::vector ¶ms) { dig_now_options options; if (!get_options(out, options, params) || options.help) - { - print_help(out); - return options.help ? CR_OK : CR_FAILURE; - } + return CR_WRONG_USAGE; return dig_now_impl(out, options) ? CR_OK : CR_FAILURE; } DFhackCExport command_result plugin_init(color_ostream &, std::vector &commands) { - commands.push_back(PluginCommand( - "dig-now", "Instantly complete dig designations", dig_now, false)); + commands.push_back( + PluginCommand( + "dig-now", + "Instantly complete dig designations.", + dig_now)); return CR_OK; } diff --git a/plugins/lua/dig-now.lua b/plugins/lua/dig-now.lua index 2d7ae40d7..b3ffeb0bc 100644 --- a/plugins/lua/dig-now.lua +++ b/plugins/lua/dig-now.lua @@ -4,37 +4,6 @@ local argparse = require('argparse') local guidm = require('gui.dwarfmode') local utils = require('utils') -local short_help_text = [=[ - -dig-now -======= - -Instantly completes dig designations, modifying map tiles and creating boulders, -ores, and gems as if a miner were doing the mining or engraving. By default, all -dig designations on the map are completed and boulder generation follows -standard game rules, but the behavior is configurable. - -Usage: - - dig-now [ []] [] - -Examples: - -dig-now - Dig all designated tiles according to standard game rules. - -dig-now --clean - Dig designated tiles, but don't generate any boulders, ores, or gems. - -dig-now --dump here - Dig tiles and dump all generated boulders, ores, and gems at the tile under - the game cursor. - -See the online DFHack documentation for details on all options. -]=] - -function print_help() print(short_help_text) end - local function parse_coords(opts, configname, arg) local cursor = argparse.coords(arg, configname) utils.assign(opts[configname], cursor) From a7346dd05b3bb653b9707b574f252b995de495b9 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 17:51:48 -0700 Subject: [PATCH 095/334] add missing labels --- docs/plugins/cleaners.rst | 3 + docs/plugins/cromulate.rst | 2 +- docs/plugins/dig.rst | 86 ++++++++++++++++++++++++++ docs/plugins/filltraffic.rst | 13 ++++ docs/plugins/zone.rst | 113 +++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 1 deletion(-) diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index b290a73a4..9578bb22a 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -1,3 +1,6 @@ +.. _clean: +.. _spotclean: + cleaners ======== diff --git a/docs/plugins/cromulate.rst b/docs/plugins/cromulate.rst index b025fe877..6e659989c 100644 --- a/docs/plugins/cromulate.rst +++ b/docs/plugins/cromulate.rst @@ -1,7 +1,7 @@ cromulate ========= -Tags: `tag/productivity`, `tag/unit`, `tag/adventure` +Tags: `tag/productivity`, `tag/units`, `tag/adventure` :dfhack-keybind:`cromulate` :index:`Collects all widgets into a frobozz electric cromufiler. diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 2b1e96a18..6026bd88a 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -1,4 +1,5 @@ .. _digv: +.. _digtype: dig === @@ -20,3 +21,88 @@ Basic commands: specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, where ``#`` is a number from 1 to 7. If a priority is not specified, the priority selected in-game is used as the default. +digcircle +========= +A command for easy designation of filled and hollow circles. +It has several types of options. + +Shape: + +:hollow: Set the circle to hollow (default) +:filled: Set the circle to filled +:#: Diameter in tiles (default = 0, does nothing) + +Action: + +:set: Set designation (default) +:unset: Unset current designation +:invert: Invert designations already present + +Designation types: + +:dig: Normal digging designation (default) +:ramp: Ramp digging +:ustair: Staircase up +:dstair: Staircase down +:xstair: Staircase up/down +:chan: Dig channel + +After you have set the options, the command called with no options +repeats with the last selected parameters. + +Examples: + +``digcircle filled 3`` + Dig a filled circle with diameter = 3. +``digcircle`` + Do it again. +digtype +======= +For every tile on the map of the same vein type as the selected tile, +this command designates it to have the same designation as the +selected tile. If the selected tile has no designation, they will be +dig designated. +If an argument is given, the designation of the selected tile is +ignored, and all appropriate tiles are set to the specified +designation. + +Options: + +:dig: +:channel: +:ramp: +:updown: up/down stairs +:up: up stairs +:down: down stairs +:clear: clear designation +digexp +====== +This command is for :wiki:`exploratory mining `. + +There are two variables that can be set: pattern and filter. + +Patterns: + +:diag5: diagonals separated by 5 tiles +:diag5r: diag5 rotated 90 degrees +:ladder: A 'ladder' pattern +:ladderr: ladder rotated 90 degrees +:clear: Just remove all dig designations +:cross: A cross, exactly in the middle of the map. + +Filters: + +:all: designate whole z-level +:hidden: designate only hidden tiles of z-level (default) +:designated: Take current designation and apply pattern to it. + +After you have a pattern set, you can use ``expdig`` to apply it again. + +Examples: + +``expdig diag5 hidden`` + Designate the diagonal 5 patter over all hidden tiles +``expdig`` + Apply last used pattern and filter +``expdig ladder designated`` + Take current designations and replace them with the ladder pattern diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index 6a9c57c9f..1f8768ec3 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -1,3 +1,5 @@ +.. _alltraffic: + filltraffic =========== Set traffic designations using flood-fill starting at the cursor. @@ -15,3 +17,14 @@ Example: ``filltraffic H`` When used in a room with doors, it will set traffic to HIGH in just that room. +alltraffic +========== +Set traffic designations for every single tile of the map - useful for resetting +traffic designations. See also `filltraffic`, `restrictice`, and `restrictliquids`. + +Options: + +:H: High Traffic +:N: Normal Traffic +:L: Low Traffic +:R: Restricted Traffic diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index a3112c147..08d561bbc 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -1,3 +1,6 @@ +.. _autobutcher: +.. _autonestbox: + zone ==== Helps a bit with managing activity zones (pens, pastures and pits) and cages. @@ -128,3 +131,113 @@ Examples ``zone tocages count 50 own tame male not grazer`` Stuff up to 50 owned tame male animals who are not grazers into cages built on the current default zone. +autobutcher +=========== +Assigns lifestock for slaughter once it reaches a specific count. Requires that +you add the target race(s) to a watch list. Only tame units will be processed. + +Units will be ignored if they are: + +* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool + individually, or `zone` ``nick`` for groups) +* Caged, if and only if the cage is defined as a room (to protect zoos) +* Trained for war or hunting + +Creatures who will not reproduce (because they're not interested in the +opposite sex or have been gelded) will be butchered before those who will. +Older adults and younger children will be butchered first if the population +is above the target (default 1 male, 5 female kids and adults). Note that +you may need to set a target above 1 to have a reliable breeding population +due to asexuality etc. See `fix-ster` if this is a problem. + +Options: + +:example: Print some usage examples. +:start: Start running every X frames (df simulation ticks). + Default: X=6000, which would be every 60 seconds at 100fps. +:stop: Stop running automatically. +:sleep : Changes the timer to sleep X frames between runs. +:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, + BIRD_TURKEY, etc) or a list of ids seperated by spaces or + the keyword 'all' which affects all races on your current + watchlist. +:unwatch R: Stop watching race(s). The current target settings will be + remembered. R can be a list of ids or the keyword 'all'. +:forget R: Stop watching race(s) and forget it's/their target settings. + R can be a list of ids or the keyword 'all'. +:autowatch: Automatically adds all new races (animals you buy from merchants, + tame yourself or get from migrants) to the watch list using + default target count. +:noautowatch: Stop auto-adding new races to the watchlist. +:list: Print the current status and watchlist. +:list_export: Print the commands needed to set up status and watchlist, + which can be used to import them to another save (see notes). +:target : + Set target count for specified race(s). The first four arguments + are the number of female and male kids, and female and male adults. + R can be a list of spceies ids, or the keyword ``all`` or ``new``. + ``R = 'all'``: change target count for all races on watchlist + and set the new default for the future. ``R = 'new'``: don't touch + current settings on the watchlist, only set the new default + for future entries. +:list_export: Print the commands required to rebuild your current settings. + +.. note:: + + Settings and watchlist are stored in the savegame, so that you can have + different settings for each save. If you want to copy your watchlist to + another savegame you must export the commands required to recreate your settings. + + To export, open an external terminal in the DF directory, and run + ``dfhack-run autobutcher list_export > filename.txt``. To import, load your + new save and run ``script filename.txt`` in the DFHack terminal. + + +Examples: + +You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, +1 male) of the race alpaca. Once the kids grow up the oldest adults will get +slaughtered. Excess kids will get slaughtered starting with the youngest +to allow that the older ones grow into adults. Any unnamed cats will +be slaughtered as soon as possible. :: + + autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY + autobutcher target 0 0 0 0 CAT + autobutcher watch ALPACA BIRD_TURKEY CAT + autobutcher start + +Automatically put all new races onto the watchlist and mark unnamed tame units +for slaughter as soon as they arrive in your fort. Settings already made +for specific races will be left untouched. :: + + autobutcher target 0 0 0 0 new + autobutcher autowatch + autobutcher start + +Stop watching the races alpaca and cat, but remember the target count +settings so that you can use 'unwatch' without the need to enter the +values again. Note: 'autobutcher unwatch all' works, but only makes sense +if you want to keep the plugin running with the 'autowatch' feature or manually +add some new races with 'watch'. If you simply want to stop it completely use +'autobutcher stop' instead. :: + + autobutcher unwatch ALPACA CAT +autonestbox +=========== +Assigns unpastured female egg-layers to nestbox zones. Requires that you create +pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox +must be in the top left corner. Only 1 unit will be assigned per pen, regardless +of the size. The age of the units is currently not checked, most birds grow up +quite fast. Egglayers who are also grazers will be ignored, since confining them +to a 1x1 pasture is not a good idea. Only tame and domesticated own units are +processed since pasturing half-trained wild egglayers could destroy your neat +nestbox zones when they revert to wild. When called without options autonestbox +will instantly run once. + +Options: + +:start: Start running every X frames (df simulation ticks). + Default: X=6000, which would be every 60 seconds at 100fps. +:stop: Stop running automatically. +:sleep: Must be followed by number X. Changes the timer to sleep X + frames between runs. From 5dde613a12bbdd9aae1f80979e34b2035748abca Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 19:06:50 -0700 Subject: [PATCH 096/334] fix formatting errors --- docs/plugins/dig.rst | 3 +++ docs/plugins/filltraffic.rst | 1 + docs/plugins/zone.rst | 2 ++ 3 files changed, 6 insertions(+) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 6026bd88a..c4811883b 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -21,6 +21,7 @@ Basic commands: specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, where ``#`` is a number from 1 to 7. If a priority is not specified, the priority selected in-game is used as the default. + digcircle ========= A command for easy designation of filled and hollow circles. @@ -56,6 +57,7 @@ Examples: Dig a filled circle with diameter = 3. ``digcircle`` Do it again. + digtype ======= For every tile on the map of the same vein type as the selected tile, @@ -75,6 +77,7 @@ Options: :up: up stairs :down: down stairs :clear: clear designation + digexp ====== This command is for :wiki:`exploratory mining `. diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index 1f8768ec3..93306e9eb 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -17,6 +17,7 @@ Example: ``filltraffic H`` When used in a room with doors, it will set traffic to HIGH in just that room. + alltraffic ========== Set traffic designations for every single tile of the map - useful for resetting diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 08d561bbc..be827b47c 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -131,6 +131,7 @@ Examples ``zone tocages count 50 own tame male not grazer`` Stuff up to 50 owned tame male animals who are not grazers into cages built on the current default zone. + autobutcher =========== Assigns lifestock for slaughter once it reaches a specific count. Requires that @@ -222,6 +223,7 @@ add some new races with 'watch'. If you simply want to stop it completely use 'autobutcher stop' instead. :: autobutcher unwatch ALPACA CAT + autonestbox =========== Assigns unpastured female egg-layers to nestbox zones. Requires that you create From cb3e537b383e06dfc00c89e184ead77d58acdc8b Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 20:57:19 -0700 Subject: [PATCH 097/334] fix help message when trying to run a plugin name --- library/Core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 12675054c..72ddc33aa 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1227,7 +1227,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v con.printerr("that is not loaded - try \"load %s\" or check stderr.log\n", first.c_str()); else if (p->size()) - con.printerr("that implements %zi commands - see \"ls %s\" for details\n", + con.printerr("that implements %zi commands - see \"help %s\" for details\n", p->size(), first.c_str()); else con.printerr("but does not implement any commands\n"); From 3141ecbec262d826e4699d64aa6702952cc821c4 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 21:04:22 -0700 Subject: [PATCH 098/334] remove cromulate. it has outlived its purpose --- docs/plugins/cromulate.rst | 36 ------------------------------------ plugins/CMakeLists.txt | 1 - plugins/cromulate.cpp | 26 -------------------------- 3 files changed, 63 deletions(-) delete mode 100644 docs/plugins/cromulate.rst delete mode 100644 plugins/cromulate.cpp diff --git a/docs/plugins/cromulate.rst b/docs/plugins/cromulate.rst deleted file mode 100644 index 6e659989c..000000000 --- a/docs/plugins/cromulate.rst +++ /dev/null @@ -1,36 +0,0 @@ -cromulate -========= - -Tags: `tag/productivity`, `tag/units`, `tag/adventure` -:dfhack-keybind:`cromulate` - -:index:`Collects all widgets into a frobozz electric cromufiler. -` You might -want to do this if you discover that your widgets have become decromulated. It -is safe to run this command periodically even if you are unsure if that's the -case. - -Usage:: - - cromulate [all|here] [] - -When run without parameters, it lists all your widgets. Add the ``all`` keyword -to collect all widgets into the cromufiler, or the ``here`` keyword to just -collect those under the cursor. - -Options: - -- ``-d``, ``--destroy`` - Destroy the widgets instead of collecting them into the cromufiler. -- ``-q``, ``--quiet`` - Don't display any informational output. Errors will still be printed to the - console. - -Examples: - -- ``cromulate`` - Lists all widgets and their positions -- ``cromlate all`` - Gather all widgets into the cromufiler -- ``cromulate here --destroy`` - Destroys the widgets under the cursor diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b9d63839e..283f74fad 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -105,7 +105,6 @@ if(BUILD_SUPPORTED) dfhack_plugin(command-prompt command-prompt.cpp) dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) dfhack_plugin(createitem createitem.cpp) - dfhack_plugin(cromulate cromulate.cpp) dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) dfhack_plugin(deramp deramp.cpp) diff --git a/plugins/cromulate.cpp b/plugins/cromulate.cpp deleted file mode 100644 index 7ed9ed115..000000000 --- a/plugins/cromulate.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "Core.h" -#include -#include -#include - -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("cromulate"); - -command_result cromulate (color_ostream &out, std::vector & parameters); - -DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("cromulate", - "in-cpp plugin short desc", //to use one line in the ``[DFHack]# ls`` output - cromulate)); - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown (color_ostream &out) { - return CR_OK; -} - -command_result cromulate (color_ostream &out, std::vector ¶meters) { - return CR_OK; -} From 9b7cc6180de2b082035eeadf95e0afc9b95a81a5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 22 Jul 2022 21:21:38 -0700 Subject: [PATCH 099/334] don't create entries for non-enableable plugins --- library/lua/helpdb.lua | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index c660a37dd..8b35b0d25 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -273,11 +273,13 @@ local function update_db(old_db, entry_name, text_entry, help_source, kwargs) -- already in db (e.g. from a higher-priority script dir); skip return end - entrydb[entry_name] = { - entry_types=kwargs.entry_types, - short_help=kwargs.short_help, - text_entry=text_entry - } + if not kwargs.text_entry_only then + entrydb[entry_name] = { + entry_types=kwargs.entry_types, + short_help=kwargs.short_help, + text_entry=text_entry + } + end if entry_name ~= text_entry then return end @@ -329,13 +331,16 @@ local function scan_plugins(old_db) kwargs) has_commands = true end - if not includes_plugin and (has_commands or - dfhack.internal.isPluginEnableable(plugin)) then + if includes_plugin then goto continue end + local is_enableable = dfhack.internal.isPluginEnableable(plugin) + if has_commands or is_enableable then update_db(old_db, plugin, plugin, has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, - {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) + {entry_types={[ENTRY_TYPES.PLUGIN]=true}, + text_entry_only=not is_enableable}) end + ::continue:: end end From 62523bdcb1a2da3083b219ef5d320d5f7f828a00 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 15:13:11 -0700 Subject: [PATCH 100/334] update documentation for the documentation --- docs/Documentation.rst | 304 ++++++++++++++++++++++++++++++----------- 1 file changed, 225 insertions(+), 79 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index c035a0366..6be4de37f 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -14,11 +14,15 @@ compiled to HTML, such as automatic tables of contents, cross-linking, special external links (forum, wiki, etc) and more. The documentation is compiled by a Python tool, `Sphinx `_. -The DFHack build process will compile the documentation, but this is disabled -by default due to the additional Python and Sphinx requirements. You typically -only need to build the docs if you're changing them, or perhaps -if you want a local HTML copy; otherwise, you can read an -`online version hosted by ReadTheDocs `_. +The DFHack build process will compile and install the documentation so it can be +displayed in-game by the `help` and `ls` commands (and any other command or GUI that +displays help text), but this is disabled by default due to the additional Python and +Sphinx requirements. If you already have a version of the docs installed (say from a +downloaded release binary), then you only need to build the docs if you're changing them +and want to see the changes reflected in your game. + +You can also build the docs if you just want a local HTML- or text-rendered copy, though +you can always read the `online version `_. (Note that even if you do want a local copy, it is certainly not necessary to compile the documentation in order to read it. Like Markdown, reST documents are @@ -28,6 +32,63 @@ The main thing you lose in plain text format is hyperlinking.) .. contents:: Contents :local: +Concepts and general guidance +============================= + +The source ``.rst`` files are compiled to HTML for viewing in a browser and to text +format for viewing in-game. For in-game help, the help text is read from its installed +location in ``hack/docs`` under the DF directory for in-game display. + +Remember, everything should be documented! If it's not clear *where* a particular thing +should be documented, ask on Discord or in the DFHack thread on Bay12 -- as well as +getting help, you'll be providing valuable feedback that makes it easier for future readers! + +Try to keep lines within 80-100 characters, so it's readable in plain text +in the terminal - Sphinx (our documentation system) will make sure +paragraphs flow. + +Short descriptions +------------------ + +Each command that a user can run, as well as every plugin that can be enabled for some +lasting effect, needs to have a short (~54 character) descriptive string associated with +it. This description text is: + +- used in-game by the `ls` command and DFHack UI screens that list commands +- used in the generated index entries in the HTML docs + +Tags +---- + +To make it easier for players to find related commands, all plugins and commands are marked +with relevant tags. These are used to compile indices and generate cross-links between the +commands, both in the HTML documents and in-game. See the list of available tags +`here ` and think about which categories your new tools belongs in. + +Links +----- + +If it would be helpful to mention another DFHack command, don't just type the +name - add a hyperlink! Specify the link target in backticks, and it will be +replaced with the corresponding title and linked: e.g. ```autolabor``` +=> `autolabor`. Scripts and plugins have link targets that match their names +created for you automatically. + +If you want to link to a heading in your own page, you can specifiy it like this:: + + `Heading text exactly as written`_ + +Note that the DFHack documentation is configured so that single backticks (with +no prefix or suffix) produce links to internal link targets, such as the +``autolabor`` target shown above. This is different from the reStructuredText +default behavior of rendering such text in italics (as a reference to a title). +For alternative link behaviors, see: + +- `The reStructuredText documentation on roles `__ +- `The reStructuredText documentation on external links `__ +- `The Sphinx documentation on roles `__ + - ``:doc:`` is useful for linking to another document outside of DFHack. + .. _docs-standards: Documentation standards @@ -38,106 +99,191 @@ there are a few important standards for completeness and consistent style. Trea this section as a guide rather than iron law, match the surrounding text, and you'll be fine. -Command documentation ---------------------- +Where do I add the help text? +----------------------------- -Each command should have a short (~54 character) help string, which is shown -by the `ls` command. For scripts, this is a comment on the first line -(the comment marker and whitespace is stripped). For plugins it's the second -argument to ``PluginCommand``. Please make this brief but descriptive! +For scripts and plugins that are distributed as part of DFHack, documentation files +should be added to the :source-scripts:`docs` and :source:`docs/plugins` directories, +respectively, in a file named after the script or plugin. For example, a script named +``gui/foobar.lua`` (which provides the ``gui/foobar`` command) should be documented +in a file named ``docs/gui/foobar.rst`` in the scripts repo. Similarly, a plugin named +``foobaz`` should be documented in a file named ``docs/plugins/foobaz.rst`` in the dfhack repo. +For plugins, all commands provided by that plugin should be documented in that same file. -Everything should be documented! If it's not clear *where* a particular -thing should be documented, ask on IRC or in the DFHack thread on Bay12 - -as well as getting help, you'll be providing valuable feedback that -makes it easier for future readers! +Short descriptions (the ~54 character short help) are taken from the first "sentence" of +the help text for scripts and plugins that can be enabled. This means that the help should +begin with a sentence fragment that begins with a capital letter and ends in a full stop +(``.``). Please make this brief but descriptive! -Scripts can use a custom autodoc function, based on the Sphinx ``include`` -directive - anything between the tokens is copied into the appropriate scripts -documentation page. For Ruby, we follow the built-in docstring convention -(``=begin`` and ``=end``). For Lua, the tokens are ``[====[`` and ``]====]`` -- ordinary multi-line strings. It is highly encouraged to reuse this string -as the in-console documentation by (e.g.) printing it when a ``-help`` argument -is given. +Short descriptions for commands provided by plugins are taken from the ``description`` +parameter passed to the ``PluginCommand`` constructor used when the command is registered +in the plugin source file. -The docs **must** have a heading which exactly matches the command, underlined -with ``=====`` to the same length. For example, a lua file would have: - -.. code-block:: lua +Header format +------------- - local helpstr = [====[ +The docs **must** begin with a heading which exactly matches the script or plugin name, underlined +with ``=====`` to the same length. This should be followed by a ``Tags:`` line with +comma-separated links to the tag indices, and then a ``:dfhack-keybind:`commandname``` line for +each command provided by the script or plugin. For scripts, this will just be the script name. +Plugins that do not provide commands (i.e. they can just be enabled for some persistent effect or +they just export functionality via a Lua API) don't need any ``:dfhack-keybind:`` lines at all. +The first line of the text should then be the short description that will be used for the script +or plugin. For example, documentation for the ``build-now`` script might look like: - add-thought - =========== - Adds a thought or emotion to the selected unit. Can be used by other scripts, - or the gui invoked by running ``add-thought gui`` with a unit selected. +.. code-block:: rst - ]====] + build-now + ========= + Tags: `tag/fort`, `tag/buildings` + :dfhack-keybind:`build-now` + Instantly completes unsuspended building jobs. By default, all constructions + and buildings on the map are completed, but the area of effect is configurable. -.. highlight:: rst +Usage help +---------- -Where the heading for a section is also the name of a command, the spelling -and case should exactly match the command to enter in the DFHack command line. +The first section after the header and introductory text should be the usage block. You can +choose between two formats, based on whatever is cleaner or clearer for your syntax. The first +option is to show usage formats together, with an explanation following the block:: -Try to keep lines within 80-100 characters, so it's readable in plain text -in the terminal - Sphinx (our documentation system) will make sure -paragraphs flow. + Usage:: -Command usage -------------- + build-now [] + build-now here [] + build-now [ []] [] -If there aren't many options or examples to show, they can go in a paragraph of -text. Use double-backticks to put commands in monospaced font, like this:: + Where the optional ```` pair can be used to specify the coordinate bounds + within which ``build-now`` will operate. If they are not specified, + ``build-now`` will scan the entire map. If only one ```` is specified, only + the building at that coordinate is built. - You can use ``cleanowned scattered x`` to dump tattered or abandoned items. + The ```` parameters can either be an ``,,`` triple (e.g. + ``35,12,150``) or the string ``here``, which means the position of the active + game cursor. -If the command takes more than three arguments, format the list as a table -called Usage. The table *only* lists arguments, not full commands. -Input values are specified in angle brackets. Example:: +The second option is to arrange the usage options in a list, with the full command +and arguments in monospaced font. Then indent the next line and describe the effect:: Usage: - :arg1: A simple argument. - :arg2 : Does something based on the input value. - :Very long argument: - Is very specific. + ``build-now []`` + Scan the entire map and build all unsuspended constructions and buildings. + ``build-now here []`` + Build the unsuspended construction or building under the cursor. + ``build-now [ []] []`` + Build all unsuspended constructions within the specified coordinate box. -To demonstrate usage - useful mainly when the syntax is complicated, list the -full command with arguments in monospaced font, then indent the next line and -describe the effect:: + The ```` parameters are specified as... - ``resume all`` - Resumes all suspended constructions. +Note that in both options, the entire commandline syntax is written, including the command itself. +Literal text is written as-is (e.g. the word ``here`` in the above example), and text that +describes the kind of parameter that is being passed (e.g. ``pos`` or ``options``) is enclosed in +angle brackets (``<`` and ``>``). Optional elements are enclosed in square brackets (``[`` and ``]``). -Links ------ +Examples +-------- -If it would be helpful to mention another DFHack command, don't just type the -name - add a hyperlink! Specify the link target in backticks, and it will be -replaced with the corresponding title and linked: e.g. ```autolabor``` -=> `autolabor`. Link targets should be equivalent to the command -described (without file extension), and placed above the heading of that -section like this:: +If the only way to run the command is to type the command itself, then this section is not necessary. +Otherwise, please consider adding a section that shows some real, practical usage examples. For +many users, this will be the **only** section they will read. It is so important that it is a good +idea to include the ``Examples`` section **before** you describe any extended options your command +might take. Write examples for what you expect the popular use cases will be. Also be sure to write +examples showing specific, practical values being used for any parameter that takes a value. - .. _autolabor: +Examples should go in their own subheading with a single dash underline (``--------``). The examples +themselves should be organized in a list, the same as in option 2 for Usage above. Here is an +example Examples section:: - autolabor - ========= + Examples + -------- -Add link targets if you need them, but otherwise plain headings are preferred. -Scripts have link targets created automatically. + ``build-now`` + Completes all unsuspended construction jobs on the map. + ``build-now 37,20,154 here`` + Builds the unsuspended, unconstructed buildings in the box bounded by the coordinate + x=37,y=20,z=154 and the cursor. -Note that the DFHack documentation is configured so that single backticks (with -no prefix or suffix) produce links to internal link targets, such as the -``autolabor`` target shown above. This is different from the reStructuredText -default behavior of rendering such text in italics (as a reference to a title). -For alternative link behaviors, see: +Options +------- -- `The reStructuredText documentation on roles `__ -- `The reStructuredText documentation on external links `__ -- `The Sphinx documentation on roles `__ +The options header should follow the examples, with each option in the same list format as the +examples:: + + Options + ------- + + ``-h``, ``--help`` + Show help text. + ``-l``, ``--quality `` + Set the quality of the architecture for built architected builtings. + ``-q``, ``--quiet`` + Suppress informational output (error messages are still printed). + +Note that for parameters that have both short and long forms, any values that those options +take only need to be specified once (e.g. ````). + +External scripts and plugins +============================ + +Scripts and plugins distributed separately from DFHack's release packages don't have the +opportunity to add their documentation to the rendered HTML or text output. However, these +scripts and plugins can use a different mechanism to at least make their help text available +in-game. + +Note that since help text for external scripts and plugins is not rendered by Sphinx, +it should be written in plain text. Any reStructuredText markup will not be processed. + +For external scripts, the short description comes from a comment on the first line +(the comment marker and extra whitespace is stripped). For Lua, this would look like:: + + -- A short description of my cool script. + +and for Ruby scripts it would look like:: + + # A short description of my cool script. + +The main help text for an external script needs to appear between two markers. For +Lua, these markers are ``[====[`` and ``]====]``, and for Ruby they are ``=begin`` and +``=end``. The documentation standards above still apply to external tools, but there is +no need to include backticks for links or monospaced fonts. Here is a Lua example for an +entire script header:: + + -- Inventory management for adventurers. + -- [====[ + gui/adv-inventory + ================= + Tags: adventure, map + + Allows you to quickly move items between containers. This includes yourself + and any followers you have. + + Usage: + + gui/adv-inventory [] + + Examples: + + gui/adv-inventory + Opens the GUI with nothing preselected + gui/adv-inventory take-all + Opens the GUI with all container items already selected and ready to move + into the adventurer's inventory. + + Options: + + take-all + Starts the GUI with container items pre-selected + give-all + Starts the GUI with your own items pre-selected + ]====] - - ``:doc:`` is useful for linking to another document +For external plugins, help text for provided commands can be passed as the ``usage`` +parameter when registering the commands with the ``PluginCommand`` constructor. There +is currently no way for associating help text with the plugin itself, so any +information about what the plugin does when enabled should be combined into the command +help. Required dependencies ===================== @@ -258,8 +404,8 @@ ways to do this: * On Windows, if you prefer to use the batch scripts, you can run ``generate-msvc-gui.bat`` and set ``BUILD_DOCS`` through the GUI. If you are running another file, such as ``generate-msvc-all.bat``, you will need to edit - it to add the flag. You can also run ``cmake`` on the command line, similar to - other platforms. + the batch script to add the flag. You can also run ``cmake`` on the command line, + similar to other platforms. By default, both HTML and text docs are built by CMake. The generated documentation is stored in ``docs/html`` and ``docs/text`` (respectively) in the From c1a694cb1812eaf30ed3e0453d7bc487f1dd8f1a Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 23 Jul 2022 18:26:40 -0400 Subject: [PATCH 101/334] Improve syntax highlighting in Documentation.rst --- docs/Documentation.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 6be4de37f..50a086cdd 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -94,6 +94,8 @@ For alternative link behaviors, see: Documentation standards ======================= +.. highlight:: rst + Whether you're adding new code or just fixing old documentation (and there's plenty), there are a few important standards for completeness and consistent style. Treat this section as a guide rather than iron law, match the surrounding text, and you'll @@ -129,9 +131,7 @@ each command provided by the script or plugin. For scripts, this will just be th Plugins that do not provide commands (i.e. they can just be enabled for some persistent effect or they just export functionality via a Lua API) don't need any ``:dfhack-keybind:`` lines at all. The first line of the text should then be the short description that will be used for the script -or plugin. For example, documentation for the ``build-now`` script might look like: - -.. code-block:: rst +or plugin. For example, documentation for the ``build-now`` script might look like:: build-now ========= @@ -236,11 +236,15 @@ Note that since help text for external scripts and plugins is not rendered by Sp it should be written in plain text. Any reStructuredText markup will not be processed. For external scripts, the short description comes from a comment on the first line -(the comment marker and extra whitespace is stripped). For Lua, this would look like:: +(the comment marker and extra whitespace is stripped). For Lua, this would look like: + +.. code-block:: lua -- A short description of my cool script. -and for Ruby scripts it would look like:: +and for Ruby scripts it would look like: + +.. code-block:: ruby # A short description of my cool script. From a94f3c3cca434158ffb1cc1b3e09b53d39afa340 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 15:31:56 -0700 Subject: [PATCH 102/334] fix some formatting --- docs/Documentation.rst | 69 ++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 50a086cdd..8f135e2a3 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -5,14 +5,14 @@ DFHack Documentation System ########################### -DFHack documentation, like the file you are reading now, is created as ``.rst`` files, -which are in `reStructuredText (reST) `_ format. -This is a documentation format common in the Python community. It is very +DFHack documentation, like the file you are reading now, is created as a set of +``.rst`` files in `reStructuredText (reST) `_ +format. This is a documentation format common in the Python community. It is very similar in concept - and in syntax - to Markdown, as found on GitHub and many other places. However it is more advanced than Markdown, with more features available when compiled to HTML, such as automatic tables of contents, cross-linking, special external links (forum, wiki, etc) and more. The documentation is compiled by a -Python tool, `Sphinx `_. +Python tool named `Sphinx `_. The DFHack build process will compile and install the documentation so it can be displayed in-game by the `help` and `ls` commands (and any other command or GUI that @@ -22,7 +22,7 @@ downloaded release binary), then you only need to build the docs if you're chang and want to see the changes reflected in your game. You can also build the docs if you just want a local HTML- or text-rendered copy, though -you can always read the `online version `_. +you can always read the `online version `_ too. (Note that even if you do want a local copy, it is certainly not necessary to compile the documentation in order to read it. Like Markdown, reST documents are @@ -37,11 +37,13 @@ Concepts and general guidance The source ``.rst`` files are compiled to HTML for viewing in a browser and to text format for viewing in-game. For in-game help, the help text is read from its installed -location in ``hack/docs`` under the DF directory for in-game display. +location in ``hack/docs`` under the DF directory. -Remember, everything should be documented! If it's not clear *where* a particular thing -should be documented, ask on Discord or in the DFHack thread on Bay12 -- as well as -getting help, you'll be providing valuable feedback that makes it easier for future readers! +When writing documentation, remember that everything should be documented! If it's not +clear *where* a particular thing should be documented, ask on Discord or in the DFHack +thread on Bay12 -- you'll not only be getting help, you'll also be providing valuable +feedback that makes it easier for future contributers to find documentation on how to +write the documentation! Try to keep lines within 80-100 characters, so it's readable in plain text in the terminal - Sphinx (our documentation system) will make sure @@ -105,7 +107,7 @@ Where do I add the help text? ----------------------------- For scripts and plugins that are distributed as part of DFHack, documentation files -should be added to the :source-scripts:`docs` and :source:`docs/plugins` directories, +should be added to the :source-scripts:`scripts/docs ` and :source:`docs/plugins` directories, respectively, in a file named after the script or plugin. For example, a script named ``gui/foobar.lua`` (which provides the ``gui/foobar`` command) should be documented in a file named ``docs/gui/foobar.rst`` in the scripts repo. Similarly, a plugin named @@ -138,8 +140,9 @@ or plugin. For example, documentation for the ``build-now`` script might look li Tags: `tag/fort`, `tag/buildings` :dfhack-keybind:`build-now` - Instantly completes unsuspended building jobs. By default, all constructions - and buildings on the map are completed, but the area of effect is configurable. + Instantly completes unsuspended building jobs. By default, all + constructions and buildings on the map are completed, but the area + of effect is configurable. Usage help ---------- @@ -154,14 +157,15 @@ option is to show usage formats together, with an explanation following the bloc build-now here [] build-now [ []] [] - Where the optional ```` pair can be used to specify the coordinate bounds - within which ``build-now`` will operate. If they are not specified, - ``build-now`` will scan the entire map. If only one ```` is specified, only - the building at that coordinate is built. + Where the optional ```` pair can be used to specify the + coordinate bounds within which ``build-now`` will operate. If they + are not specified, ``build-now`` will scan the entire map. If only + one ```` is specified, only the building at that coordinate + is built. - The ```` parameters can either be an ``,,`` triple (e.g. - ``35,12,150``) or the string ``here``, which means the position of the active - game cursor. + The ```` parameters can either be an ``,,`` triple + (e.g. ``35,12,150``) or the string ``here``, which means the + position of the active game cursor. The second option is to arrange the usage options in a list, with the full command and arguments in monospaced font. Then indent the next line and describe the effect:: @@ -169,11 +173,14 @@ and arguments in monospaced font. Then indent the next line and describe the eff Usage: ``build-now []`` - Scan the entire map and build all unsuspended constructions and buildings. + Scan the entire map and build all unsuspended constructions + and buildings. ``build-now here []`` - Build the unsuspended construction or building under the cursor. + Build the unsuspended construction or building under the + cursor. ``build-now [ []] []`` - Build all unsuspended constructions within the specified coordinate box. + Build all unsuspended constructions within the specified + coordinate box. The ```` parameters are specified as... @@ -202,8 +209,8 @@ example Examples section:: ``build-now`` Completes all unsuspended construction jobs on the map. ``build-now 37,20,154 here`` - Builds the unsuspended, unconstructed buildings in the box bounded by the coordinate - x=37,y=20,z=154 and the cursor. + Builds the unsuspended, unconstructed buildings in the box + bounded by the coordinate x=37,y=20,z=154 and the cursor. Options ------- @@ -217,9 +224,11 @@ examples:: ``-h``, ``--help`` Show help text. ``-l``, ``--quality `` - Set the quality of the architecture for built architected builtings. + Set the quality of the architecture for built architected + builtings. ``-q``, ``--quiet`` - Suppress informational output (error messages are still printed). + Suppress informational output (error messages are still + printed). Note that for parameters that have both short and long forms, any values that those options take only need to be specified once (e.g. ````). @@ -260,8 +269,8 @@ entire script header:: ================= Tags: adventure, map - Allows you to quickly move items between containers. This includes yourself - and any followers you have. + Allows you to quickly move items between containers. This includes + yourself and any followers you have. Usage: @@ -272,8 +281,8 @@ entire script header:: gui/adv-inventory Opens the GUI with nothing preselected gui/adv-inventory take-all - Opens the GUI with all container items already selected and ready to move - into the adventurer's inventory. + Opens the GUI with all container items already selected and + ready to move into the adventurer's inventory. Options: From e4273589e1df7710f866dddfd0b80fce665db63e Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 15:38:35 -0700 Subject: [PATCH 103/334] one fewer char per line so no horiz scrolling --- docs/Documentation.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 8f135e2a3..170a6fd4e 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -141,8 +141,8 @@ or plugin. For example, documentation for the ``build-now`` script might look li :dfhack-keybind:`build-now` Instantly completes unsuspended building jobs. By default, all - constructions and buildings on the map are completed, but the area - of effect is configurable. + constructions and buildings on the map are completed, but the + area of effect is configurable. Usage help ---------- @@ -158,10 +158,10 @@ option is to show usage formats together, with an explanation following the bloc build-now [ []] [] Where the optional ```` pair can be used to specify the - coordinate bounds within which ``build-now`` will operate. If they - are not specified, ``build-now`` will scan the entire map. If only - one ```` is specified, only the building at that coordinate - is built. + coordinate bounds within which ``build-now`` will operate. If + they are not specified, ``build-now`` will scan the entire map. + If only one ```` is specified, only the building at that + coordinate is built. The ```` parameters can either be an ``,,`` triple (e.g. ``35,12,150``) or the string ``here``, which means the @@ -269,8 +269,8 @@ entire script header:: ================= Tags: adventure, map - Allows you to quickly move items between containers. This includes - yourself and any followers you have. + Allows you to quickly move items between containers. This + includes yourself and any followers you have. Usage: From 947889873d379f49e138a1740c3bda32eaeb22f4 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 16:03:40 -0700 Subject: [PATCH 104/334] update all docs I've done so far to new standards --- docs/Documentation.rst | 2 +- docs/builtins/alias.rst | 9 +++--- docs/builtins/cls.rst | 1 - docs/builtins/devel/dump-rpc.rst | 1 - docs/builtins/die.rst | 1 - docs/builtins/disable.rst | 1 - docs/builtins/enable.rst | 5 ++- docs/builtins/fpause.rst | 1 - docs/builtins/help.rst | 1 - docs/builtins/hide.rst | 1 - docs/builtins/keybinding.rst | 15 +++++---- docs/builtins/kill-lua.rst | 1 - docs/builtins/load.rst | 1 - docs/builtins/ls.rst | 22 ++++++------- docs/builtins/plug.rst | 5 ++- docs/builtins/reload.rst | 1 - docs/builtins/sc-script.rst | 13 ++++---- docs/builtins/script.rst | 7 ++--- docs/builtins/show.rst | 1 - docs/builtins/tags.rst | 1 - docs/builtins/type.rst | 1 - docs/builtins/unload.rst | 1 - docs/plugins/3dveins.rst | 1 - docs/plugins/add-spatter.rst | 1 - docs/plugins/autochop.rst | 1 - docs/plugins/autoclothing.rst | 5 ++- docs/plugins/autodump.rst | 17 +++++----- docs/plugins/autofarm.rst | 15 +++++---- docs/plugins/autogems.rst | 5 ++- docs/plugins/autohauler.rst | 23 +++++++------- docs/plugins/autolabor.rst | 41 ++++++++++++------------ docs/plugins/automaterial.rst | 1 - docs/plugins/automelt.rst | 1 - docs/plugins/autotrade.rst | 1 - docs/plugins/blueprint.rst | 53 ++++++++++++++++---------------- docs/plugins/building-hacks.rst | 1 - docs/plugins/buildingplan.rst | 9 +++--- docs/plugins/burrows.rst | 23 +++++++------- docs/plugins/changeitem.rst | 33 ++++++++++---------- docs/plugins/changelayer.rst | 45 +++++++++++++-------------- docs/plugins/changevein.rst | 5 ++- docs/plugins/cleanconst.rst | 1 - docs/plugins/cleaners.rst | 23 +++++++------- docs/plugins/cleanowned.rst | 9 +++--- docs/plugins/command-prompt.rst | 1 - docs/plugins/confirm.rst | 5 ++- docs/plugins/createitem.rst | 16 +++++----- docs/plugins/cursecheck.rst | 37 +++++++++++----------- 48 files changed, 211 insertions(+), 254 deletions(-) diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 170a6fd4e..8908272ca 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -267,7 +267,7 @@ entire script header:: -- [====[ gui/adv-inventory ================= - Tags: adventure, map + Tags: adventure, items Allows you to quickly move items between containers. This includes yourself and any followers you have. diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 234f8ae4a..96f088798 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,6 +1,5 @@ alias ===== - Tags: `tag/system` :dfhack-keybind:`alias` @@ -12,14 +11,14 @@ or script. Usage: -- ``alias list`` +``alias list`` Lists all configured aliases -- ``alias add [arguments...]`` +``alias add [arguments...]`` Adds an alias -- ``alias replace [arguments...]`` +``alias replace [arguments...]`` Replaces an existing alias with a new command, or adds the alias if it does not already exist -- ``alias delete `` +``alias delete `` Removes the specified alias Aliases can be given additional arguments when created and invoked, which will diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index 2f5387c7f..a0478c8df 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,6 +1,5 @@ cls === - Tags: `tag/system` :dfhack-keybind:`cls` diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index 2f798c79e..65d4dae57 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -1,6 +1,5 @@ devel/dump-rpc ============== - Tags: `tag/system` :dfhack-keybind:`devel/dump-rpc` diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index df53c94bd..54a134433 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,6 +1,5 @@ die === - Tags: `tag/system` :dfhack-keybind:`die` diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index d17706486..567e9dbb1 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,6 +1,5 @@ disable ======= - Tags: `tag/system` :dfhack-keybind:`disable` diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 1a714f602..b99dd194a 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,6 +1,5 @@ enable ====== - Tags: `tag/system` :dfhack-keybind:`enable` @@ -25,7 +24,7 @@ Usage:: Examples -------- -- ``enable manipulator`` +``enable manipulator`` Enable the ``manipulator`` plugin. -- ``enable manipulator search`` +``enable manipulator search`` Enable multiple plugins at once. diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index f40f6d55c..c43cedfab 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,6 +1,5 @@ fpause ====== - Tags: `tag/system` :dfhack-keybind:`fpause` diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 290d62e4c..957a84466 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,6 +1,5 @@ help ==== - Tags: `tag/system` :dfhack-keybind:`help` diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index e4f91abaa..de144b637 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,6 +1,5 @@ hide ==== - Tags: `tag/system` :dfhack-keybind:`hide` diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index 2ad9e1542..ad759b14c 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,6 +1,5 @@ keybinding ========== - Tags: `tag/system` :dfhack-keybind:`keybinding` @@ -14,15 +13,15 @@ Hotkeys can be any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or Usage: -- ``keybinding`` +``keybinding`` Show some useful information, including the current game context. -- ``keybinding list `` +``keybinding list `` List bindings active for the key combination. -- ``keybinding clear [...]`` +``keybinding clear [...]`` Remove bindings for the specified keys. -- ``keybinding add "cmdline" ["cmdline"...]`` +``keybinding add "cmdline" ["cmdline"...]`` Add bindings for the specified key. -- ``keybinding set "cmdline" ["cmdline"...]`` +``keybinding set "cmdline" ["cmdline"...]`` Clear, and then add bindings for the specified key. The ```` parameter above has the following **case-sensitive** syntax:: @@ -53,8 +52,8 @@ Interactive commands like `liquids` cannot be used as hotkeys. Examples -------- -- ``keybinding add Alt-F1 hotkeys`` +``keybinding add Alt-F1 hotkeys`` Bind Alt-F1 to run the `hotkeys` command on any screen at any time. -- ``keybinding add Alt-F@dwarfmode gui/quickfort`` +``keybinding add Alt-F@dwarfmode gui/quickfort`` Bind Alt-F to run `gui/quickfort`, but only when on a screen that shows the main map. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 4b93b8734..222795db3 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,6 +1,5 @@ kill-lua ======== - Tags: `tag/system` :dfhack-keybind:`kill-lua` diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index 8e51847dd..b7e3db8f3 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,6 +1,5 @@ load ==== - Tags: `tag/system` :dfhack-keybind:`load` diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index e68b740c7..3ae3fb3e1 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,6 +1,5 @@ ls == - Tags: `tag/system` :dfhack-keybind:`ls` @@ -11,23 +10,16 @@ command name. Can also be invoked as ``dir``. Usage: -- ``ls []`` +``ls []`` Lists all available commands and the tags associated with them. -- ``ls []`` +``ls []`` Shows only commands that have the given tag. Use the `tags` command to see the list of available tags. -- ``ls []`` +``ls []`` Shows commands that include the given string. E.g. ``ls quick`` will show all the commands with "quick" in their names. If the string is also the name of a tag, then it will be interpreted as a tag name. -You can also pass some optional parameters to change how ``ls`` behaves: - -- ``--notags`` - Don't print out the tags associated with each command. -- ``--dev`` - Include commands intended for developers and modders. - Examples -------- @@ -36,3 +28,11 @@ Examples - ``ls --dev trigger`` Lists all commands, including developer and modding commands, that match the substring "trigger" + +Options +------- + +``--notags`` + Don't print out the tags associated with each command. +``--dev`` + Include commands intended for developers and modders. diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 47efe66b3..26699d400 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,6 +1,5 @@ plug ==== - Tags: `tag/system` :dfhack-keybind:`plug` @@ -9,8 +8,8 @@ Tags: `tag/system` Usage: -- ``plug`` +``plug`` Lists available plugins and whether they are enabled. -- ``plug [ ...]`` +``plug [ ...]`` Shows the commands implemented by the named plugins and whether the plugins are enabled. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index cde113ac8..4417b5185 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,6 +1,5 @@ reload ====== - Tags: `tag/system` :dfhack-keybind:`reload` diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index bcedb610e..e19af0bf9 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,6 +1,5 @@ sc-script ========= - Tags: `tag/system` :dfhack-keybind:`sc-script` @@ -11,16 +10,16 @@ dynamically. Usage: -- ``sc-script [help]`` +``sc-script [help]`` Show the list of valid event names. -- ``sc-script list []`` +``sc-script list []`` List the currently registered files for all events or the specified event. -- ``sc-script add|remove [ ...]`` +``sc-script add|remove [ ...]`` Register or unregister a file to be run for the specified event. -Examples --------- +Example +------- -- ``sc-script add SC_MAP_LOADED spawn_extra_monsters.init`` +``sc-script add SC_MAP_LOADED spawn_extra_monsters.init`` Registers the ``spawn_extra_monsters.init`` file to be run whenever a new map is loaded. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index ef1aab64a..f15e86bec 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,6 +1,5 @@ script ====== - Tags: `tag/system` :dfhack-keybind:`script` @@ -16,9 +15,9 @@ Usage:: script -Examples --------- +Example +------- -- ``script startup.txt`` +``script startup.txt`` Executes the commands in ``startup.txt``, which exists in your DF game directory. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index b22d4bab9..a173231d7 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,6 +1,5 @@ show ==== - Tags: `tag/system` :dfhack-keybind:`show` diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index b601a73a1..33ac36fe2 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,6 +1,5 @@ tags ==== - Tags: `tag/system` :dfhack-keybind:`tags` diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 755548da9..7c446bc4e 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,6 +1,5 @@ type ==== - Tags: `tag/system` :dfhack-keybind:`type` diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index 1fc8c311e..ec385489f 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,6 +1,5 @@ unload ====== - Tags: `tag/system` :dfhack-keybind:`unload` diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 3c1276b46..9fe83a4b5 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,6 +1,5 @@ 3dveins ======= - Tags: :dfhack-keybind:`3dveins` diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 38a9f91f0..6e5a8ddb7 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -1,6 +1,5 @@ add-spatter =========== - Tags: :index:`Make tagged reactions produce contaminants. diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 3a2284aef..6893644db 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -1,6 +1,5 @@ autochop ======== - Tags: :index:`Auto-harvest trees when low on stockpiled logs. diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index df937c338..bbfe14cc9 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -1,6 +1,5 @@ autoclothing ============ - Tags: :dfhack-keybind:`autoclothing` @@ -22,7 +21,7 @@ material and item. Examples -------- -* ``autoclothing cloth "short skirt" 10``: +``autoclothing cloth "short skirt" 10`` Sets the desired number of cloth short skirts available per citizen to 10. -* ``autoclothing cloth dress``: +``autoclothing cloth dress`` Displays the currently set number of cloth dresses chosen per citizen. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index a47878c34..b42ed9d85 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -1,6 +1,5 @@ autodump ======== - Tags: :dfhack-keybind:`autodump` :dfhack-keybind:`autodump-destroy-here` @@ -37,28 +36,28 @@ before the game is resumed, cancels destruction of the item. Options ------- -- ``destroy`` +``destroy`` Destroy instead of dumping. Doesn't require a cursor. If ``autodump`` is called again with this option before the game is resumed, it cancels pending destroy actions. -- ``destroy-here`` +``destroy-here`` :index:`Destroy items marked for dumping under the cursor. ` -- ``visible`` +``visible`` Only process items that are not hidden. -- ``hidden`` +``hidden`` Only process hidden items. -- ``forbidden`` +``forbidden`` Only process forbidden items (default: only unforbidden). Examples -------- -- ``autodump`` +``autodump`` :index:`Teleports items marked for dumping to the cursor position. ` -- ``autodump destroy`` +``autodump destroy`` Destroys all unforbidden items marked for dumping -- ``autodump-destroy-item`` +``autodump-destroy-item`` :index:`Destroys the selected item. ` diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index 0294b574b..9a2169d65 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -1,6 +1,5 @@ autofarm ======== - Tags: :dfhack-keybind:`autofarm` @@ -12,15 +11,15 @@ threshold for each crop type is configurable. Usage: -- ``enable autofarm`` +``enable autofarm`` Enable the plugin and start managing crop assignment. -- ``autofarm runonce`` +``autofarm runonce`` Updates all farm plots once, without enabling the plugin. -- ``autofarm status`` +``autofarm status`` Prints status information, including any defined thresholds. -- ``autofarm default `` +``autofarm default `` Sets the default threshold. -- ``autofarm threshold [ ...]`` +``autofarm threshold [ ...]`` Sets thresholds of individual plant types. You can find the identifiers for the crop types in your world by running the @@ -31,7 +30,7 @@ following command:: Examples -------- -- ``autofarm default 30`` +``autofarm default 30`` Set the default threshold to 30. -- ``autofarm threshold 150 MUSHROOM_HELMET_PLUMP GRASS_TAIL_PIG`` +``autofarm threshold 150 MUSHROOM_HELMET_PLUMP GRASS_TAIL_PIG`` Set the threshold for Plump Helmets and Pig Tails to 150 diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index bdfd51864..72b850754 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,6 +1,5 @@ autogems ======== - Tags: :dfhack-keybind:`autogems-reload` @@ -10,9 +9,9 @@ orders for cutting them at a Jeweler's Workshop. Usage: -- ``enable autogems`` +``enable autogems`` Enables the plugin -- ``autogems-reload`` +``autogems-reload`` :index:`Reloads the autogems configuration file. ` You might need to do this if you have manually modified the contents while the game is diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 1dce8a692..82561fa2a 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -1,6 +1,5 @@ autohauler ========== - Tags: :dfhack-keybind:`autohauler` @@ -24,33 +23,33 @@ can be changed by the user. Usage: -- ``enable autohauler`` +``enable autohauler`` Start managing hauling labors. This is normally all you need to do. Autohauler works well on default settings. -- ``autohauler status`` +``autohauler status`` Show autohauler status and status of fort dwarves. -- ``autohauler haulers`` +``autohauler haulers`` Set whether a particular labor should be assigned to haulers. -- ``autohauler allow|forbid`` +``autohauler allow|forbid`` Set whether a particular labor should mark a dwarf as exempt from hauling. By default, only the ``ALCHEMIST`` labor is set to ``forbid``. -- ``autohauler reset-all| reset`` +``autohauler reset-all| reset`` Reset a particular labor (or all labors) to their default haulers/allow/forbid state. -- ``autohauler list`` +``autohauler list`` Show the active configuration for all labors. -- ``autohauler frameskip `` +``autohauler frameskip `` Set the number of frames between runs of autohauler. -- ``autohauler debug`` +``autohauler debug`` In the next cycle, output the state of every dwarf. Examples -------- -- ``autohauler HAUL_STONE haulers`` +``autohauler HAUL_STONE haulers`` Set stone hauling as a hauling labor (this is already the default). -- ``autohauler RECOVER_WOUNDED allow`` +``autohauler RECOVER_WOUNDED allow`` Allow the "Recover wounded" labor to be manually assigned by the player. By default it is automatically given to haulers. -- ``autohauler MINE forbid`` +``autohauler MINE forbid`` Don't assign hauling labors to dwarves with the Mining labor enabled. diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 7696efe71..ca249ee19 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -1,6 +1,5 @@ autolabor ========= - Tags: :dfhack-keybind:`autolabor` @@ -54,36 +53,36 @@ dwarfs that meet any of these conditions: We stop assigning dwarves when we reach the maximum allowed. +Examples +-------- + +``autolabor MINE 5`` + Keep at least 5 dwarves with mining enabled. +``autolabor CUT_GEM 1 1`` + Keep exactly 1 dwarf with gemcutting enabled. +``autolabor COOK 1 1 3`` + Keep 1 dwarf with cooking enabled, selected only from the top 3. +``autolabor FEED_WATER_CIVILIANS haulers`` + Have haulers feed and water wounded dwarves. +``autolabor CUTWOOD disable`` + Turn off autolabor for wood cutting. + Advanced usage -------------- -- ``autolabor [] []`` +``autolabor [] []`` Set range of dwarves assigned to a labor, optionally specifying the size of the pool of most skilled dwarves that will ever be considered for this labor. -- ``autolabor haulers`` +``autolabor haulers`` Set a labor to be handled by hauler dwarves. -- ``autolabor disable`` +``autolabor disable`` Turn off autolabor for a specific labor. -- ``autolabor reset-all| reset`` +``autolabor reset-all| reset`` Return a labor (or all labors) to the default handling. -- ``autolabor list`` +``autolabor list`` List current status of all labors. -- ``autolabor status`` +``autolabor status`` Show basic status information. See `autolabor-artisans` for a differently-tuned setup. - -Examples --------- - -- ``autolabor MINE 5`` - Keep at least 5 dwarves with mining enabled. -- ``autolabor CUT_GEM 1 1`` - Keep exactly 1 dwarf with gemcutting enabled. -- ``autolabor COOK 1 1 3`` - Keep 1 dwarf with cooking enabled, selected only from the top 3. -- ``autolabor FEED_WATER_CIVILIANS haulers`` - Have haulers feed and water wounded dwarves. -- ``autolabor CUTWOOD disable`` - Turn off autolabor for wood cutting. diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 7f8355b49..3b9a68b7a 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -1,6 +1,5 @@ automaterial ============ - Tags: :index:`Sorts building materials by recent usage. diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index b51a431c0..8fe995d23 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -1,6 +1,5 @@ automelt ======== - Tags: :index:`Quickly designate items to be melted. diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index ed2d65bc5..68aa2d50d 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -1,6 +1,5 @@ autotrade ========= - Tags: :index:`Quickly designate items to be traded. diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 3825de7b2..6a0adfe58 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -1,6 +1,5 @@ blueprint ========= - Tags: :dfhack-keybind:`blueprint` @@ -23,14 +22,14 @@ Usage:: Examples -------- -- ``blueprint gui`` +``blueprint gui`` Runs `gui/blueprint`, the interactive frontend, where all configuration for a ``blueprint`` command can be set visually and interactively. -- ``blueprint 30 40 bedrooms`` +``blueprint 30 40 bedrooms`` Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting from the active cursor on the current z-level. Blueprints are written to ``bedrooms.csv`` in the ``blueprints`` directory. -- ``blueprint 30 40 bedrooms dig --cursor 108,100,150`` +``blueprint 30 40 bedrooms dig --cursor 108,100,150`` Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and the start of the blueprint area is set to a specific value instead of using the in-game cursor position. @@ -38,15 +37,15 @@ Examples Positional parameters --------------------- -- ``width`` +``width`` Width of the area (in tiles) to translate. -- ``height`` +``height`` Height of the area (in tiles) to translate. -- ``depth`` +``depth`` Number of z-levels to translate. Positive numbers go *up* from the cursor and negative numbers go *down*. Defaults to 1 if not specified, indicating that the blueprint should only include the current z-level. -- ``name`` +``name`` Base name for blueprint files created in the ``blueprints`` directory. If no name is specified, "blueprint" is used by default. The string must contain some characters other than numbers so the name won't be confused with the @@ -59,17 +58,17 @@ If you want to generate blueprints only for specific phases, add their names to the commandline, anywhere after the blueprint base name. You can list multiple phases; just separate them with a space. -- ``dig`` +``dig`` Generate quickfort ``#dig`` blueprints for digging natural stone. -- ``carve`` +``carve`` Generate quickfort ``#dig`` blueprints for smoothing and carving. -- ``build`` +``build`` Generate quickfort ``#build`` blueprints for constructions and buildings. -- ``place`` +``place`` Generate quickfort ``#place`` blueprints for placing stockpiles. -- ``zone`` +``zone`` Generate quickfort ``#zone`` blueprints for designating zones. -- ``query`` +``query`` Generate quickfort ``#query`` blueprints for configuring rooms. If no phases are specified, phases are autodetected. For example, a ``#place`` @@ -78,20 +77,20 @@ blueprint will be created only if there are stockpiles in the blueprint area. Options ------- -- ``-c``, ``--cursor ,,`` +``-c``, ``--cursor ,,`` Use the specified map coordinates instead of the current cursor position for the upper left corner of the blueprint range. If this option is specified, then an active game map cursor is not necessary. -- ``-e``, ``--engrave`` +``-e``, ``--engrave`` Record engravings in the ``carve`` phase. If this option is not specified, engravings are ignored. -- ``-f``, ``--format `` - Select the output format of the generated files. See the ``Output formats`` +``-f``, ``--format `` + Select the output format of the generated files. See the `Output formats`_ section below for options. If not specified, the output format defaults to "minimal", which will produce a small, fast ``.csv`` file. -- ``-h``, ``--help`` +``-h``, ``--help`` Show command help text. -- ``-s``, ``--playback-start ,,`` +``-s``, ``--playback-start ,,`` Specify the column and row offsets (relative to the upper-left corner of the blueprint, which is ``1,1``) where the player should put the cursor when the blueprint is played back with `quickfort`, in @@ -100,9 +99,9 @@ Options to surround the parameter string in double quotes: ``"-s10,10,central stairs"`` or ``--playback-start "10,10,central stairs"`` or ``"--playback-start=10,10,central stairs"``. -- ``-t``, ``--splitby `` - Split blueprints into multiple files. See the ``Splitting output into - multiple files`` section below for details. If not specified, defaults to +``-t``, ``--splitby `` + Split blueprints into multiple files. See the `Splitting output into + multiple files`_ section below for details. If not specified, defaults to "none", which will create a standard quickfort `multi-blueprint ` file. @@ -111,10 +110,10 @@ Output formats Here are the values that can be passed to the ``--format`` flag: -- ``minimal`` +``minimal`` Creates ``.csv`` files with minimal file size that are fast to read and write. This is the default. -- ``pretty`` +``pretty`` Makes the blueprints in the ``.csv`` files easier to read and edit with a text editor by adding extra spacing and alignment markers. @@ -123,8 +122,8 @@ Splitting output into multiple files The ``--splitby`` flag can take any of the following values: -- ``none`` +``none`` Writes all blueprints into a single file. This is the standard format for quickfort fortress blueprint bundles and is the default. -- ``phase`` +``phase`` Creates a separate file for each phase. diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index abd6ac4b4..bdae83392 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -1,6 +1,5 @@ building-hacks ============== - Tags: :index:`Allows mods to create and manage powered workshops. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index ac73acd49..6aba86209 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -1,6 +1,5 @@ buildingplan ============ - Tags: :dfhack-keybind:`buildingplan` @@ -36,12 +35,12 @@ The buildingplan plugin has global settings that can be set from the UI :kbd:`b`:kbd:`a`:kbd:`G`). These settings can also be set via the ``buildingplan set`` command. The available settings are: -- ``all_enabled`` (default: false) +``all_enabled`` (default: false) Enable planning mode for all building types. -- ``blocks``, ``boulders``, ``logs``, ``bars`` (defaults: true, true, true, false) +``blocks``, ``boulders``, ``logs``, ``bars`` (defaults: true, true, true, false) Allow blocks, boulders, logs, or bars to be matched for generic "building material" items. -- ``quickfort_mode`` (default: false) +``quickfort_mode`` (default: false) Enable compatibility mode for the legacy Python Quickfort (this setting is not required for DFHack `quickfort`) @@ -51,7 +50,7 @@ save. If you normally embark with some blocks on hand for early workshops, you might want to add this line to your ``dfhack-config/init/onMapLoad.init`` file to -always configure buildingplan to just use blocks for buildlings and +always configure buildingplan to just use blocks for buildings and constructions:: on-new-fortress buildingplan set boulders false; buildingplan set logs false diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 6ab26caa2..733f784d0 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -1,6 +1,5 @@ burrows ======= - Tags: :dfhack-keybind:`burrow` @@ -16,33 +15,33 @@ You can also use the ``burrow`` command to Usage: -- ``enable burrows`` +``enable burrows`` Enable the plugin for the auto-grow feature (see ``burrow enable auto-grow`` below) -- ``burrow enable auto-grow`` +``burrow enable auto-grow`` When a wall inside a burrow with a name ending in '+' is dug out, the burrow will be extended to newly-revealed adjacent walls. This final '+' may be omitted in burrow name args of other ``burrows`` commands. Note that digging 1-wide corridors with the miner inside the burrow is SLOW. Be sure to also run ``enable burrows`` for this feature to work. -- ``burrow disable auto-grow`` +``burrow disable auto-grow`` Disables auto-grow processing. -- ``burrow clear-unit [ ...]`` +``burrow clear-unit [ ...]`` Remove all units from the named burrows. -- ``burrow clear-tiles [ ...]`` +``burrow clear-tiles [ ...]`` Remove all tiles from the named burrows. -- ``burrow set-units target-burrow [ ...]`` +``burrow set-units target-burrow [ ...]`` Clear all units from the target burrow, then add units from the named source burrows. -- ``burrow add-units target-burrow [ ...]`` +``burrow add-units target-burrow [ ...]`` Add units from the source burrows to the target. -- ``burrow remove-units target-burrow [ ...]`` +``burrow remove-units target-burrow [ ...]`` Remove units in source burrows from the target. -- ``burrow set-tiles target-burrow [ ...]`` +``burrow set-tiles target-burrow [ ...]`` Clear target burrow tiles and adds tiles from the names source burrows. -- ``burrow add-tiles target-burrow [ ...]`` +``burrow add-tiles target-burrow [ ...]`` Add tiles from the source burrows to the target. -- ``burrow remove-tiles target-burrow [ ...]`` +``burrow remove-tiles target-burrow [ ...]`` Remove tiles in source burrows from the target. In place of a source burrow, you can use one of the following keywords: diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index 5dd537a60..93a10cbfa 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -1,8 +1,7 @@ changeitem ========== - Tags: -:dfhack-keybind: +:dfhack-keybind:`changeitem` :index:`Change item material and base quality. ` By default, a change is @@ -14,31 +13,31 @@ refuse to touch. Usage: -- ``changeitem info`` +``changeitem info`` Show details about the selected item. Does not change the item. You can use this command to discover RAW ids for existing items. -- ``changeitem []`` +``changeitem []`` Change the item selected in the ``k`` list or inside a container/inventory. -- ``changeitem here []`` +``changeitem here []`` Change all items at the cursor position. Requires in-game cursor. +Examples +-------- + +``changeitem here m INORGANIC:GRANITE`` + Change material of all stone items under the cursor to granite. +``changeitem q 5`` + Change currently selected item to masterpiece quality. + Options ------- -- ``m``, ``material `` +``m``, ``material `` Change material. Must be followed by valid material RAW id. -- ``s``, ``subtype `` +``s``, ``subtype `` Change subtype. Must be followed by a valid subtype RAW id." -- ``q``, ``quality `` +``q``, ``quality `` Change base quality. Must be followed by number (0-5) with 0 being no quality and 5 being masterpiece quality. -- ``force`` +``force`` Ignore subtypes and force the change to the new material. - -Examples --------- - -``changeitem here m INORGANIC:GRANITE`` - Change material of all stone items under the cursor to granite. -``changeitem q 5`` - Change currently selected item to masterpiece quality. diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 76d631cdf..5298cf955 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -1,8 +1,7 @@ changelayer =========== - Tags: -:dfhack-keybind: +:dfhack-keybind:`changelayer` :index:`Change the material of an entire geology layer. ` Note that one @@ -26,27 +25,6 @@ When run without options, ``changelayer`` will: You can use the `probe` command on various tiles around your map to find valid material RAW ids and to get an idea how layers and biomes are distributed. -Options -------- - -- ``all_biomes`` - Change the corresponding geology layer for all biomes on your map. Be aware - that the same geology layer can AND WILL be on different z-levels for - different biomes. -- ``all_layers`` - Change all geology layers on your map (only for the selected biome unless - ``all_biomes`` is also specified). Candy mountain, anyone? Will make your map - quite boring, but tidy. -- ``force`` - Allow changing stone to soil and vice versa. **THIS CAN HAVE WEIRD EFFECTS, - USE WITH CARE AND SAVE FIRST**. Note that soil will not be magically replaced - with stone. You will, however, get a stone floor after digging, so it will - allow the floor to be engraved. Similarly, stone will not be magically - replaced with soil, but you will get a soil floor after digging, so it could - be helpful for creating farm plots on maps with no soil. -- ``verbose`` - Output details about what is being changed. - Examples -------- @@ -71,3 +49,24 @@ Examples weird stuff (flashing tiles, tiles changed all over place etc). Try reverting the changes manually or even better use an older savegame. You did save your game, right? + +Options +------- + +``all_biomes`` + Change the corresponding geology layer for all biomes on your map. Be aware + that the same geology layer can AND WILL be on different z-levels for + different biomes. +``all_layers`` + Change all geology layers on your map (only for the selected biome unless + ``all_biomes`` is also specified). Candy mountain, anyone? Will make your map + quite boring, but tidy. +``force`` + Allow changing stone to soil and vice versa. **THIS CAN HAVE WEIRD EFFECTS, + USE WITH CARE AND SAVE FIRST**. Note that soil will not be magically replaced + with stone. You will, however, get a stone floor after digging, so it will + allow the floor to be engraved. Similarly, stone will not be magically + replaced with soil, but you will get a soil floor after digging, so it could + be helpful for creating farm plots on maps with no soil. +``verbose`` + Output details about what is being changed. diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index 7de94a3f0..e23c3f063 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -1,8 +1,7 @@ changevein ========== - Tags: -:dfhack-keybind: +:dfhack-keybind:`changevein` :index:`Change the material of a mineral inclusion. ` You can change it to @@ -20,5 +19,5 @@ Usage:: Example ------- -- ``changevein NATIVE_PLATINUM`` +``changevein NATIVE_PLATINUM`` Convert vein at cursor position into platinum ore. diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index 286069b63..37416c551 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -1,6 +1,5 @@ cleanconst ========== - Tags: :dfhack-keybind:`cleanconst` diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 9578bb22a..305bf0db6 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -3,7 +3,6 @@ cleaners ======== - Tags: :dfhack-keybind:`clean` :dfhack-keybind:`spotclean` @@ -28,22 +27,22 @@ includes hostiles, and that cleaning items removes poisons from weapons. if you just want to clean a specific tile but don't want the `clean` command to remove all the glorious blood from your entranceway. +Examples +-------- + +``clean all`` + Clean everything that can be cleaned (except mud and snow). +``clean all mud item snow`` + Removes all spatter, including mud, leaves, and snow from map tiles. + Options ------- When cleaning the map, you can specify extra options for extra cleaning: -- ``mud`` +``mud`` Also remove mud. -- ``item`` +``item`` Also remove item spatter, like fallen leaves and flowers. -- ``snow`` +``snow`` Also remove snow coverings. - -Examples --------- - -- ``clean all`` - Clean everything that can be cleaned (except mud and snow). -- ``clean all mud item snow`` - Removes all spatter, including mud, leaves, and snow from map tiles. diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst index e827989c9..c90fe0569 100644 --- a/docs/plugins/cleanowned.rst +++ b/docs/plugins/cleanowned.rst @@ -1,6 +1,5 @@ cleanowned ========== - Tags: :dfhack-keybind:`cleanowned` @@ -21,11 +20,11 @@ anything. You can confiscate additional types of items by adding them to the commandline: -- ``scattered`` +``scattered`` Confiscate/dump all items scattered on the floor. -- ``x`` +``x`` Confiscate/dump items with wear level 'x' (lightly worn) and more. -- ``X`` +``X`` Confiscate/dump items with wear level 'X' (heavily worn) and more. Or you can confiscate all owned items by specifying ``all``. @@ -33,6 +32,6 @@ Or you can confiscate all owned items by specifying ``all``. Example ------- -- ``cleanowned scattered X`` +``cleanowned scattered X`` Confiscate and dump rotten and dropped food, garbage on the floors, and any worn items with 'X' damage and above. diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 6e3d2a829..3b46e1b33 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -1,6 +1,5 @@ command-prompt ============== - Tags: :dfhack-keybind:`command-prompt` diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index 25cc04956..aceb1e6d6 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -1,6 +1,5 @@ confirm ======= - Tags: :dfhack-keybind:`confirm` @@ -11,9 +10,9 @@ case you hit the key accidentally. Usage: -- ``enable confirm``, ``confirm enable all`` +``enable confirm``, ``confirm enable all`` Enable all confirmation options. Replace with ``disable`` to disable all. -- ``confirm enable option1 [option2...]`` +``confirm enable option1 [option2...]`` Enable (or ``disable``) specific confirmation dialogs. When run without parameters, ``confirm`` will report which confirmation dialogs diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index 13ecc2e58..b49a85be4 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -1,6 +1,5 @@ createitem ========== - Tags: :dfhack-keybind:`createitem` @@ -23,14 +22,14 @@ Corpses, body parts, and prepared meals cannot be created using this tool. Usage: -- ``createitem []`` +``createitem []`` Create copies (default is 1) of the specified item made out of the specified material. -- ``createitem inspect`` +``createitem inspect`` Obtain the item and material tokens of an existing item. Its output can be used directly as arguments to ``createitem`` to create new matching items (as long as the item type is supported). -- ``createitem floor|item|building`` +``createitem floor|item|building`` Subsequently created items will be placed on the floor beneath the selected unit's, inside the selected item, or as part of the selected building. @@ -39,14 +38,15 @@ Usage: ``createitem building`` is good for loading traps, but if you use it with workshops, you will have to deconstruct the workshop to access the item. -Examples: +Examples +-------- -- ``createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2`` +``createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2`` Create 2 pairs of steel gauntlets (that is, 2 left gauntlets and 2 right gauntlets). -- ``createitem WOOD PLANT_MAT:TOWER_CAP:WOOD 100`` +``createitem WOOD PLANT_MAT:TOWER_CAP:WOOD 100`` Create 100 tower-cap logs. -- ``createitem PLANT_GROWTH BILBERRY:FRUIT`` +``createitem PLANT_GROWTH BILBERRY:FRUIT`` Create a single bilberry. For more examples, :wiki:`the wiki `. diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index 79da6d621..17cdacf26 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -1,6 +1,5 @@ cursecheck ========== - Tags: :dfhack-keybind:`cursecheck` @@ -22,23 +21,8 @@ Usage:: cursecheck [] -Options: - -- ``detail`` - Print full name, date of birth, date of curse, and some status info (some - vampires might use fake identities in-game, though). -- ``nick`` - Set the type of curse as nickname (does not always show up in-game; some - vamps don't like nicknames). -- ``ids`` - Print the creature and race IDs. -- ``all`` - Include dead and passive cursed creatures (this can result in quite a long - list after having !!FUN!! with necromancers). -- ``verbose`` - Print all curse tags (if you really want to know it all). - -Examples: +Examples +-------- - ``cursecheck`` Display a count of cursed creatures on the map (or under the cursor). @@ -54,3 +38,20 @@ Examples: If you see any living/active creatures with a cursetype of "unknown", then it is most likely a new type of curse introduced by a mod. + +Options +------- + +``detail`` + Print full name, date of birth, date of curse, and some status info (some + vampires might use fake identities in-game, though). +``nick`` + Set the type of curse as nickname (does not always show up in-game; some + vamps don't like nicknames). +``ids`` + Print the creature and race IDs. +``all`` + Include dead and passive cursed creatures (this can result in quite a long + list after having !!FUN!! with necromancers). +``verbose`` + Print all curse tags (if you really want to know it all). From 3c92d4f1956d67cd7b07d62e80aa19c77b9289eb Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 23 Jul 2022 16:35:40 -0700 Subject: [PATCH 105/334] Fix typo --- library/lua/helpdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 8b35b0d25..5e0f49920 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -636,7 +636,7 @@ end function is_builtin(command) ensure_db() return entrydb[command] and - get_db_property(entry_name, 'entry_types')[ENTRY_TYPES.BUILTIN] + get_db_property(command, 'entry_types')[ENTRY_TYPES.BUILTIN] end --------------------------------------------------------------------------- From 2ed93418d2b7400162aa9ab6e6720bfd05af692a Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 21:51:28 -0700 Subject: [PATCH 106/334] separate base command cnts from user command cnts --- data/CMakeLists.txt | 3 +++ .../command_counts.json => data/base_command_counts.json | 0 2 files changed, 3 insertions(+) rename dfhack-config/command_counts.json => data/base_command_counts.json (100%) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index ea88d4473..412ffa347 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -1,6 +1,9 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/init/ DESTINATION "${DFHACK_DATA_DESTINATION}/init") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/base_command_counts.json + DESTINATION "${DFHACK_DATA_DESTINATION}/data/base_command_counts.json") + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/quickfort/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/quickfort") diff --git a/dfhack-config/command_counts.json b/data/base_command_counts.json similarity index 100% rename from dfhack-config/command_counts.json rename to data/base_command_counts.json From 5f56d792352c0ee3ca2c1656cd96231c8e46a804 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 21:55:49 -0700 Subject: [PATCH 107/334] move dfhack.history to dfhack-config/dfhack.history --- docs/plugins/debug.rst | 22 ++++++++--------- docs/plugins/deramp.rst | 1 - docs/plugins/dig-now.rst | 23 +++++++++--------- docs/plugins/dig.rst | 52 ++++++++++++++++++++++++++-------------- library/Core.cpp | 6 +++-- 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst index 331e675ab..2b3b20359 100644 --- a/docs/plugins/debug.rst +++ b/docs/plugins/debug.rst @@ -1,6 +1,5 @@ debug ===== - Tags: :dfhack-keybind:`debugfilter` @@ -37,36 +36,37 @@ printing level selection. Usage: -- ``debugfilter category [] []`` +``debugfilter category [] []`` List available debug plugin and category names. If filters aren't givenm then all plugins/categories are matched. This command is a good way to test regex parameters before you pass them to ``set``. -- ``debugfilter filter []`` +``debugfilter filter []`` List active and passive debug print level changes. The optional ``id`` parameter is the id listed as first column in the filter list. If ``id`` is given, then the command shows extended information for the given filter only. -- ``debugfilter set [] [] []`` +``debugfilter set [] [] []`` Create a new debug filter to set category verbosity levels. This filter will not be saved when the DF process exists or the plugin is unloaded. -- ``debugfilter set persistent [] [] []`` +``debugfilter set persistent [] [] []`` Store the filter in the configuration file to until ``unset`` is used to remove it. -- ``debugfilter unset [ ...]`` +``debugfilter unset [ ...]`` Delete a space separated list of filters. -- ``debugfilter disable [ ...]`` +``debugfilter disable [ ...]`` Disable a space separated list of filters but keep it in the filter list. -- ``debugfilter enable [ ...]`` +``debugfilter enable [ ...]`` Enable a space sperate list of filters. -- ``debugfilter header [enable] | [disable] [ ...]`` +``debugfilter header [enable] | [disable] [ ...]`` Control which header metadata is shown along with each log message. Run it without parameters to see the list of configurable elements. Include an ``enable`` or ``disable`` keyword to change whether specific elements are shown. -Examples: +Example +------- -- ``debugfilter set Warning core script`` +``debugfilter set Warning core script`` Hide script execution log messages (e.g. "Loading script: dfhack-config/dfhack.init"), which are normally output at Info verbosity in the "core" plugin with the "script" category. diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index 7489e506e..7f4a8ce24 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -1,6 +1,5 @@ deramp ====== - Tags: :dfhack-keybind:`deramp` diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index 4628f10f5..cdb21c01a 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -1,6 +1,5 @@ dig-now ======= - Tags: :dfhack-keybind:`dig-now` @@ -33,31 +32,33 @@ Any ```` parameters can either be an ``,,`` triple (e.g. game cursor should be used. You can use the `position` command to get the current cursor position if you need it. -Examples: +Examples +-------- -- ``dig-now`` +``dig-now`` Dig designated tiles according to standard game rules. -- ``dig-now --clean`` +``dig-now --clean`` Dig all designated tiles, but don't generate any boulders, ores, or gems. -- ``dig-now --dump here`` +``dig-now --dump here`` Dig tiles and teleport all generated boulders, ores, and gems to the tile under the game cursor. -Options: +Options +------- -- ``-c``, ``--clean`` +``-c``, ``--clean`` Don't generate any boulders, ores, or gems. Equivalent to ``--percentages 0,0,0,0``. -- ``-d``, ``--dump `` +``-d``, ``--dump `` Dump any generated items at the specified coordinates. If the tile at those coordinates is open space or is a wall, items will be generated on the closest walkable tile below. -- ``-e``, ``--everywhere`` +``-e``, ``--everywhere`` Generate a boulder, ore, or gem for every tile that can produce one. Equivalent to ``--percentages 100,100,100,100``. -- ``-p``, ``--percentages ,,,`` +``-p``, ``--percentages ,,,`` Set item generation percentages for each of the tile categories. The ``vein`` category includes both the large oval clusters and the long stringy mineral veins. Default is ``25,33,100,100``. -- ``-z``, ``--cur-zlevel`` +``-z``, ``--cur-zlevel`` Restricts the bounds to the currently visible z-level. diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index c4811883b..edb9b8007 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -3,17 +3,39 @@ dig === -This plugin makes many automated or complicated dig patterns easy. - -Basic commands: - -:digv: Designate all of the selected vein for digging. -:digvx: Also cross z-levels, digging stairs as needed. Alias for ``digv x``. -:digl: Like ``digv``, for layer stone. Also supports an ``undo`` option - to remove designations, for if you accidentally set 50 levels at once. -:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. - +Tags: :dfhack-keybind:`digv` +:dfhack-keybind:`digvx` +:dfhack-keybind:`digl` +:dfhack-keybind:`diglx` +:dfhack-keybind:`digcircle` +:dfhack-keybind:`digtype` +:dfhack-keybind:`digexp` + +Make complicated dig patterns easy. + +Usage: + +``digv [x] [-p]`` + Designate all of the selected vein for digging. +``digvx [-p]`` + Also cross z-levels, digging stairs as needed. Alias for ``digv x``. +``digl [x] [undo] [-p]`` + Like ``digv``, for layer stone. If ``undo`` is specified, removes the + layer designation instead (for if you accidentally set 50 levels at once). +``diglx [-p]`` + Also cross z-levels, digging stairs as needed. Alias for ``digl x``. +``digcircle [] [] [] [] [-p]`` + Designate circles. The diameter is the number of tiles across the center of + the circle that you want to dig. See the `digcircle`_ section below for an + explanation of the options. +``digtype [] +For every tile on the map of the same vein type as the selected tile, +this command designates it to have the same designation as the +selected tile. If the selected tile has no designation, they will be +dig designated. + +``digexp [] [] .. note:: @@ -24,15 +46,9 @@ Basic commands: digcircle ========= -A command for easy designation of filled and hollow circles. -It has several types of options. - -Shape: - -:hollow: Set the circle to hollow (default) -:filled: Set the circle to filled -:#: Diameter in tiles (default = 0, does nothing) + Designate filled or hollow circles. If neither ``hollow`` nor ``filled`` + is specified, the default is ``hollow``. The diameter is the number of tiles Action: :set: Set designation (default) diff --git a/library/Core.cpp b/library/Core.cpp index 72ddc33aa..a926391d9 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1311,12 +1311,14 @@ void fInitthread(void * iodata) // A thread function... for the interactive console. void fIOthread(void * iodata) { + static const char * HISTORY_FILE = "dfhack-config/dfhack.history"; + IODATA * iod = ((IODATA*) iodata); Core * core = iod->core; PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; CommandHistory main_history; - main_history.load("dfhack.history"); + main_history.load(HISTORY_FILE); Console & con = core->getConsole(); if (plug_mgr == 0) @@ -1357,7 +1359,7 @@ void fIOthread(void * iodata) { // a proper, non-empty command was entered main_history.add(command); - main_history.save("dfhack.history"); + main_history.save(HISTORY_FILE); } auto rv = core->runCommand(con, command); From f9d4781cbcb40c70badf78e57d7147e550238b8a Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 22:12:34 -0700 Subject: [PATCH 108/334] use dfhack-config/lua.history instead of lua.history --- library/LuaTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index cef46c053..fea90b394 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1145,7 +1145,7 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state, return false; if (!hfile) - hfile = "lua.history"; + hfile = "dfhack-config/lua.history"; if (!prompt) prompt = "lua"; From d1f690baa51afb0cdb20707f68055dffb88b68ca Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 23 Jul 2022 22:12:58 -0700 Subject: [PATCH 109/334] move liquids.history to dfhack-config/liquids.history --- plugins/liquids.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index 5dd64812f..76c098c8a 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -55,6 +55,7 @@ using namespace df::enums; DFHACK_PLUGIN("liquids"); REQUIRE_GLOBAL(world); +static const char * HISTORY_FILE = "dfhack-config/liquids.history"; CommandHistory liquids_hist; command_result df_liquids (color_ostream &out, vector & parameters); @@ -62,7 +63,7 @@ command_result df_liquids_here (color_ostream &out, vector & parameters DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - liquids_hist.load("liquids.history"); + liquids_hist.load(HISTORY_FILE); commands.push_back(PluginCommand( "liquids", "Place magma, water or obsidian.", df_liquids, true, @@ -80,7 +81,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sat, 23 Jul 2022 22:13:19 -0700 Subject: [PATCH 110/334] move tiletypes.history to dfhack-config/tiletypes.history --- plugins/tiletypes.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index 7f39ddd4e..b8bbc8d04 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -74,7 +74,7 @@ static const struct_field_info tiletypes_options_fields[] = { }; struct_identity tiletypes_options::_identity(sizeof(tiletypes_options), &df::allocator_fn, NULL, "tiletypes_options", NULL, tiletypes_options_fields); - +static const char * HISTORY_FILE = "dfhack-config/tiletypes.history"; CommandHistory tiletypes_hist; command_result df_tiletypes (color_ostream &out, vector & parameters); @@ -84,7 +84,7 @@ command_result df_tiletypes_here_point (color_ostream &out, vector & pa DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - tiletypes_hist.load("tiletypes.history"); + tiletypes_hist.load(HISTORY_FILE); commands.push_back(PluginCommand("tiletypes", "Paint map tiles freely, similar to liquids.", df_tiletypes, true)); commands.push_back(PluginCommand("tiletypes-command", "Run tiletypes commands (seperated by ' ; ')", df_tiletypes_command)); commands.push_back(PluginCommand("tiletypes-here", "Repeat tiletypes command at cursor (with brush)", df_tiletypes_here)); @@ -94,7 +94,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sat, 23 Jul 2022 22:40:33 -0700 Subject: [PATCH 111/334] update docs for dig --- docs/plugins/dig.rst | 177 +++++++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 74 deletions(-) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index edb9b8007..941172ef7 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -27,101 +27,130 @@ Usage: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. ``digcircle [] [] [] [] [-p]`` Designate circles. The diameter is the number of tiles across the center of - the circle that you want to dig. See the `digcircle`_ section below for an - explanation of the options. -``digtype [] -For every tile on the map of the same vein type as the selected tile, -this command designates it to have the same designation as the -selected tile. If the selected tile has no designation, they will be -dig designated. - -``digexp [] [] - -.. note:: - - All commands implemented by the `dig` plugin (listed by ``ls dig``) support - specifying the designation priority with ``-p#``, ``-p #``, or ``p=#``, - where ``#`` is a number from 1 to 7. If a priority is not specified, the - priority selected in-game is used as the default. + the circle that you want to dig. See the `digcircle`_ section below for + options. +``digtype [] [-p]`` + Designate all vein tiles of the selected type. See the `digtype`_ section + below for options. +``digexp [] [] [-p]`` + Designate dig patterns for exploratory mining. See the `digexp`_ section + below for options + +All commands support specifying the priority of the dig designations with +``-p``, where the number is from 1 to 7. If a priority is not specified, +the priority selected in-game is used as the default. + +Examples +-------- + +``digcircle filled 3`` + Dig a filled circle with a diameter of 3 tiles. +``digcircle`` + Do it again (previous parameters are reused). +``expdig diag5 hidden`` + Designate the diagonal 5 pattern over all hidden tiles on the current + z-level. +``expdig ladder designated`` + Take existing designations on the current z-level and replace them with the + ladder pattern. +``expdig`` + Do it again (previous parameters are reused). digcircle -========= +--------- - Designate filled or hollow circles. If neither ``hollow`` nor ``filled`` - is specified, the default is ``hollow``. The diameter is the number of tiles -Action: +The ``digcircle`` command can accept up to one option of each type below. -:set: Set designation (default) -:unset: Unset current designation -:invert: Invert designations already present +Solidity options: -Designation types: +``hollow`` + Designates hollow circles (default). +``filled`` + Designates filled circles. -:dig: Normal digging designation (default) -:ramp: Ramp digging -:ustair: Staircase up -:dstair: Staircase down -:xstair: Staircase up/down -:chan: Dig channel +Action options: -After you have set the options, the command called with no options -repeats with the last selected parameters. +``set`` + Set designation (default). +``unset`` + Unset current designation. +``invert`` + Invert designations already present. -Examples: +Designation options: -``digcircle filled 3`` - Dig a filled circle with diameter = 3. -``digcircle`` - Do it again. +``dig`` + Normal digging designation (default). +``ramp`` + Dig ramps. +``ustair`` + Dig up staircases. +``dstair`` + Dig down staircases. +``xstair`` + Dig up/down staircases. +``chan`` + Dig channels. + +After you have set the options, the command called with no options repeats with +the last selected parameters. digtype -======= -For every tile on the map of the same vein type as the selected tile, -this command designates it to have the same designation as the -selected tile. If the selected tile has no designation, they will be -dig designated. -If an argument is given, the designation of the selected tile is -ignored, and all appropriate tiles are set to the specified -designation. - -Options: - -:dig: -:channel: -:ramp: -:updown: up/down stairs -:up: up stairs -:down: down stairs -:clear: clear designation +------- + +For every tile on the map of the same vein type as the selected tile, this +command designates it to have the same designation as the selected tile. If the +selected tile has no designation, they will be dig designated. + +If an argument is given, the designation of the selected tile is ignored, and +all appropriate tiles are set to the specified designation. + +Designation options: + +``dig`` + Normal digging designation. +``channel`` + Dig channels. +``ramp`` + Dig ramps. +``updown`` + Dig up/down staircases. +``up`` + Dig up staircases. +``down`` + Dig down staircases. +``clear`` + Clear any designations. digexp -====== +------ + This command is for :wiki:`exploratory mining `. There are two variables that can be set: pattern and filter. Patterns: -:diag5: diagonals separated by 5 tiles -:diag5r: diag5 rotated 90 degrees -:ladder: A 'ladder' pattern -:ladderr: ladder rotated 90 degrees -:clear: Just remove all dig designations -:cross: A cross, exactly in the middle of the map. +``diag5`` + Diagonals separated by 5 tiles. +``diag5r`` + The diag5 pattern rotated 90 degrees. +``ladder`` + A 'ladder' pattern. +``ladderr`` + The ladder pattern rotated 90 degrees. +``cross`` + A cross, exactly in the middle of the map. +``clear`` + Just remove all dig designations. Filters: -:all: designate whole z-level -:hidden: designate only hidden tiles of z-level (default) -:designated: Take current designation and apply pattern to it. +``hidden`` + Designate only hidden tiles of z-level (default) +``all`` + Designate the whole z-level. +``designated`` + Take current designation and apply the selected pattern to it. After you have a pattern set, you can use ``expdig`` to apply it again. - -Examples: - -``expdig diag5 hidden`` - Designate the diagonal 5 patter over all hidden tiles -``expdig`` - Apply last used pattern and filter -``expdig ladder designated`` - Take current designations and replace them with the ladder pattern From a567665ab29446e00676b0757752eaeb53b93374 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 24 Jul 2022 05:41:40 +0000 Subject: [PATCH 112/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- library/Core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index a926391d9..facd02823 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1312,7 +1312,7 @@ void fInitthread(void * iodata) void fIOthread(void * iodata) { static const char * HISTORY_FILE = "dfhack-config/dfhack.history"; - + IODATA * iod = ((IODATA*) iodata); Core * core = iod->core; PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; From ca06d1d9c55531ece4aa8527ac2e101706e4c719 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 16:08:35 -0700 Subject: [PATCH 113/334] update docs for dig (again) --- docs/plugins/dig.rst | 37 ++++++++++++++--------- plugins/dig.cpp | 70 ++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 56 deletions(-) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 941172ef7..8fbf1a910 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -17,24 +17,33 @@ Make complicated dig patterns easy. Usage: ``digv [x] [-p]`` - Designate all of the selected vein for digging. + :index:`Designate all of the selected vein for digging. + ` ``digvx [-p]`` - Also cross z-levels, digging stairs as needed. Alias for ``digv x``. + :index:`Dig a vein across z-levels, digging stairs as needed. + ` + This is an alias for ``digv x``. ``digl [x] [undo] [-p]`` - Like ``digv``, for layer stone. If ``undo`` is specified, removes the - layer designation instead (for if you accidentally set 50 levels at once). + :index:`Dig all of the selected layer stone. + ` If ``undo`` is specified, + removes the designation instead (for if you accidentally set 50 levels at + once). ``diglx [-p]`` - Also cross z-levels, digging stairs as needed. Alias for ``digl x``. + :index:`Dig layer stone across z-levels, digging stairs as needed. + ` This + is an alias for ``digl x``. ``digcircle [] [] [] [] [-p]`` - Designate circles. The diameter is the number of tiles across the center of - the circle that you want to dig. See the `digcircle`_ section below for - options. + :index:`Designate circles. ` The diameter + is the number of tiles across the center of the circle that you want to dig. + See the `digcircle`_ section below for options. ``digtype [] [-p]`` - Designate all vein tiles of the selected type. See the `digtype`_ section - below for options. + :index:`Designate all vein tiles of the selected type. + ` See the `digtype`_ + section below for options. ``digexp [] [] [-p]`` - Designate dig patterns for exploratory mining. See the `digexp`_ section - below for options + :index:`Designate dig patterns for exploratory mining. + ` See the `digexp`_ + section below for options All commands support specifying the priority of the dig designations with ``-p``, where the number is from 1 to 7. If a priority is not specified, @@ -43,8 +52,8 @@ the priority selected in-game is used as the default. Examples -------- -``digcircle filled 3`` - Dig a filled circle with a diameter of 3 tiles. +``digcircle filled 3 -p2`` + Dig a filled circle with a diameter of 3 tiles at dig priority 2. ``digcircle`` Do it again (previous parameters are reused). ``expdig diag5 hidden`` diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 6c33f206a..ddc2cd97e 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -28,7 +28,6 @@ command_result digv (color_ostream &out, vector & parameters); command_result digvx (color_ostream &out, vector & parameters); command_result digl (color_ostream &out, vector & parameters); command_result diglx (color_ostream &out, vector & parameters); -command_result digauto (color_ostream &out, vector & parameters); command_result digexp (color_ostream &out, vector & parameters); command_result digcircle (color_ostream &out, vector & parameters); command_result digtype (color_ostream &out, vector & parameters); @@ -40,44 +39,37 @@ REQUIRE_GLOBAL(world); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "digv","Dig a whole vein.",digv,Gui::cursor_hotkey, - " Designates a whole vein under the cursor for digging.\n" - "Options:\n" - " x - follow veins through z-levels with stairs.\n" - )); + "digv", + "Dig a whole vein.", + digv, + Gui::cursor_hotkey)); commands.push_back(PluginCommand( - "digvx","Dig a whole vein, following through z-levels.",digvx,Gui::cursor_hotkey, - " Designates a whole vein under the cursor for digging.\n" - " Also follows the vein between z-levels with stairs, like 'digv x' would.\n" - )); + "digvx", + "Dig a whole vein, following through z-levels.", + digvx, + Gui::cursor_hotkey)); commands.push_back(PluginCommand( - "digl","Dig layerstone.",digl,Gui::cursor_hotkey, - " Designates layerstone under the cursor for digging.\n" - " Veins will not be touched.\n" - "Options:\n" - " x - follow layer through z-levels with stairs.\n" - " undo - clear designation (can be used together with 'x').\n" - )); + "digl", + "Dig layerstone.", + digl, + Gui::cursor_hotkey)); commands.push_back(PluginCommand( - "diglx","Dig layerstone, following through z-levels.",diglx,Gui::cursor_hotkey, - " Designates layerstone under the cursor for digging.\n" - " Also follows the stone between z-levels with stairs, like 'digl x' would.\n" - )); - commands.push_back(PluginCommand("digexp","Select or designate an exploratory pattern.",digexp)); - commands.push_back(PluginCommand("digcircle","Dig designate a circle (filled or hollow)",digcircle)); - //commands.push_back(PluginCommand("digauto","Mark a tile for continuous digging.",autodig)); - commands.push_back(PluginCommand("digtype", "Dig all veins of a given type.", digtype,Gui::cursor_hotkey, - "For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated.\n" - "If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.\n" - "Options:\n" - " dig\n" - " channel\n" - " ramp\n" - " updown - up/down stairs\n" - " up - up stairs\n" - " down - down stairs\n" - " clear - clear designation\n" - )); + "diglx", + "Dig layerstone, following through z-levels.", + diglx, + Gui::cursor_hotkey)); + commands.push_back(PluginCommand( + "digexp", + "Select or designate an exploratory pattern.", + digexp)); + commands.push_back(PluginCommand( + "digcircle", + "Dig designate a circle (filled or hollow)", + digcircle)); + commands.push_back(PluginCommand( + "digtype", + "Dig all veins of a given type.", + digtype,Gui::cursor_hotkey)); return CR_OK; } @@ -1420,12 +1412,6 @@ command_result digl (color_ostream &out, vector & parameters) return CR_OK; } - -command_result digauto (color_ostream &out, vector & parameters) -{ - return CR_NOT_IMPLEMENTED; -} - command_result digtype (color_ostream &out, vector & parameters) { //mostly copy-pasted from digv From 60b599865c664fe065d875f20874b55b111d7a11 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 17:01:50 -0700 Subject: [PATCH 114/334] update docs for digFlood --- docs/plugins/digFlood.rst | 46 ++++++++++++++++++++++++++++----------- plugins/digFlood.cpp | 23 +++----------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst index dd957feb2..cb8018145 100644 --- a/docs/plugins/digFlood.rst +++ b/docs/plugins/digFlood.rst @@ -1,18 +1,38 @@ digFlood ======== -Automatically digs out specified veins as they are discovered. It runs once -every time a dwarf finishes a dig job. It will only dig out appropriate tiles -that are adjacent to the finished dig job. To add a vein type, use ``digFlood 1 [type]``. -This will also enable the plugin. To remove a vein type, use ``digFlood 0 [type] 1`` -to disable, then remove, then re-enable. +Tags: +:dfhack-keybind:`digFlood` + +:index:`Digs out veins as they are discovered. +` It will only dig out +appropriate tiles that are adjacent to a just-finished dig job, so if you want +to autodig a vein that has already been discovered, you may need to manually +designate one tile of the tile for digging to get started. Usage: -:help digflood: detailed help message -:digFlood 0: disable the plugin -:digFlood 1: enable the plugin -:digFlood 0 MICROCLINE COAL_BITUMINOUS 1: - disable plugin, remove microcline and bituminous coal from monitoring, then re-enable the plugin -:digFlood CLEAR: remove all inorganics from monitoring -:digFlood digAll1: ignore the monitor list and dig any vein -:digFlood digAll0: disable digAll mode +``enable digflood`` + Enable the plugin. +``digflood 1 [ ...]`` + Start monitoring for the specified vein types. +``digFlood 0 [ ...] 1`` + Stop monitoring for the specified vein types. Note the required ``1`` at the + end. +``digFlood CLEAR`` + Remove all inorganics from monitoring. +``digFlood digAll1`` + Ignore the monitor list and dig any vein. +``digFlood digAll0`` + Disable digAll mode. + +You can get the list of valid vein types with this command:: + + lua "for i,mat in ipairs(df.global.world.raws.inorganics) do if mat.material.flags.IS_STONE and not mat.material.flags.NO_STONE_STOCKPILE then print(i, mat.id) end end" + +Examples +-------- + +``digFlood 1 MICROCLINE COAL_BITUMINOUS`` + Automatically dig microcline and bituminous coal veins. +``digFlood 0 MICROCLINE 1`` + Stop automatically digging microcline. diff --git a/plugins/digFlood.cpp b/plugins/digFlood.cpp index 459a12c11..7370d3fe5 100644 --- a/plugins/digFlood.cpp +++ b/plugins/digFlood.cpp @@ -51,26 +51,9 @@ DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) { DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "digFlood", "Automatically dig out veins as you discover them.", - digFlood, false, - "Example:\n" - " digFlood 0\n" - " disable plugin\n" - " digFlood 1\n" - " enable plugin\n" - " digFlood 0 MICROCLINE COAL_BITUMINOUS 1\n" - " disable plugin and remove microcline and bituminous coal from being monitored, then re-enable plugin\n" - " digFlood 1 MICROCLINE 0 COAL_BITUMINOUS 1\n" - " do monitor microcline, don't monitor COAL_BITUMINOUS, then enable plugin\n" - " digFlood CLEAR\n" - " remove all inorganics from monitoring\n" - " digFlood digAll1\n" - " enable digAll mode: dig any vein, regardless of the monitor list\n" - " digFlood digAll0\n" - " disable digAll mode\n" - "\n" - "Note that while order matters, multiple commands can be sequenced in one line. It is recommended to alter your save-specific regionX/raw/onLoad.init or global onLoadWorld.init file so that you won't have to type in every mineral type you want to dig every time you start the game. Material names are case sensitive.\n" - )); + "digFlood", + "Automatically dig out veins as you discover them.", + digFlood)); return CR_OK; } From b2ca3cb19439aaaed531b48f4b21943a54fff174 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 20:12:04 -0700 Subject: [PATCH 115/334] update docs for diggingInvaders --- docs/plugins/diggingInvaders.rst | 83 ++++++++++++++------- plugins/diggingInvaders/diggingInvaders.cpp | 25 +------ 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index 09a22c07a..9bca23163 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -1,28 +1,59 @@ diggingInvaders =============== -Makes invaders dig or destroy constructions to get to your dwarves. - -To enable/disable the pluging, use: ``diggingInvaders (1|enable)|(0|disable)`` - -Basic usage: - -:add GOBLIN: registers the race GOBLIN as a digging invader. Case-sensitive. -:remove GOBLIN: unregisters the race GOBLIN as a digging invader. Case-sensitive. -:now: makes invaders try to dig now, if plugin is enabled -:clear: clears all digging invader races -:edgesPerTick n: makes the pathfinding algorithm work on at most n edges per tick. - Set to 0 or lower to make it unlimited. - -You can also use ``diggingInvaders setCost (race) (action) n`` to set the -pathing cost of particular action, or ``setDelay`` to set how long it takes. -Costs and delays are per-tile, and the table shows default values. - -============================== ======= ====== ================================= -Action Cost Delay Notes -============================== ======= ====== ================================= -``walk`` 1 0 base cost in the path algorithm -``destroyBuilding`` 2 1,000 delay adds to the job_completion_timer of destroy building jobs that are assigned to invaders -``dig`` 10,000 1,000 digging soil or natural stone -``destroyRoughConstruction`` 1,000 1,000 constructions made from boulders -``destroySmoothConstruction`` 100 100 constructions made from blocks or bars -============================== ======= ====== ================================= +Tags: +:dfhack-keybind:`diggingInvaders` + +:index:`Invaders dig and destroy to get to your dwarves. +` + +Usage: + +``enable diggingInvaders`` + Enable the plugin. +``diggingInvaders add `` + Register the specified race as a digging invader. +``diggingInvaders remove `` + Unregisters the specified race as a digging invader. +``diggingInvaders now`` + Makes invaders try to dig now (if the plugin is enabled). +``diggingInvaders clear`` + Clears the registry of digging invader races. +``diggingInvaders edgesPerTick `` + Makes the pathfinding algorithm work on at most n edges per tick. Set to 0 + or lower to make it unlimited. +``diggingInvaders setCost `` + Set the pathing cost per tile for a particular action. This determines what + invaders consider to be the shortest path to their target. +``diggingInvaders setDelay `` + Set the time cost (in ticks) for performing a particular action. This + determines how long it takes for invaders to get to their target. + +Note that the race is case-sensitive. You can get a list of races for your world +with this command:: + + devel/query --table df.global.world.raws.creatures.all --search creature_id --maxdepth 1 --maxlength 5000 + +but in general, the race is what you'd expect, just capitalized (e.g. ``GOBLIN`` +or ``ELF``). + +Actions: +``walk`` + Default cost: 1, default delay: 0. This is the base cost for the pathing + algorithm. +``destroyBuilding`` + Default cost: 2, default delay: 1,000, +``dig`` + Default cost: 10,000, default delay: 1,000. This is for digging soil or + natural stone. +``destroyRoughConstruction`` + Default cost: 1,000, default delay: 1,000. This is for destroying + constructions made from boulders. +``destroySmoothConstruction`` + Default cost: 100, default delay: 100. This is for destroying constructions + made from blocks or bars. + +Example +------- + +``diggingInvaders add GOBLIN`` + Registers members of the GOBLIN race as a digging invader. diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 573c85320..457a00e50 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -120,28 +120,9 @@ static int32_t jobDelayDefault[] = { DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "diggingInvaders", "Makes invaders dig to your dwarves.", - diggingInvadersCommand, false, /* true means that the command can't be used from non-interactive user interface */ - " diggingInvaders 0\n disables the plugin\n" - " diggingInvaders 1\n enables the plugin\n" - " diggingInvaders enable\n enables the plugin\n" - " diggingInvaders disable\n disables the plugin\n" - " diggingInvaders add GOBLIN\n registers the race GOBLIN as a digging invader. Case-sensitive.\n" - " diggingInvaders remove GOBLIN\n unregisters the race GOBLIN as a digging invader. Case-sensitive.\n" - " diggingInvaders setCost GOBLIN walk n\n sets the walk cost in the path algorithm for the race GOBLIN\n" - " diggingInvaders setCost GOBLIN destroyBuilding n\n" - " diggingInvaders setCost GOBLIN dig n\n" - " diggingInvaders setCost GOBLIN destroyRoughConstruction n\n rough constructions are made from boulders\n" - " diggingInvaders setCost GOBLIN destroySmoothConstruction n\n smooth constructions are made from blocks or bars instead of boulders\n" - " diggingInvaders setDelay GOBLIN destroyBuilding n\n adds to the job_completion_timer of destroy building jobs that are assigned to invaders\n" - " diggingInvaders setDelay GOBLIN dig n\n" - " diggingInvaders setDelay GOBLIN destroyRoughConstruction n\n" - " diggingInvaders setDelay GOBLIN destroySmoothConstruction n\n" - " diggingInvaders now\n makes invaders try to dig now, if plugin is enabled\n" - " diggingInvaders clear\n clears all digging invader races\n" - " diggingInvaders edgesPerTick n\n makes the pathfinding algorithm work on at most n edges per tick. Set to 0 or lower to make it unlimited." -// " diggingInvaders\n Makes invaders try to dig now.\n" - )); + "diggingInvaders", + "Makes invaders dig to your dwarves.", + diggingInvadersCommand)); //*df::global::debug_showambush = true; return CR_OK; From d624e9167304c65f6daf096856f9de11af51b895 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 24 Jul 2022 20:59:24 -0700 Subject: [PATCH 116/334] Fix missing newline --- docs/plugins/diggingInvaders.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index 9bca23163..d2fada0d4 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -37,6 +37,7 @@ but in general, the race is what you'd expect, just capitalized (e.g. ``GOBLIN`` or ``ELF``). Actions: + ``walk`` Default cost: 1, default delay: 0. This is the base cost for the pathing algorithm. From ac11cde2130b587a586ea8ee8566d45f384a41a3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 23:08:44 -0700 Subject: [PATCH 117/334] update docs for dwarfmonitor --- docs/plugins/dwarfmonitor.rst | 65 +++++++++++++++++++++-------------- plugins/dwarfmonitor.cpp | 17 ++------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index cbe3e8bdc..295377c2d 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -1,35 +1,48 @@ dwarfmonitor ============ -Records dwarf activity to measure fort efficiency. - -Options: - -:enable : Start monitoring ``mode``. ``mode`` can be "work", "misery", - "weather", or "all". This will enable all corresponding widgets, - if applicable. -:disable : Stop monitoring ``mode``, and disable corresponding widgets, if applicable. -:stats: Show statistics summary -:prefs: Show dwarf preferences summary -:reload: Reload configuration file (``dfhack-config/dwarfmonitor.json``) - +Tags: :dfhack-keybind:`dwarfmonitor` -Widget configuration: - -The following types of widgets (defined in :file:`hack/lua/plugins/dwarfmonitor.lua`) -can be displayed on the main fortress mode screen: - -:date: Show the in-game date -:misery: Show overall happiness levels of all dwarves -:weather: Show current weather (rain/snow) -:cursor: Show the current mouse cursor position +:index:`Measure fort happiness and efficiency. +` Also show heads-up +display widgets with live fort statistics. + +Usage: + +``dwarfmonitor enable `` + Start tracking a specific facet of fortress life. The ``mode`` can be + "work", "misery", "date", "weather", or "all". This will show the + corresponding on-screen widgets, if applicable. +``dwarfmonitor disable `` + Stop monitoring ``mode`` and disable corresponding widgets. +``dwarfmonitor stats`` + Show statistics summary. +``dwarfmonitor prefs`` + Show summary of dwarf preferences. +``dwarfmonitor reload`` + Reload the widget configuration file (``dfhack-config/dwarfmonitor.json``). + +Widget configuration +-------------------- + +The following types of widgets (defined in +:file:`hack/lua/plugins/dwarfmonitor.lua`) can be displayed on the main fortress +mode screen: + +``misery`` + Show overall happiness levels of all dwarves. +``date`` + Show the in-game date. +``weather`` + Show current weather (e.g. rain/snow). +``cursor`` + Show the current mouse cursor position. The file :file:`dfhack-config/dwarfmonitor.json` can be edited to control the -positions and settings of all widgets displayed. This file should contain a -JSON object with the key ``widgets`` containing an array of objects - see the -included file in the ``dfhack-config`` folder for an example: +positions and settings of all widgets. This file should contain a JSON object +with the key ``widgets`` containing an array of objects: -.. code-block:: lua +.. code-block:: json { "widgets": [ @@ -45,7 +58,7 @@ included file in the ``dfhack-config`` folder for an example: X and Y coordinates begin at zero (in the upper left corner of the screen). Negative coordinates will be treated as distances from the lower right corner, beginning at 1 - e.g. an x coordinate of 0 is the leftmost column, while an x -coordinate of 1 is the rightmost column. +coordinate of -1 is the rightmost column. By default, the x and y coordinates given correspond to the leftmost tile of the widget. Including an ``anchor`` option set to ``right`` will cause the diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 84e9b9abe..493796fc6 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -2075,20 +2075,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector \n" - " Start monitoring \n" - " can be \"work\", \"misery\", \"weather\", or \"all\"\n" - "dwarfmonitor disable \n" - " as above\n\n" - "dwarfmonitor stats\n" - " Show statistics summary\n" - "dwarfmonitor prefs\n" - " Show dwarf preferences summary\n\n" - "dwarfmonitor reload\n" - " Reload configuration file (dfhack-config/dwarfmonitor.json)\n" - )); + "dwarfmonitor", + "Records dwarf activity to measure fort efficiency", + dwarfmonitor_cmd)); dm_lua::state=Lua::Core::State; if (dm_lua::state == NULL) From 91e3f6767c303267a43ebec02c361f8df53ab359 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 23:10:25 -0700 Subject: [PATCH 118/334] update dwarfmonitor short description --- plugins/dwarfmonitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 493796fc6..1a453e0d5 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -2076,7 +2076,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Sun, 24 Jul 2022 23:24:35 -0700 Subject: [PATCH 119/334] code block is not valid json --- docs/plugins/dwarfmonitor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index 295377c2d..6c1472c7d 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -42,7 +42,7 @@ The file :file:`dfhack-config/dwarfmonitor.json` can be edited to control the positions and settings of all widgets. This file should contain a JSON object with the key ``widgets`` containing an array of objects: -.. code-block:: json +.. code-block:: lua { "widgets": [ From 32e2ac21031fb3d7769c5b53b30f99505e70f0e5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 23:24:52 -0700 Subject: [PATCH 120/334] update docs for dwarfvet --- docs/plugins/dwarfvet.rst | 30 +++++++++++++++++------------- plugins/dwarfvet.cpp | 10 ++-------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index 1ceb1bc2f..6a1c5a60f 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -1,19 +1,23 @@ dwarfvet ======== -Enables Animal Caretaker functionality +Tags: +:dfhack-keybind:`dwarfvet` -Always annoyed your dragons become useless after a minor injury? Well, with -dwarfvet, your animals become first rate members of your fort. It can also -be used to train medical skills. +:index:`Allows animals to be treated at animal hospitals. +` -Animals need to be treated in an animal hospital, which is simply a hospital -that is also an animal training zone. The console will print out a list on game -load, and whenever one is added or removed. Dwarfs must have the Animal Caretaker -labor to treat animals. Normal medical skills are used (and no experience is given -to the Animal Caretaker skill). +Annoyed your dragons become useless after a minor injury? Well, with dwarfvet, +injured animals will be treated at an animal hospital, which is simply a hospital +that is also an animal training zone. Dwarfs with the Animal Caretaker labor +enabled will come to the hospital to treat animals. Normal medical skills are +used (and trained), but no experience is given to the Animal Caretaker skill +itself. -Options: +Usage: -:enable: Enables Animal Caretakers to treat and manage animals -:disable: Turns off the plguin -:report: Reports all zones that the game considers animal hospitals +``enable dwarfvet`` + Enables the plugin. +``dwarfvet report`` + Reports all zones that the game considers animal hospitals. +``dwarfvet report-usage`` + Reports on animals currently being treated. diff --git a/plugins/dwarfvet.cpp b/plugins/dwarfvet.cpp index 30308bc57..356fda238 100644 --- a/plugins/dwarfvet.cpp +++ b/plugins/dwarfvet.cpp @@ -370,14 +370,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sun, 24 Jul 2022 23:39:13 -0700 Subject: [PATCH 121/334] update docs for embark-assistant --- docs/plugins/embark-assistant.rst | 25 ++++++++++++++----- plugins/embark-assistant/embark-assistant.cpp | 14 +++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index 4500a919e..88f59efab 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -1,9 +1,22 @@ embark-assistant ================ +Tags: +:dfhack-keybind:`embark-assistant` -This plugin provides embark site selection help. It has to be run with the -``embark-assistant`` command while the pre-embark screen is displayed and shows -extended (and correct(?)) resource information for the embark rectangle as well -as normally undisplayed sites in the current embark region. It also has a site -selection tool with more options than DF's vanilla search tool. For detailed -help invoke the in game info screen. +:index:`Embark site selection support. +` Run this command while the +pre-embark screen is displayed to show extended (and correct(?)) resource +information for the embark rectangle as well as normally undisplayed sites in +the current embark region. You will also have access to a site selection tool +with far more options than DF's vanilla search tool. + +If you enable the plugin, you'll also be able to invoke ``embark-assistant`` +with the :kdb:`A` key on the pre-embark screen. + +Usage:: + + enable embark-assistant + embark-assistant + +Note the site selection tool requires a display height of at least 46 lines to +display properly. diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index 61a1b4f83..b7afe494b 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -178,17 +178,9 @@ IMPLEMENT_VMETHOD_INTERPOSE(start_site_hook, feed); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "embark-assistant", "Embark site selection support.", - embark_assistant, false, /* false means that the command can be used from non-interactive user interface */ - // Extended help string. Used by CR_WRONG_USAGE and the help command: - " This command starts the embark-assist plugin that provides embark site\n" - " selection help. It has to be called while the pre-embark screen is\n" - " displayed and shows extended (and correct(?)) resource information for\n" - " the embark rectangle as well as normally undisplayed sites in the\n" - " current embark region. It also has a site selection tool with more\n" - " options than DF's vanilla search tool. For detailed help invoke the\n" - " in game info screen. Prefers 46 lines to display properly.\n" - )); + "embark-assistant", + "Embark site selection support.", + embark_assistant)); return CR_OK; } From 02cc085a437e07e7b1e5ea379657879baa9592d6 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 24 Jul 2022 23:59:04 -0700 Subject: [PATCH 122/334] update docs for embark-tools --- docs/plugins/embark-assistant.rst | 2 +- docs/plugins/embark-tools.rst | 36 ++++++++++++++++++++++++------- plugins/embark-tools.cpp | 14 ++---------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index 88f59efab..3a6081655 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -11,7 +11,7 @@ the current embark region. You will also have access to a site selection tool with far more options than DF's vanilla search tool. If you enable the plugin, you'll also be able to invoke ``embark-assistant`` -with the :kdb:`A` key on the pre-embark screen. +with the :kbd:`A` key on the pre-embark screen. Usage:: diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index fcab18fc7..987b76df6 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -1,12 +1,32 @@ embark-tools ============ -A collection of embark-related tools. Usage and available tools:: +Tags: +:dfhack-keybind:`embark-tools` - embark-tools enable/disable tool [tool]... +:index:`Extend the embark screen functionality. +` -:anywhere: Allows embarking anywhere (including sites, mountain-only biomes, - and oceans). Use with caution. -:mouse: Implements mouse controls (currently in the local embark region only) -:sand: Displays an indicator when sand is present in the currently-selected - area, similar to the default clay/stone indicators. -:sticky: Maintains the selected local area while navigating the world map +Usage:: + + enable embark-tools + embark-tools enable|disable all + embark-tools enable|disable [ ...] + +Avaliable tools are: + +``anywhere`` + Allows embarking anywhere (including sites, mountain-only biomes, and + oceans). Use with caution. +``mouse`` + Implements mouse controls (currently in the local embark region only). +``sand`` + Displays an indicator when sand is present in the currently-selected area, + similar to the default clay/stone indicators. +``sticky`` + Maintains the selected local area while navigating the world map. + +Example +------- + +``embark-tools enable all`` + Enable all embark screen extensions. diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index b87295020..924def798 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -752,20 +752,10 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector second->getId() + ": " + iter->second->getDesc() + "\n"); - } commands.push_back(PluginCommand( "embark-tools", - "A collection of embark tools", - embark_tools_cmd, - false, - help.c_str() - )); + "Extend the embark screen functionality.", + embark_tools_cmd)); return CR_OK; } From 98ad22ddddc69ce06353414270237a62f240fb5b Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 06:08:59 -0700 Subject: [PATCH 123/334] align remaining plugin docs to plugin names --- ...essreader.rst => RemoteFortressReader.rst} | 0 docs/plugins/filltraffic.rst | 12 +++++ docs/plugins/fix-job-postings.rst | 5 -- docs/plugins/job-duplicate.rst | 6 --- docs/plugins/job-material.rst | 15 ------ docs/plugins/job.rst | 15 ------ docs/plugins/jobutils.rst | 40 +++++++++++++++ docs/plugins/liquids-here.rst | 6 --- docs/plugins/liquids.rst | 9 ++++ docs/plugins/nopause.rst | 4 -- docs/plugins/{plant.rst => plants.rst} | 2 + docs/plugins/prospect.rst | 51 ------------------- docs/plugins/prospector.rst | 2 + docs/plugins/rb.rst | 4 -- docs/plugins/restrictice.rst | 4 -- docs/plugins/restrictliquids.rst | 4 -- docs/plugins/reveal.rst | 7 +++ docs/plugins/revflood.rst | 23 --------- docs/plugins/revforget.rst | 23 --------- docs/plugins/revtoggle.rst | 23 --------- docs/plugins/ruby.rst | 2 + docs/plugins/sort-items.rst | 17 ------- docs/plugins/{sort-units.rst => sort.rst} | 16 ++++++ docs/plugins/tiletypes-command.rst | 10 ---- docs/plugins/tiletypes-here-point.rst | 6 --- docs/plugins/tiletypes-here.rst | 14 ----- docs/plugins/tiletypes.rst | 36 +++++++++++++ docs/plugins/unreveal.rst | 23 --------- docs/plugins/workflow.rst | 6 +++ 29 files changed, 132 insertions(+), 253 deletions(-) rename docs/plugins/{remotefortressreader.rst => RemoteFortressReader.rst} (100%) delete mode 100644 docs/plugins/fix-job-postings.rst delete mode 100644 docs/plugins/job-duplicate.rst delete mode 100644 docs/plugins/job-material.rst delete mode 100644 docs/plugins/job.rst create mode 100644 docs/plugins/jobutils.rst delete mode 100644 docs/plugins/liquids-here.rst delete mode 100644 docs/plugins/nopause.rst rename docs/plugins/{plant.rst => plants.rst} (97%) delete mode 100644 docs/plugins/prospect.rst delete mode 100644 docs/plugins/rb.rst delete mode 100644 docs/plugins/restrictice.rst delete mode 100644 docs/plugins/restrictliquids.rst delete mode 100644 docs/plugins/revflood.rst delete mode 100644 docs/plugins/revforget.rst delete mode 100644 docs/plugins/revtoggle.rst delete mode 100644 docs/plugins/sort-items.rst rename docs/plugins/{sort-units.rst => sort.rst} (52%) delete mode 100644 docs/plugins/tiletypes-command.rst delete mode 100644 docs/plugins/tiletypes-here-point.rst delete mode 100644 docs/plugins/tiletypes-here.rst delete mode 100644 docs/plugins/unreveal.rst diff --git a/docs/plugins/remotefortressreader.rst b/docs/plugins/RemoteFortressReader.rst similarity index 100% rename from docs/plugins/remotefortressreader.rst rename to docs/plugins/RemoteFortressReader.rst diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index 93306e9eb..d3e952204 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -1,4 +1,6 @@ .. _alltraffic: +.. _restrictice: +.. _restrictliquids: filltraffic =========== @@ -29,3 +31,13 @@ Options: :N: Normal Traffic :L: Low Traffic :R: Restricted Traffic + +restrictice +=========== +Restrict traffic on all tiles on top of visible ice. +See also `alltraffic`, `filltraffic`, and `restrictliquids`. + +restrictliquids +=============== +Restrict traffic on all visible tiles with liquid. +See also `alltraffic`, `filltraffic`, and `restrictice`. diff --git a/docs/plugins/fix-job-postings.rst b/docs/plugins/fix-job-postings.rst deleted file mode 100644 index 198dcd61d..000000000 --- a/docs/plugins/fix-job-postings.rst +++ /dev/null @@ -1,5 +0,0 @@ -fix-job-postings ----------------- -This command fixes crashes caused by previous versions of workflow, mostly in -DFHack 0.40.24-r4, and should be run automatically when loading a world (but can -also be run manually if desired). diff --git a/docs/plugins/job-duplicate.rst b/docs/plugins/job-duplicate.rst deleted file mode 100644 index a18d73df3..000000000 --- a/docs/plugins/job-duplicate.rst +++ /dev/null @@ -1,6 +0,0 @@ -job-duplicate -============= -In :kbd:`q` mode, when a job is highlighted within a workshop or furnace -building, calling ``job-duplicate`` instantly duplicates the job. - -:dfhack-keybind:`job-duplicate` diff --git a/docs/plugins/job-material.rst b/docs/plugins/job-material.rst deleted file mode 100644 index f1b37fc1d..000000000 --- a/docs/plugins/job-material.rst +++ /dev/null @@ -1,15 +0,0 @@ -job-material -============ -Alter the material of the selected job. Similar to ``job item-material ...`` - -Invoked as:: - - job-material - -:dfhack-keybind:`job-material` - -* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, - changes the material of the job. Only inorganic materials can be used - in this mode. -* In :kbd:`b` mode, during selection of building components positions the cursor - over the first available choice with the matching material. diff --git a/docs/plugins/job.rst b/docs/plugins/job.rst deleted file mode 100644 index 26f163864..000000000 --- a/docs/plugins/job.rst +++ /dev/null @@ -1,15 +0,0 @@ -job -=== -Command for general job query and manipulation. - -Options: - -*no extra options* - Print details of the current job. The job can be selected - in a workshop, or the unit/jobs screen. -**list** - Print details of all jobs in the selected workshop. -**item-material ** - Replace the exact material id in the job item. -**item-type ** - Replace the exact item type id in the job item. diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst new file mode 100644 index 000000000..230c69f4c --- /dev/null +++ b/docs/plugins/jobutils.rst @@ -0,0 +1,40 @@ +.. _job: + +job +=== +Command for general job query and manipulation. + +Options: + +*no extra options* + Print details of the current job. The job can be selected + in a workshop, or the unit/jobs screen. +**list** + Print details of all jobs in the selected workshop. +**item-material ** + Replace the exact material id in the job item. +**item-type ** + Replace the exact item type id in the job item. + +job-duplicate +============= +In :kbd:`q` mode, when a job is highlighted within a workshop or furnace +building, calling ``job-duplicate`` instantly duplicates the job. + +:dfhack-keybind:`job-duplicate` + +job-material +============ +Alter the material of the selected job. Similar to ``job item-material ...`` + +Invoked as:: + + job-material + +:dfhack-keybind:`job-material` + +* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, + changes the material of the job. Only inorganic materials can be used + in this mode. +* In :kbd:`b` mode, during selection of building components positions the cursor + over the first available choice with the matching material. diff --git a/docs/plugins/liquids-here.rst b/docs/plugins/liquids-here.rst deleted file mode 100644 index d838170a2..000000000 --- a/docs/plugins/liquids-here.rst +++ /dev/null @@ -1,6 +0,0 @@ -liquids-here ------------- -Run the liquid spawner with the current/last settings made in liquids (if no -settings in liquids were made it paints a point of 7/7 magma by default). - -Intended to be used as keybinding. Requires an active in-game cursor. diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index d55962ad5..75e41da5b 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -1,3 +1,5 @@ +.. _liquids-here: + liquids ======= Allows adding magma, water and obsidian to the game. It replaces the normal @@ -63,3 +65,10 @@ Brush size and shape: :block: DF map block with cursor in it (regular spaced 16x16x1 blocks) :column: Column from cursor, up through free space :flood: Flood-fill water tiles from cursor (only makes sense with wclean) + +liquids-here +------------ +Run the liquid spawner with the current/last settings made in liquids (if no +settings in liquids were made it paints a point of 7/7 magma by default). + +Intended to be used as keybinding. Requires an active in-game cursor. diff --git a/docs/plugins/nopause.rst b/docs/plugins/nopause.rst deleted file mode 100644 index c30543187..000000000 --- a/docs/plugins/nopause.rst +++ /dev/null @@ -1,4 +0,0 @@ -nopause -======= -Disables pausing (both manual and automatic) with the exception of pause forced -by `reveal` ``hell``. This is nice for digging under rivers. diff --git a/docs/plugins/plant.rst b/docs/plugins/plants.rst similarity index 97% rename from docs/plugins/plant.rst rename to docs/plugins/plants.rst index b621b890d..0111b0770 100644 --- a/docs/plugins/plant.rst +++ b/docs/plugins/plants.rst @@ -1,3 +1,5 @@ +.. _plant: + plant ===== A tool for creating shrubs, growing, or getting rid of them. diff --git a/docs/plugins/prospect.rst b/docs/plugins/prospect.rst deleted file mode 100644 index a8979f1cc..000000000 --- a/docs/plugins/prospect.rst +++ /dev/null @@ -1,51 +0,0 @@ -prospect -======== - -**Usage:** - - ``prospect [all|hell] []`` - -Shows a summary of resources that exist on the map. By default, only the visible -part of the map is scanned. Include the ``all`` keyword if you want ``prospect`` -to scan the whole map as if it were revealed. Use ``hell`` instead of ``all`` if -you also want to see the Z range of HFS tubes in the 'features' report section. - -**Options:** - -:``-h``, ``--help``: - Shows this help text. -:``-s``, ``--show ``: - Shows only the named comma-separated list of report sections. Report section - names are: summary, liquids, layers, features, ores, gems, veins, shrubs, - and trees. If run during pre-embark, only the layers, ores, gems, and veins - report sections are available. -:``-v``, ``--values``: - Includes material value in the output. Most useful for the 'gems' report - section. - -**Examples:** - -``prospect all`` - Shows the entire report for the entire map. - -``prospect hell --show layers,ores,veins`` - Shows only the layers, ores, and other vein stone report sections, and - includes information on HFS tubes when a fort is loaded. - -``prospect all -sores`` - Show only information about ores for the pre-embark or fortress map report. - -**Pre-embark estimate:** - -If prospect is called during the embark selection screen, it displays an -estimate of layer stone availability. If the ``all`` keyword is specified, it -also estimates ores, gems, and vein material. The estimate covers all tiles of -the embark rectangle. - -.. note:: - - The results of pre-embark prospect are an *estimate*, and can at best be - expected to be somewhere within +/- 30% of the true amount; sometimes it - does a lot worse. Especially, it is not clear how to precisely compute how - many soil layers there will be in a given embark tile, so it can report a - whole extra layer, or omit one that is actually present. diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index a8979f1cc..03dd193f8 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -1,3 +1,5 @@ +.. _prospect: + prospect ======== diff --git a/docs/plugins/rb.rst b/docs/plugins/rb.rst deleted file mode 100644 index 359d6149b..000000000 --- a/docs/plugins/rb.rst +++ /dev/null @@ -1,4 +0,0 @@ -ruby -==== -Ruby language plugin, which evaluates the following arguments as a ruby string. -Best used as ``:rb [string]``, for the special parsing mode. Alias ``rb_eval``. diff --git a/docs/plugins/restrictice.rst b/docs/plugins/restrictice.rst deleted file mode 100644 index 558fb5679..000000000 --- a/docs/plugins/restrictice.rst +++ /dev/null @@ -1,4 +0,0 @@ -restrictice -=========== -Restrict traffic on all tiles on top of visible ice. -See also `alltraffic`, `filltraffic`, and `restrictliquids`. diff --git a/docs/plugins/restrictliquids.rst b/docs/plugins/restrictliquids.rst deleted file mode 100644 index 4f6cd9550..000000000 --- a/docs/plugins/restrictliquids.rst +++ /dev/null @@ -1,4 +0,0 @@ -restrictliquids -=============== -Restrict traffic on all visible tiles with liquid. -See also `alltraffic`, `filltraffic`, and `restrictice`. diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index da7be8277..42f490aa4 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -1,3 +1,5 @@ +.. _revflood: + reveal ====== This reveals the map. By default, HFS will remain hidden so that the demons @@ -21,3 +23,8 @@ Usage and related commands: :revforget: Discard info about what was visible before revealing the map. Only useful where (e.g.) you abandoned with the fort revealed and no longer want the data. + +nopause +======= +Disables pausing (both manual and automatic) with the exception of pause forced +by `reveal` ``hell``. This is nice for digging under rivers. diff --git a/docs/plugins/revflood.rst b/docs/plugins/revflood.rst deleted file mode 100644 index da7be8277..000000000 --- a/docs/plugins/revflood.rst +++ /dev/null @@ -1,23 +0,0 @@ -reveal -====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. - -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. - -Usage and related commands: - -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. diff --git a/docs/plugins/revforget.rst b/docs/plugins/revforget.rst deleted file mode 100644 index da7be8277..000000000 --- a/docs/plugins/revforget.rst +++ /dev/null @@ -1,23 +0,0 @@ -reveal -====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. - -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. - -Usage and related commands: - -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. diff --git a/docs/plugins/revtoggle.rst b/docs/plugins/revtoggle.rst deleted file mode 100644 index da7be8277..000000000 --- a/docs/plugins/revtoggle.rst +++ /dev/null @@ -1,23 +0,0 @@ -reveal -====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. - -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. - -Usage and related commands: - -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst index 359d6149b..bdd5521a4 100644 --- a/docs/plugins/ruby.rst +++ b/docs/plugins/ruby.rst @@ -1,3 +1,5 @@ +.. _rb: + ruby ==== Ruby language plugin, which evaluates the following arguments as a ruby string. diff --git a/docs/plugins/sort-items.rst b/docs/plugins/sort-items.rst deleted file mode 100644 index d18f33c4e..000000000 --- a/docs/plugins/sort-items.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _sort: - -sort-items -========== -Sort the visible item list:: - - sort-items order [order...] - -Sort the item list using the given sequence of comparisons. -The ``<`` prefix for an order makes undefined values sort first. -The ``>`` prefix reverses the sort order for defined values. - -Item order examples:: - - description material wear type quality - -The orderings are defined in ``hack/lua/plugins/sort/*.lua`` diff --git a/docs/plugins/sort-units.rst b/docs/plugins/sort.rst similarity index 52% rename from docs/plugins/sort-units.rst rename to docs/plugins/sort.rst index 1a88381ce..cc86b3ddd 100644 --- a/docs/plugins/sort-units.rst +++ b/docs/plugins/sort.rst @@ -1,3 +1,19 @@ +sort-items +========== +Sort the visible item list:: + + sort-items order [order...] + +Sort the item list using the given sequence of comparisons. +The ``<`` prefix for an order makes undefined values sort first. +The ``>`` prefix reverses the sort order for defined values. + +Item order examples:: + + description material wear type quality + +The orderings are defined in ``hack/lua/plugins/sort/*.lua`` + sort-units ========== Sort the visible unit list:: diff --git a/docs/plugins/tiletypes-command.rst b/docs/plugins/tiletypes-command.rst deleted file mode 100644 index 0f75c633e..000000000 --- a/docs/plugins/tiletypes-command.rst +++ /dev/null @@ -1,10 +0,0 @@ -tiletypes-command ------------------ -Runs tiletypes commands, separated by ``;``. This makes it possible to change -tiletypes modes from a hotkey or via dfhack-run. - -Example:: - - tiletypes-command p any ; p s wall ; p sp normal - -This resets the paint filter to unsmoothed walls. diff --git a/docs/plugins/tiletypes-here-point.rst b/docs/plugins/tiletypes-here-point.rst deleted file mode 100644 index 8951bdff8..000000000 --- a/docs/plugins/tiletypes-here-point.rst +++ /dev/null @@ -1,6 +0,0 @@ -tiletypes-here-point --------------------- -Apply the current tiletypes options at the in-game cursor position to a single -tile. Can be used from a hotkey. - -This command supports the same options as `tiletypes-here` above. diff --git a/docs/plugins/tiletypes-here.rst b/docs/plugins/tiletypes-here.rst deleted file mode 100644 index fe9ceefd9..000000000 --- a/docs/plugins/tiletypes-here.rst +++ /dev/null @@ -1,14 +0,0 @@ -tiletypes-here --------------- -Apply the current tiletypes options at the in-game cursor position, including -the brush. Can be used from a hotkey. - -Options: - -:``-c``, ``--cursor ,,``: - Use the specified map coordinates instead of the current cursor position. If - this option is specified, then an active game map cursor is not necessary. -:``-h``, ``--help``: - Show command help text. -:``-q``, ``--quiet``: - Suppress non-error status output. diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 41f780521..74af00240 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -1,3 +1,6 @@ +.. _tiletypes-here: +.. _tiletypes-here-point: + tiletypes ========= Can be used for painting map tiles and is an interactive command, much like @@ -80,3 +83,36 @@ The range starts at the position of the cursor and goes to the east, south and up. For more details, use ``tiletypes help``. + +tiletypes-command +----------------- +Runs tiletypes commands, separated by ``;``. This makes it possible to change +tiletypes modes from a hotkey or via dfhack-run. + +Example:: + + tiletypes-command p any ; p s wall ; p sp normal + +This resets the paint filter to unsmoothed walls. + +tiletypes-here-point +-------------------- +Apply the current tiletypes options at the in-game cursor position to a single +tile. Can be used from a hotkey. + +This command supports the same options as `tiletypes-here` above. + +tiletypes-here +-------------- +Apply the current tiletypes options at the in-game cursor position, including +the brush. Can be used from a hotkey. + +Options: + +:``-c``, ``--cursor ,,``: + Use the specified map coordinates instead of the current cursor position. If + this option is specified, then an active game map cursor is not necessary. +:``-h``, ``--help``: + Show command help text. +:``-q``, ``--quiet``: + Suppress non-error status output. diff --git a/docs/plugins/unreveal.rst b/docs/plugins/unreveal.rst deleted file mode 100644 index da7be8277..000000000 --- a/docs/plugins/unreveal.rst +++ /dev/null @@ -1,23 +0,0 @@ -reveal -====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. - -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. - -Usage and related commands: - -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index 17db57e69..f6b8c4475 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -111,3 +111,9 @@ Make sure there are always 80-100 units of dimple dye:: Maintain 10-100 locally-made crafts of exceptional quality:: workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90 + +fix-job-postings +---------------- +This command fixes crashes caused by previous versions of workflow, mostly in +DFHack 0.40.24-r4, and should be run automatically when loading a world (but can +also be run manually if desired). From 4132dbdbbb771189025c39bfb36f899741068112 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 06:51:40 -0700 Subject: [PATCH 124/334] remove docs for no-command, un-enableable plugin it's already documented in lua-api --- docs/plugins/building-hacks.rst | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 docs/plugins/building-hacks.rst diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst deleted file mode 100644 index bdae83392..000000000 --- a/docs/plugins/building-hacks.rst +++ /dev/null @@ -1,13 +0,0 @@ -building-hacks -============== -Tags: - -:index:`Allows mods to create and manage powered workshops. -` - -Usage:: - - enable building-hacks - -Please see the `building-hacks-api` for information on accessing this plugin -from Lua scripts. From 6b1c0b5308fcec549ef07dc8269f51f14827ee7e Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 06:53:08 -0700 Subject: [PATCH 125/334] rename building-hacks section --- docs/Lua API.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index aba0bcab7..107a0ba2e 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -4287,10 +4287,10 @@ blueprint files: The names of the functions are also available as the keys of the ``valid_phases`` table. -.. _building-hacks-api: +.. _building-hacks: -building-hacks API -================== +building-hacks +============== This plugin overwrites some methods in workshop df class so that mechanical workshops are possible. Although plugin export a function it's recommended to use lua decorated function. From 38c17b5215b5cc241157c227375edd755e21bc17 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 06:56:21 -0700 Subject: [PATCH 126/334] update docs for RemoteFortressReader --- docs/plugins/RemoteFortressReader.rst | 20 ++++++++++++++++--- .../remotefortressreader.cpp | 8 +++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index 287debca8..103c904db 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -1,4 +1,18 @@ -remotefortressreader +RemoteFortressReader ==================== -An in-development plugin for realtime fortress visualisation. -See :forums:`Armok Vision <146473>`. +Tags: +:dfhack-keybind:`RemoteFortressReader_version` +:dfhack-keybind:`load-art-image-chunk` + +:index:`Backend for Armok Vision. +` Provides an API for realtime +remote fortress visualization. See :forums:`Armok Vision <146473>`. + +Usage: + +``enable RemoteFortressReader`` + Enable the plugin. +``RemoteFortressReader_version`` + Print the loaded RemoteFortressReader version. +``load-art-image-chunk `` + Gets an art image chunk by index, loading from disk if necessary. diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index 9ab9a7502..643e92b21 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -221,12 +221,14 @@ DFHACK_PLUGIN_IS_ENABLED(enableUpdates); // Mandatory init function. If you have some global state, create it here. DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("RemoteFortressReader_version", "List the loaded RemoteFortressReader version", RemoteFortressReader_version, false, "This is used for plugin version checking.")); + commands.push_back(PluginCommand( + "RemoteFortressReader_version", + "List the loaded RemoteFortressReader version", + RemoteFortressReader_version)); commands.push_back(PluginCommand( "load-art-image-chunk", "Gets an art image chunk by index, loading from disk if necessary", - loadArtImageChunk, false, - "Usage: load_art_image_chunk N, where N is the id of the chunk to get.")); + loadArtImageChunk)); enableUpdates = true; return CR_OK; } From 886289d805af845ba8d7bc50afd8d804c8d74abc Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:21:33 -0700 Subject: [PATCH 127/334] update docs for fix-armory --- docs/plugins/fix-armory.rst | 12 ++++++++++-- plugins/fix-armory.cpp | 9 +++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/plugins/fix-armory.rst b/docs/plugins/fix-armory.rst index f4b988420..dfc5eef7f 100644 --- a/docs/plugins/fix-armory.rst +++ b/docs/plugins/fix-armory.rst @@ -1,4 +1,12 @@ fix-armory ========== -`This plugin requires a binpatch `, which has not -been available since DF 0.34.11 +Tags: +:dfhack-keybind:`fix-armory` + +Allow the military to store equipment in barracks. However, +`this plugin requires a binpatch `, which has not +been available since DF 0.34.11. + +Usage:: + + enable fix-armory diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index 0f63b4d93..7dfefff0f 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -70,12 +70,9 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "fix-armory", "Enables or disables the fix-armory plugin.", fix_armory, false, - " fix-armory enable\n" - " Enables the tweaks.\n" - " fix-armory disable\n" - " Disables the tweaks. All equipment will be hauled off to stockpiles.\n" - )); + "fix-armory", + "Enables or disables the fix-armory plugin.", + fix_armory)); if (Core::getInstance().isMapLoaded()) plugin_onstatechange(out, SC_MAP_LOADED); From a6cb79c2371debb0d763715dd63ff14a178ed1f9 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:21:59 -0700 Subject: [PATCH 128/334] remove defunct tool fix-armory --- docs/plugins/fix-armory.rst | 12 - plugins/CMakeLists.txt | 1 - plugins/fix-armory.cpp | 840 ------------------------------------ 3 files changed, 853 deletions(-) delete mode 100644 docs/plugins/fix-armory.rst delete mode 100644 plugins/fix-armory.cpp diff --git a/docs/plugins/fix-armory.rst b/docs/plugins/fix-armory.rst deleted file mode 100644 index dfc5eef7f..000000000 --- a/docs/plugins/fix-armory.rst +++ /dev/null @@ -1,12 +0,0 @@ -fix-armory -========== -Tags: -:dfhack-keybind:`fix-armory` - -Allow the military to store equipment in barracks. However, -`this plugin requires a binpatch `, which has not -been available since DF 0.34.11. - -Usage:: - - enable fix-armory diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 283f74fad..3b7ae17f1 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -120,7 +120,6 @@ if(BUILD_SUPPORTED) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(fastdwarf fastdwarf.cpp) dfhack_plugin(filltraffic filltraffic.cpp) - dfhack_plugin(fix-armory fix-armory.cpp) dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) dfhack_plugin(fixveins fixveins.cpp) dfhack_plugin(flows flows.cpp) diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp deleted file mode 100644 index 7dfefff0f..000000000 --- a/plugins/fix-armory.cpp +++ /dev/null @@ -1,840 +0,0 @@ -// Fixes containers in barracks to actually work as intended. - -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" - -#include "modules/Gui.h" -#include "modules/Screen.h" -#include "modules/Units.h" -#include "modules/Items.h" -#include "modules/Job.h" -#include "modules/World.h" -#include "modules/Maps.h" - -#include "MiscUtils.h" - -#include "DataDefs.h" -#include -#include "df/ui.h" -#include "df/world.h" -#include "df/squad.h" -#include "df/unit.h" -#include "df/squad_position.h" -#include "df/squad_ammo_spec.h" -#include "df/items_other_id.h" -#include "df/item_weaponst.h" -#include "df/item_armorst.h" -#include "df/item_helmst.h" -#include "df/item_pantsst.h" -#include "df/item_shoesst.h" -#include "df/item_glovesst.h" -#include "df/item_shieldst.h" -#include "df/item_flaskst.h" -#include "df/item_backpackst.h" -#include "df/item_quiverst.h" -#include "df/item_ammost.h" -#include "df/building_weaponrackst.h" -#include "df/building_armorstandst.h" -#include "df/building_cabinetst.h" -#include "df/building_boxst.h" -#include "df/building_squad_use.h" -#include "df/job.h" -#include "df/general_ref_building_holderst.h" -#include "df/general_ref_building_destinationst.h" -#include "df/barrack_preference_category.h" - -#include - -using std::vector; -using std::string; -using std::endl; -using namespace DFHack; -using namespace df::enums; - -using df::global::ui; -using df::global::world; -using df::global::gamemode; -using df::global::ui_build_selector; - -using namespace DFHack::Gui; -using Screen::Pen; - -static command_result fix_armory(color_ostream &out, vector & parameters); - -DFHACK_PLUGIN("fix-armory"); - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event); - -DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) -{ - commands.push_back(PluginCommand( - "fix-armory", - "Enables or disables the fix-armory plugin.", - fix_armory)); - - if (Core::getInstance().isMapLoaded()) - plugin_onstatechange(out, SC_MAP_LOADED); - - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown (color_ostream &out) -{ - return CR_OK; -} - -/* - * PART 1 - Stop restockpiling of items stored in the armory. - * - * For everything other than ammo this is quite straightforward, - * since the uniform switch code already tries to store items - * in barracks containers, and it is thus known what the intention - * is. Moreover these containers know which squad and member they - * belong to. - * - * For ammo there is no such code (in fact, ammo is never removed - * from a quiver, except when it is dropped itself), so I had to - * apply some improvisation. There is one place where BOX containers - * with Squad Equipment set are used as an anchor location for a - * pathfinding check when assigning ammo, so presumably that's - * the correct place. I however wanted to also differentiate - * training ammo, so came up with the following rules: - * - * 1. Combat ammo and ammo without any allowed use can be stored - * in BOXes marked for Squad Equipment, either directly or via - * containing room. No-allowed-use ammo is assumed to be reserved - * for emergency combat use, or something like that; however if - * it is already stored in a training chest, it won't be moved. - * 1a. If assigned to a squad position, that box can be used _only_ - * for ammo assigned to that specific _squad_. Otherwise, if - * multiple squads can use this room, they will store their - * ammo all mixed up. - * 2. Training ammo can be stored in BOXes within archery ranges - * (designated from archery target) that are enabled for Training. - * Train-only ammo in particular can _only_ be stored in such - * boxes. The inspiration for this comes from some broken code - * for weapon racks in Training rooms. - * - * As an additional feature (partially needed due to the constraints - * of working from an external hack), this plugin also blocks instant - * queueing of stockpiling jobs for items blocked on the ground, if - * these items are assigned to any squad. - * - * Since there apparently still are bugs that cause uniform items to be - * momentarily dropped on ground, this delay is set not to the minimally - * necessary 50 ticks, but to 0.5 - 1.0 in-game days, so as to provide a - * grace period during which the items can be instantly picked up again. - */ - -// Completely block the use of stockpiles -#define NO_STOCKPILES - -// Check if the item is assigned to any use controlled by the military tab -static bool is_assigned_item(df::item *item) -{ - if (!ui) - return false; - - auto type = item->getType(); - int idx = binsearch_index(ui->equipment.items_assigned[type], item->id); - if (idx < 0) - return false; - - return true; -} - -// Check if this ammo item is assigned to this squad with one of the specified uses -static bool is_squad_ammo(df::item *item, df::squad *squad, bool combat, bool train) -{ - for (size_t i = 0; i < squad->ammunition.size(); i++) - { - auto spec = squad->ammunition[i]; - bool cs = spec->flags.bits.use_combat; - bool ts = spec->flags.bits.use_training; - - // no-use ammo assumed to fit any category - if (((cs || !ts) && combat) || ((ts || !cs) && train)) - { - if (binsearch_index(spec->assigned, item->id) >= 0) - return true; - } - } - - return false; -} - -// Recursively check room parents to find out if this ammo item is allowed here -static bool can_store_ammo_rec(df::item *item, df::building *holder, int squad_id) -{ - auto squads = holder->getSquads(); - - if (squads) - { - for (size_t i = 0; i < squads->size(); i++) - { - auto use = (*squads)[i]; - - // For containers assigned to a squad, only consider that squad - if (squad_id >= 0 && use->squad_id != squad_id) - continue; - - // Squad Equipment -> combat - bool combat = use->mode.bits.squad_eq; - bool train = false; - - if (combat || train) - { - auto squad = df::squad::find(use->squad_id); - - if (squad && is_squad_ammo(item, squad, combat, train)) - return true; - } - } - } - // Ugh, archery targets don't actually have a squad use vector - else if (holder->getType() == building_type::ArcheryTarget) - { - auto &squads = world->squads.all; - - for (size_t si = 0; si < squads.size(); si++) - { - auto squad = squads[si]; - - // For containers assigned to a squad, only consider that squad - if (squad_id >= 0 && squad->id != squad_id) - continue; - - for (size_t j = 0; j < squad->rooms.size(); j++) - { - auto use = squad->rooms[j]; - - if (use->building_id != holder->id) - continue; - - // Squad Equipment -> combat - bool combat = use->mode.bits.squad_eq; - // Archery target with Train -> training - bool train = use->mode.bits.train; - - if (combat || train) - { - if (is_squad_ammo(item, squad, combat, train)) - return true; - } - - break; - } - } - } - - for (size_t i = 0; i < holder->parents.size(); i++) - if (can_store_ammo_rec(item, holder->parents[i], squad_id)) - return true; - - return false; -} - -// Check if the ammo item can be stored in this container -static bool can_store_ammo(df::item *item, df::building *holder) -{ - // Only chests - if (holder->getType() != building_type::Box) - return false; - - // with appropriate flags set - return can_store_ammo_rec(item, holder, holder->getSpecificSquad()); -} - -// Check if the item is assigned to the squad member who owns this armory building -static bool belongs_to_position(df::item *item, df::building *holder) -{ - int sid = holder->getSpecificSquad(); - if (sid < 0) - return false; - - auto squad = df::squad::find(sid); - if (!squad) - return false; - - int position = holder->getSpecificPosition(); - - // Weapon racks belong to the whole squad, i.e. can be used by any position - if (position == -1 && holder->getType() == building_type::Weaponrack) - { - for (size_t i = 0; i < squad->positions.size(); i++) - { - if (binsearch_index(squad->positions[i]->assigned_items, item->id) >= 0) - return true; - } - } - else - { - auto cpos = vector_get(squad->positions, position); - if (cpos && binsearch_index(cpos->assigned_items, item->id) >= 0) - return true; - } - - return false; -} - -// Check if the item is appropriately stored in an armory building -static bool is_in_armory(df::item *item) -{ - if (item->flags.bits.in_inventory || item->flags.bits.on_ground) - return false; - - auto holder = Items::getHolderBuilding(item); - if (!holder) - return false; - - // If indeed in a building, check if it is the right one - if (item->getType() == item_type::AMMO) - return can_store_ammo(item, holder); - else - return belongs_to_position(item, holder); -} - -/* - * Hooks used to affect stockpiling code as it runs, and prevent it - * from doing unwanted stuff. - * - * Toady can simply add these checks directly to the stockpiling code; - * we have to abuse some handy item vmethods. - */ - -template struct armory_hook : Item { - typedef Item interpose_base; - - /* - * This vmethod is called by the actual stockpiling code before it - * tries to queue a job, and is normally used to prevent stockpiling - * of uncollected webs. - */ - DEFINE_VMETHOD_INTERPOSE(bool, isCollected, ()) - { -#ifdef NO_STOCKPILES - /* - * Completely block any items assigned to a squad from being stored - * in stockpiles. The reason is that I still observe haulers running - * around with bins to pick them up for some reason. There could be - * some unaccounted race conditions involved. - */ - if (is_assigned_item(this)) - return false; -#else - // Block stockpiling of items in the armory. - if (is_in_armory(this)) - return false; - - /* - * When an item is removed from inventory due to Pickup Equipment - * process, the unit code directly invokes the stockpiling code - * and thus creates the job even before the item is actually dropped - * on the ground. We don't want this at all, especially due to the - * grace period idea. - * - * With access to source, that code can just be changed to simply - * drop the item on ground, without running stockpiling code. - */ - if (this->flags.bits.in_inventory) - { - auto holder = Items::getHolderUnit(this); - - // When that call happens, the item is still in inventory - if (holder && is_assigned_item(this)) - { - // And its ID is is this vector - if (::binsearch_index(holder->military.uniform_drop, this->id) >= 0) - return false; - } - } -#endif - - // Call the original vmethod - return INTERPOSE_NEXT(isCollected)(); - } - - /* - * This vmethod is used to actually put the item on the ground. - * When it does that, it also adds it to a vector of items to be - * instanly restockpiled by a loop in another part of the code. - * - * We don't want this either, even more than when removing from - * uniform, because this can happen in lots of various situations, - * including deconstructed containers etc, and we want our own - * armory code below to have a chance to look at the item. - * - * The logical place for this code is in the loop that processes - * that vector, but that part is not virtual. - */ - DEFINE_VMETHOD_INTERPOSE(bool, moveToGround, (int16_t x, int16_t y, int16_t z)) - { - // First, let it do its work - bool rv = INTERPOSE_NEXT(moveToGround)(x, y, z); - - // Prevent instant restockpiling of dropped assigned items. - if (is_assigned_item(this)) - { - // The original vmethod adds the item to this vector to force instant check - auto &ovec = world->items.other[items_other_id::ANY_RECENTLY_DROPPED]; - - // If it is indeed there, remove it - if (erase_from_vector(ovec, &df::item::id, this->id)) - { - // and queue it to be checked normally in 0.5-1 in-game days - // (this is a grace period in case the uniform is dropped just - // for a moment due to a momentary glitch) - this->stockpile_countdown = 12 + random_int(12); - this->stockpile_delay = 0; - } - } - - return rv; - } -}; - -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, isCollected); - -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); -template<> IMPLEMENT_VMETHOD_INTERPOSE(armory_hook, moveToGround); - -/* - * PART 2 - Actively queue jobs to haul assigned items to the armory. - * - * The logical place for this of course is in the same code that decides - * to put stuff in stockpiles, alongside the checks to prevent moving - * stuff away from the armory. We just run it independently every 50 - * simulation frames. - */ - -// Check if this item is loose and can be moved to armory -static bool can_store_item(df::item *item) -{ - // bad id, or cooldown timer still counting - if (!item || item->stockpile_countdown > 0) - return false; - - // bad flags? - if (item->flags.bits.in_job || - item->flags.bits.removed || - item->flags.bits.in_building || - item->flags.bits.encased || - item->flags.bits.owned || - item->flags.bits.forbid || - item->flags.bits.on_fire) - return false; - - // in unit inventory? - auto top = item; - - while (top->flags.bits.in_inventory) - { - auto parent = Items::getContainer(top); - if (!parent) break; - top = parent; - } - - if (Items::getGeneralRef(top, general_ref_type::UNIT_HOLDER)) - return false; - - // already in armory? - if (is_in_armory(item)) - return false; - - return true; -} - -// Queue a job to store the item in the building, if possible -static bool try_store_item(df::building *target, df::item *item) -{ - // Check if the dwarves can path between the target and the item - df::coord tpos(target->centerx, target->centery, target->z); - df::coord ipos = Items::getPosition(item); - - if (!Maps::canWalkBetween(tpos, ipos)) - return false; - - // Check if the target has enough space left - if (!target->canStoreItem(item, true)) - return false; - - // Create the job - auto href = df::allocate(); - if (!href) - return false; - - auto job = new df::job(); - - job->pos = tpos; - - bool dest = false; - - // Choose the job type - correct matching is needed so that - // later canStoreItem calls would take the job into account. - switch (target->getType()) { - case building_type::Weaponrack: - job->job_type = job_type::StoreWeapon; - // Without this flag dwarves will pick up the item, and - // then dismiss the job and put it back into the stockpile: - job->flags.bits.specific_dropoff = true; - break; - case building_type::Armorstand: - job->job_type = job_type::StoreArmor; - job->flags.bits.specific_dropoff = true; - break; - case building_type::Cabinet: - job->job_type = job_type::StoreOwnedItem; - dest = true; - break; - default: - job->job_type = job_type::StoreItemInHospital; - dest = true; - break; - } - - // job <-> item link - if (!Job::attachJobItem(job, item, df::job_item_ref::Hauled)) - { - delete job; - delete href; - return false; - } - - // job <-> building link - href->building_id = target->id; - target->jobs.push_back(job); - job->general_refs.push_back(href); - - // Two of the jobs need this link to find the job in canStoreItem(). - // They also don't actually need BUILDING_HOLDER, but it doesn't hurt. - if (dest) - { - auto rdest = df::allocate(); - - if (rdest) - { - rdest->building_id = target->id; - job->general_refs.push_back(rdest); - } - } - - // add to job list - Job::linkIntoWorld(job); - return true; -} - -// Store the item into the first building in the list that would accept it. -static void try_store_item(std::vector &vec, df::item *item) -{ - for (size_t i = 0; i < vec.size(); i++) - { - auto target = df::building::find(vec[i]); - if (!target) - continue; - - if (try_store_item(target, item)) - return; - } -} - -// Store the items into appropriate armory buildings -static void try_store_item_set(std::vector &items, df::squad *squad, df::squad_position *pos) -{ - for (size_t j = 0; j < items.size(); j++) - { - auto item = df::item::find(items[j]); - - // not loose - if (!can_store_item(item)) - continue; - - // queue jobs to put it in the appropriate container - if (item->isWeapon()) - try_store_item(squad->rack_combat, item); - else if (item->isClothing()) - try_store_item(pos->preferences[barrack_preference_category::Cabinet], item); - else if (item->isArmorNotClothing()) - try_store_item(pos->preferences[barrack_preference_category::Armorstand], item); - else - try_store_item(pos->preferences[barrack_preference_category::Box], item); - } -} - -// For storing ammo, use a data structure sorted by free space, to even out the load -typedef std::map > ammo_box_set; - -// Enumerate boxes in the room, adding them to the set -static void index_boxes(df::building *root, ammo_box_set &group, int squad_id) -{ - if (root->getType() == building_type::Box) - { - int id = root->getSpecificSquad(); - - if (id < 0 || id == squad_id) - { - //color_ostream_proxy out(Core::getInstance().getConsole()); - //out.print("%08x %d\n", unsigned(root), root->getFreeCapacity(true)); - - group[root->getFreeCapacity(true)].insert(root); - } - } - - for (size_t i = 0; i < root->children.size(); i++) - index_boxes(root->children[i], group, squad_id); -} - -// Loop over the set from most empty to least empty -static bool try_store_ammo(df::item *item, ammo_box_set &group) -{ - int volume = item->getVolume(); - - for (auto it = group.rbegin(); it != group.rend(); ++it) - { - if (it->first < volume) - break; - - for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) - { - auto bld = *it2; - - if (try_store_item(bld, item)) - { - it->second.erase(bld); - group[bld->getFreeCapacity(true)].insert(bld); - return true; - } - } - } - - return false; -} - -// Collect chests for ammo storage -static void index_ammo_boxes(df::squad *squad, ammo_box_set &train_set, ammo_box_set &combat_set) -{ - for (size_t j = 0; j < squad->rooms.size(); j++) - { - auto room = squad->rooms[j]; - auto bld = df::building::find(room->building_id); - - // Chests in rooms marked for Squad Equipment used for combat ammo - if (room->mode.bits.squad_eq) - index_boxes(bld, combat_set, squad->id); - - // Chests in archery ranges used for training ammo - if (room->mode.bits.train && bld->getType() == building_type::ArcheryTarget) - index_boxes(bld, train_set, squad->id); - } -} - -// Store ammo into appropriate chests -static void try_store_ammo(df::squad *squad) -{ - bool indexed = false; - ammo_box_set train_set, combat_set; - - for (size_t i = 0; i < squad->ammunition.size(); i++) - { - auto spec = squad->ammunition[i]; - bool cs = spec->flags.bits.use_combat; - bool ts = spec->flags.bits.use_training; - - for (size_t j = 0; j < spec->assigned.size(); j++) - { - auto item = df::item::find(spec->assigned[j]); - - // not loose - if (!can_store_item(item)) - continue; - - // compute the maps lazily - if (!indexed) - { - indexed = true; - index_ammo_boxes(squad, train_set, combat_set); - } - - // BUG: if the same container is in both sets, - // when a job is queued, the free space in the other - // set will not be updated, which could lead to uneven - // loading - but not to overflowing the container! - - // As said above, combat goes into Squad Equipment - if (cs && try_store_ammo(item, combat_set)) - continue; - // Training goes into Archery Range with Train - if (ts && try_store_ammo(item, train_set)) - continue; - // No use goes into combat - if (!(ts || cs) && try_store_ammo(item, combat_set)) - continue; - } - } -} - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -DFhackCExport command_result plugin_onupdate(color_ostream &out, state_change_event event) -{ - if (!is_enabled) - return CR_OK; - - // Process every 50th frame, sort of like regular stockpiling does - if (DF_GLOBAL_VALUE(cur_year_tick,1) % 50 != 0) - return CR_OK; - - // Loop over squads - auto &squads = world->squads.all; - - for (size_t si = 0; si < squads.size(); si++) - { - auto squad = squads[si]; - - for (size_t i = 0; i < squad->positions.size(); i++) - { - auto pos = squad->positions[i]; - - try_store_item_set(pos->assigned_items, squad, pos); - } - - try_store_ammo(squad); - } - - return CR_OK; -} - -static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, bool enable) -{ - if (!hook.apply(enable)) - out.printerr("Could not %s hook.\n", enable?"activate":"deactivate"); -} - -static void enable_hooks(color_ostream &out, bool enable) -{ - is_enabled = enable; - - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, isCollected), enable); - - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); - enable_hook(out, INTERPOSE_HOOK(armory_hook, moveToGround), enable); -} - -static void enable_plugin(color_ostream &out) -{ - auto entry = World::GetPersistentData("fix-armory/enabled", NULL); - if (!entry.isValid()) - { - out.printerr("Could not save the status.\n"); - return; - } - - enable_hooks(out, true); -} - -static void disable_plugin(color_ostream &out) -{ - auto entry = World::GetPersistentData("fix-armory/enabled"); - World::DeletePersistentData(entry); - - enable_hooks(out, false); -} - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_MAP_LOADED: - if (!gamemode || *gamemode == game_mode::DWARF) - { - bool enable = World::GetPersistentData("fix-armory/enabled").isValid(); - - if (enable) - { - out.print("Enabling the fix-armory plugin.\n"); - enable_hooks(out, true); - } - else - enable_hooks(out, false); - } - break; - case SC_MAP_UNLOADED: - enable_hooks(out, false); - default: - break; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (!Core::getInstance().isWorldLoaded()) { - out.printerr("World is not loaded: please load a game first.\n"); - return CR_FAILURE; - } - - if (enable) - enable_plugin(out); - else - disable_plugin(out); - - return CR_OK; -} - -static command_result fix_armory(color_ostream &out, vector ¶meters) -{ - CoreSuspender suspend; - - if (parameters.empty()) - return CR_WRONG_USAGE; - - string cmd = parameters[0]; - - if (cmd == "enable") - return plugin_enable(out, true); - else if (cmd == "disable") - return plugin_enable(out, false); - else - return CR_WRONG_USAGE; - - return CR_OK; -} From 989befa5824bd1ef54fecb5589c933e1409fc994 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:22:23 -0700 Subject: [PATCH 129/334] update docs for fastdwarf --- docs/plugins/fastdwarf.rst | 46 ++++++++++++++++++++++++++++---------- plugins/fastdwarf.cpp | 18 ++++----------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index 65d2a3d45..d964b319a 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -1,14 +1,36 @@ fastdwarf ========= -Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly -and perform tasks quickly. Teledwarf makes dwarves move instantaneously, -but do jobs at the same speed. - -:fastdwarf 0: disables both (also ``0 0``) -:fastdwarf 1: enables speedydwarf and disables teledwarf (also ``1 0``) -:fastdwarf 2: sets a native debug flag in the game memory that implements an - even more aggressive version of speedydwarf. -:fastdwarf 0 1: disables speedydwarf and enables teledwarf -:fastdwarf 1 1: enables both - -See `superdwarf` for a per-creature version. +Tags: +:dfhack-keybind:`fastdwarf` + +Dwarves teleport and/or finish jobs instantly. + +Usage: + +``enable fastdwarf`` + Enable the plugin. +``fastdwarf []`` + +Examples +-------- + +``fastdwarf 1`` + Make all your dwarves move and work at maximum speed. +``fastdwarf 1 1`` + In addition to working at maximum speed, dwarves also teleport to their + destinations. + +Options +------- + +Speed modes: + +:0: Dwarves move and work at normal rates. +:1: Dwarves move and work at maximum speed. +:2: ALL units move (and work) at maximum speed, including creatures and + hostiles. + +Tele modes: + +:0: No teleportation. +:1: Dwarves teleport to their destinations. diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 3948d8577..787f85c5d 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -224,20 +224,10 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("fastdwarf", - "let dwarves teleport and/or finish jobs instantly", - fastdwarf, false, - "fastdwarf: make dwarves faster.\n" - "Usage:\n" - " fastdwarf (tele)\n" - "Valid values for speed:\n" - " * 0 - Make dwarves move and work at standard speed.\n" - " * 1 - Make dwarves move and work at maximum speed.\n" - " * 2 - Make ALL creatures move and work at maximum speed.\n" - "Valid values for (tele):\n" - " * 0 - Disable dwarf teleportation (default)\n" - " * 1 - Make dwarves teleport to their destinations instantly.\n" - )); + commands.push_back(PluginCommand( + "fastdwarf", + "Dwarves teleport and/or finish jobs instantly.", + fastdwarf)); return CR_OK; } From ab9c3a07c4d83769e0e5bb3dc3952dec955d0b7d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:22:42 -0700 Subject: [PATCH 130/334] add missing 'enable' usage for dwarfmonitor --- docs/plugins/dwarfmonitor.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index 6c1472c7d..75b71d06c 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -9,6 +9,8 @@ display widgets with live fort statistics. Usage: +``enable dwarfmonitor`` + Enable the plugin. ``dwarfmonitor enable `` Start tracking a specific facet of fortress life. The ``mode`` can be "work", "misery", "date", "weather", or "all". This will show the From 2654de583f13ecc080475ad45fc127be2dedd273 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:23:05 -0700 Subject: [PATCH 131/334] update docs for filltraffic --- docs/plugins/filltraffic.rst | 58 ++++++++++++++++++------------------ plugins/filltraffic.cpp | 44 ++++++++------------------- 2 files changed, 41 insertions(+), 61 deletions(-) diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index d3e952204..5056ec830 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -1,43 +1,43 @@ -.. _alltraffic: .. _restrictice: .. _restrictliquids: filltraffic =========== -Set traffic designations using flood-fill starting at the cursor. -See also `alltraffic`, `restrictice`, and `restrictliquids`. Options: +Tags: +:dfhack-keybind:`` -:H: High Traffic -:N: Normal Traffic -:L: Low Traffic -:R: Restricted Traffic -:X: Fill across z-levels. -:B: Include buildings and stockpiles. -:P: Include empty space. +Usage: + +``filltraffic []`` + Set traffic designations using flood-fill starting at the cursor. Flood + filling stops at walls and doors. +``alltraffic `` + Set traffic designations for every single tile of the map - useful for + resetting traffic designations. +``restrictice`` + Restrict traffic on all tiles on top of visible ice. +``restrictliquids`` + Restrict traffic on all visible tiles with liquid. -Example: +Examples +-------- ``filltraffic H`` - When used in a room with doors, it will set traffic to HIGH in just that room. + When used in a room with doors, it will set traffic to HIGH in just that + room. -alltraffic -========== -Set traffic designations for every single tile of the map - useful for resetting -traffic designations. See also `filltraffic`, `restrictice`, and `restrictliquids`. +Options +------- -Options: +Traffic designations: -:H: High Traffic -:N: Normal Traffic -:L: Low Traffic -:R: Restricted Traffic +:H: High Traffic. +:N: Normal Traffic. +:L: Low Traffic. +:R: Restricted Traffic. -restrictice -=========== -Restrict traffic on all tiles on top of visible ice. -See also `alltraffic`, `filltraffic`, and `restrictliquids`. +Filltraffic extra options: -restrictliquids -=============== -Restrict traffic on all visible tiles with liquid. -See also `alltraffic`, `filltraffic`, and `restrictice`. +:X: Fill across z-levels. +:B: Include buildings and stockpiles. +:P: Include empty space. diff --git a/plugins/filltraffic.cpp b/plugins/filltraffic.cpp index ddb5ef877..781fce0a8 100644 --- a/plugins/filltraffic.cpp +++ b/plugins/filltraffic.cpp @@ -44,41 +44,21 @@ DFHACK_PLUGIN("filltraffic"); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "filltraffic","Flood-fill selected traffic designation from cursor", - filltraffic, Gui::cursor_hotkey, - " Flood-fill selected traffic type from the cursor.\n" - "Traffic Type Codes:\n" - " H: High Traffic\n" - " N: Normal Traffic\n" - " L: Low Traffic\n" - " R: Restricted Traffic\n" - "Other Options:\n" - " X: Fill across z-levels.\n" - " B: Include buildings and stockpiles.\n" - " P: Include empty space.\n" - "Example:\n" - " filltraffic H\n" - " When used in a room with doors,\n" - " it will set traffic to HIGH in just that room.\n" - )); + "filltraffic", + "Flood-fill selected traffic designation from cursor.", + filltraffic, Gui::cursor_hotkey)); commands.push_back(PluginCommand( - "alltraffic","Set traffic for the entire map", - alltraffic, false, - " Set traffic types for all tiles on the map.\n" - "Traffic Type Codes:\n" - " H: High Traffic\n" - " N: Normal Traffic\n" - " L: Low Traffic\n" - " R: Restricted Traffic\n" - )); + "alltraffic", + "Set traffic designation for the entire map.", + alltraffic)); commands.push_back(PluginCommand( - "restrictliquids","Restrict on every visible square with liquid", - restrictLiquid, false, "" - )); + "restrictliquids", + "Restrict traffic on every visible square with liquid.", + restrictLiquid)); commands.push_back(PluginCommand( - "restrictice","Restrict traffic on squares above visible ice", - restrictIce, false, "" - )); + "restrictice", + "Restrict traffic on squares above visible ice.", + restrictIce)); return CR_OK; } From 9c0bd6bd709e7565373cf8fdfaa1e59c99af9577 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:23:26 -0700 Subject: [PATCH 132/334] update docs for fix-unit-occupancy --- docs/plugins/fix-unit-occupancy.rst | 44 ++++++++++++++++++++++------- plugins/fix-unit-occupancy.cpp | 14 ++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index bf999c8a3..48efd7255 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -1,12 +1,36 @@ fix-unit-occupancy ================== -This plugin fixes issues with unit occupancy, notably phantom -"unit blocking tile" messages (:bug:`3499`). It can be run manually, or -periodically when enabled with the built-in enable/disable commands: - -:(no argument): Run the plugin once immediately, for the whole map. -:-h, here, cursor: Run immediately, only operate on the tile at the cursor -:-n, dry, dry-run: Run immediately, do not write changes to map -:interval : Run the plugin every ``X`` ticks (when enabled). - The default is 1200 ticks, or 1 day. - Ticks are only counted when the game is unpaused. +Tags: +:dfhack-keybind:`` + +Fix phantom unit occupancy issues. For example, if you see "unit blocking tile" +messages that you can't account for (:bug:`3499`), this tool can help. + +Usage:: + + enable fix-unit-occupancy + fix-unit-occupancy [here] [-n] + fix-unit-occupancy interval + +When run without arguments (or with just the ``here`` or ``-n`` parameters), +the fix just runs once. You can also have it run periodically by enbling the +plugin. + +Examples +-------- + +``fix-unit-occupancy`` + Run once and fix all occupancy issues on the map. +``fix-unit-occupancy -n`` + Report on, but do not fix, all occupancy issues on the map. + +Options +------- + +``here`` + Only operate on the tile at the cursor. +``-n`` + Report issues, but do not any write changes to the map. +``interval `` + Set how often the plugin will check for and fix issues when it is enabled. + The default is 1200 ticks, or 1 game day. diff --git a/plugins/fix-unit-occupancy.cpp b/plugins/fix-unit-occupancy.cpp index d602ae213..973c185a8 100644 --- a/plugins/fix-unit-occupancy.cpp +++ b/plugins/fix-unit-occupancy.cpp @@ -181,18 +181,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector Date: Mon, 25 Jul 2022 10:54:11 -0700 Subject: [PATCH 133/334] update docs for fixveins --- docs/plugins/fixveins.rst | 12 +++++++++--- plugins/fixveins.cpp | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index 58678d154..940c13b7a 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -1,5 +1,11 @@ fixveins ======== -Removes invalid references to mineral inclusions and restores missing ones. -Use this if you broke your embark with tools like `tiletypes`, or if you -accidentally placed a construction on top of a valuable mineral floor. +Tags: +:dfhack-keybind:`fixveins` + +Restore missing mineral inclusions. This tool can also remove invalid references +to mineral inclusions if you broke your embark with tools like `tiletypes`. + +Usage:: + + fixveins diff --git a/plugins/fixveins.cpp b/plugins/fixveins.cpp index 84d475518..f633f8422 100644 --- a/plugins/fixveins.cpp +++ b/plugins/fixveins.cpp @@ -103,8 +103,9 @@ command_result df_fixveins (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("fixveins", - "Remove invalid references to mineral inclusions and restore missing ones.", + commands.push_back(PluginCommand( + "fixveins", + "Restore missing mineral inclusions.", df_fixveins)); return CR_OK; } From e13aae2ce19e20c37850bac12c2c1e59d1f1803d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:55:04 -0700 Subject: [PATCH 134/334] update docs for flows --- docs/plugins/flows.rst | 12 +++++++++--- plugins/flows.cpp | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index 3df451195..354b0078e 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -1,5 +1,11 @@ flows ===== -A tool for checking how many tiles contain flowing liquids. If you suspect that -your magma sea leaks into HFS, you can use this tool to be sure without -revealing the map. +Tags: +:dfhack-keybind:`flows` + +Counts map blocks with flowing liquids.. If you suspect that your magma sea +leaks into HFS, you can use this tool to be sure without revealing the map. + +Usage:: + + flows diff --git a/plugins/flows.cpp b/plugins/flows.cpp index 070776b10..a17554cb9 100644 --- a/plugins/flows.cpp +++ b/plugins/flows.cpp @@ -59,7 +59,8 @@ command_result df_flows (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("flows", + commands.push_back(PluginCommand( + "flows", "Counts map blocks with flowing liquids.", df_flows)); return CR_OK; From 20ccd3a99cd1937c5fb71c7de7c3a97fb2d9f5ec Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 10:55:31 -0700 Subject: [PATCH 135/334] update docs for follow --- docs/plugins/follow.rst | 13 ++++++++++--- plugins/follow.cpp | 9 ++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst index a91c14f73..a0c15547e 100644 --- a/docs/plugins/follow.rst +++ b/docs/plugins/follow.rst @@ -1,5 +1,12 @@ follow ====== -Makes the game view follow the currently highlighted unit after you exit from the -current menu or cursor mode. Handy for watching dwarves running around. Deactivated -by moving the view manually. +Tags: +:dfhack-keybind:`follow` + +Make the screen follow the selected unit. Once you exit from the current menu or +cursor mode, the screen will stay centered on the unit. Handy for watching +dwarves running around. Deactivated by moving the cursor manually. + +Usage:: + + follow diff --git a/plugins/follow.cpp b/plugins/follow.cpp index e9733d5da..53ff5c523 100644 --- a/plugins/follow.cpp +++ b/plugins/follow.cpp @@ -31,11 +31,10 @@ uint8_t prevMenuWidth; DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "follow", "Make the screen follow the selected unit", - follow, Gui::view_unit_hotkey, - " Select a unit and run this plugin to make the camera follow it.\n" - " Moving the camera yourself deactivates the plugin.\n" - )); + "follow", + "Make the screen follow the selected unit.", + follow, + Gui::view_unit_hotkey)); followedUnit = 0; prevX=prevY=prevZ = -1; prevMenuWidth = 0; From 82953d8b2fe08fc0b31ca50b0eb09896189baa5f Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 13:37:52 -0700 Subject: [PATCH 136/334] fix doc build errors --- docs/Removed.rst | 8 ++++++++ docs/plugins/fastdwarf.rst | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/Removed.rst b/docs/Removed.rst index 3dbe26820..a9d1d3470 100644 --- a/docs/Removed.rst +++ b/docs/Removed.rst @@ -49,6 +49,14 @@ existing .csv files. Just move them to the ``blueprints`` folder in your DF installation, and instead of ``digfort file.csv``, run ``quickfort run file.csv``. +.. _fix-armory: + +fix-armory +========== +Allowed the military to store equipment in barracks containers. Removed because +it required a binary patch to DF in order to function, and no such patch has +existed since DF 0.34.11. + .. _fortplan: fortplan diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index d964b319a..e06f2564c 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -5,11 +5,10 @@ Tags: Dwarves teleport and/or finish jobs instantly. -Usage: +Usage:: -``enable fastdwarf`` - Enable the plugin. -``fastdwarf []`` + enable fastdwarf + fastdwarf [] Examples -------- From eb0f016804953fc8fd5ac8dfa30c96430b616e68 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 16:40:19 -0700 Subject: [PATCH 137/334] update docs for forceequip --- docs/plugins/forceequip.rst | 101 +++++++++++++++++++++- plugins/forceequip.cpp | 165 +----------------------------------- 2 files changed, 100 insertions(+), 166 deletions(-) diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 2278bca22..2b8a55905 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -1,7 +1,100 @@ forceequip ========== -Forceequip moves local items into a unit's inventory. It is typically used to -equip specific clothing/armor items onto a dwarf, but can also be used to put -armor onto a war animal or to add unusual items (such as crowns) to any unit. +Tags: +:dfhack-keybind:`forceequip` -For more information run ``forceequip help``. See also `modtools/equip-item`. +Move items into a unit's inventory. This tool is typically used to equip +specific clothing/armor items onto a dwarf, but can also be used to put armor +onto a war animal or to add unusual items (such as crowns) to any unit. Make +sure the unit you want to equip is standing on the target items, which must be +on the ground and be unforbidden. If multiple units are standing on the same +tile, the first one will be equipped. + +The most reliable way to set up the environment for this command is to pile +target items on a tile of floor with a garbage dump activity zone or the +`autodump` command, then walk/pasture a unit (or use `gui/teleport`) on top of +the items. Be sure to unforbid the items that you want to work with! + +.. note:: + + Weapons are not currently supported. + +Usage:: + + forceequip [] + +As mentioned above, this plugin can be used to equip items onto units (such as +animals) who cannot normally equip gear. There's an important caveat here: such +creatures will automatically drop inappropriate gear almost immediately (within +10 game ticks). If you want them to retain their equipment, you must forbid it +AFTER using forceequip to get it into their inventory. This technique can also +be used to clothe dwarven infants, but only if you're able to separate them from +their mothers. + +By default, the ``forceequip`` command will attempt to abide by game rules as +closely as possible. For instance, it will skip any item which is flagged for +use in a job, and will not equip more than one piece of clothing/armor onto any +given body part. These restrictions can be overridden via options, but doing so +puts you at greater risk of unexpected consequences. For instance, a dwarf who +is wearing three breastplates will not be able to move very quickly. + +Items equipped by this plugin DO NOT become owned by the recipient. Adult +dwarves are free to adjust their own wardrobe, and may promptly decide to doff +your gear in favour of their owned items. Animals, as described above, will tend +to discard ALL clothing immediately unless it is manually forbidden. Armor items +seem to be an exception: an animal will tend to retain an equipped suit of mail" + even if you neglect to forbid it. + +Please note that armored animals are quite vulnerable to ranged attacks. Unlike +dwarves, animals cannot block, dodge, or deflect arrows, and they are slowed by +the weight of their armor. + +Examples +-------- + +``forceequip`` + Attempts to equip all of the clothing and armor under the cursor onto the + unit under the cursor, following game rules regarding which item can be + equipped on which body part and only equipping 1 item onto each body part. + Items owned by other dwarves are ignored. +``forceequip v bp QQQ`` + List the bodyparts of the selected unit. +``forceequip bp LH`` + Equips an appopriate item onto the unit's left hand. +``forceequip m bp LH`` + Equips ALL appropriate items onto the unit's left hand. The unit may end up + wearing a dozen left-handed mittens. Use with caution, and remember that + dwarves tend to drop extra items ASAP. +``forceequip i bp NECK`` + Equips an item around the unit's neck, ignoring appropriateness + restrictions. If there's a millstone or an albatross carcass sitting on the + same square as the targeted unit, then there's a good chance that it will + end up around his neck. For precise control, remember that you can + selectively forbid some of the items that are piled on the ground. +``forceequip s`` + Equips the item currently selected in the k menu, if possible. +``forceequip s m i bp HD`` + Equips the selected item onto the unit's head. Ignores all restrictions and + conflicts. If you know exactly what you want to equip, and exactly where you + want it to go, then this is the most straightforward and reliable option. + +Options +------- + +``i``, ``ignore`` + Bypasses the usual item eligibility checks (such as "Never equip gear + belonging to another dwarf" and "Nobody is allowed to equip a Hive". +``m``, ``multi`` + Bypasses the 1-item-per-bodypart limit. Useful for equipping both a mitten + and a gauntlet on the same hand (or twelve breastplates on the upper body). +``m2``, ``m3``, ``m4`` + Modifies the 1-item-per-bodypart limit, allowing each part to receive 2, 3, + or 4 pieces of gear. +``s``, ``selected`` + Equip only the item currently selected in the k menu and ignore all other + items in the tile. +``bp``, ``bodypart `` + Specify which body part should be equipped. +``v``, ``verbose`` + Provide detailed narration and error messages, including listing available + body parts when an invalid ``bodypart`` code is specified. diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index ab39bb0d6..10a157608 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -55,171 +55,12 @@ const int const_GloveLeftHandedness = 2; command_result df_forceequip(color_ostream &out, vector & parameters); -const string forceequip_help = - "ForceEquip moves local items into a unit's inventory. It is typically\n" - "used to equip specific clothing/armor items onto a dwarf, but can also\n" - "be used to put armor onto a war animal or to add unusual items (such\n" - "as crowns) to any unit.\n" - "This plugin can process multiple items in a single call, but will only\n" - "work with a single unit (the first one it finds under the cursor).\n" - "In order to minimize confusion, it is recommended that you use\n" - "forceequip only when you have a unit standing alone atop a pile of\n" - "gear that you would like it to wear. Items which are stored in bins\n" - "or other containers (e.g. chests, armor racks) may also work, but\n" - "piling items on the floor (via a garbage dump activity zone, of the\n" - "DFHack autodump command) is the most reliable way to do it.\n" - "The plugin will ignore any items that are forbidden. Hence, you\n" - "can setup a large pile of surplus gear, walk a unit onto it (or\n" - "pasture an animal on it), unforbid a few items and run forceequip.\n" - "The (forbidden) majority of your gear will remain in-place, ready\n" - "for the next passerby." - "\n" - "As mentioned above, this plugin can be used to equip items onto\n" - "units (such as animals) which cannot normally equip gear. There's\n" - "an important caveat here - such creatures will automatically drop\n" - "inappropriate gear almost immediately (within 10 game ticks).\n" - "If you want them to retain their equipment, you must forbid it\n" - "AFTER using forceequip to get it into their inventory.\n" - "This technique can also be used to clothe dwarven infants, but\n" - "only if you're able to separate them from their mothers.\n" - "\n" - "By default, the forceequip plugin will attempt to avoid\n" - "conflicts and outright cheating. For instance, it will skip\n" - "any item which is flagged for use in a job, and will not\n" - "equip more than one piece of clothing/armor onto any given\n" - "body part. These restrictions can be overridden via command\n" - "switches (see examples below) but doing so puts you at greater\n" - "risk of unexpected consequences. For instance, a dwarf who\n" - "is wearing three breastplates will not be able to move very\n" - "quickly.\n" - "\n" - "Items equipped by this plugin DO NOT become owned by the\n" - "recipient. Adult dwarves are free to adjust their own\n" - "wardrobe, and may promptly decide to doff your gear in\n" - "favour of their owned items. Animals, as described above,\n" - "will tend to discard ALL clothing immediately unless it is\n" - "manually forbidden. Armor items seem to be an exception;\n" - "an animal will tend to retain an equipped suit of mail\n" - "even if you neglect to Forbid it.\n" - "\n" - "Please note that armored animals are quite vulnerable to ranged\n" - "attacks. Unlike dwarves, animals cannot block, dodge, or deflect\n" - "arrows, and they are slowed by the weight of their armor.\n" - "\n" - "This plugin currently does not support weapons.\n" - "\n" - "Options:\n" - " here, h - process the unit and item(s) under the cursor.\n" - " - This option is enabled by default since the plugin\n" - " - does not currently support remote equpping.\n" - " ignore, i - bypasses the usual item eligibility checks (such as\n" - " - \"Never equip gear belonging to another dwarf\" and\n" - " - \"Nobody is allowed to equip a Hive\".)\n" - " multi, m - bypasses the 1-item-per-bodypart limit, allowing\n" - " - the unit to receive an unlimited amount of gear.\n" - " - Can be used legitimately (e.g. mitten + gauntlet)\n" - " - or for cheating (e.g. twelve breastplates).\n" - " m2, m3, m4 - alters the 1-item-per-bodypart limit, allowing\n" - " - each part to receive 2, 3, or 4 pieces of gear.\n" - " selected, s - rather than processing all items piled at a unit's\n" - " - feet, process only the one item currently selected.\n" - " bodypart, bp - must be followed by a bodypart code (e.g. LH).\n" - " - Instructs the plugin to equip all available items\n" - " - onto this body part only. Typically used in\n" - " - conjunction with the f switch (to over-armor\n" - " - a particular bodypart) or the i switch (to equip\n" - " - an unusual item onto a specific slot).\n" - " verbose, v - provides detailed narration and error messages.\n" - " - Can be helpful in resolving failures; not needed\n" - " - for casual use.\n" - "\n" - "Examples:\n" - " forceequip\n" - " attempts to equip all of the items under the cursor onto the unit\n" - " under the cursor. Uses only clothing/armor items; ignores all\n" - " other types. Equips a maximum of 1 item onto each bodypart,\n" - " and equips only \"appropriate\" items in each slot (e.g. glove\n" - " --> hand). Bypasses any item which might cause a conflict,\n" - " such as a Boot belonging to a different dwarf.\n" - " forceequip bp LH\n" - " attempts to equip all local items onto the left hand of the local\n" - " unit. If the hand is already equipped then nothing will happen,\n" - " and if it is not equipped then only one appropriate item (e.g. \n" - " a single mitten or gauntlet) will be equipped. This command can\n" - " be useful if you don't want to selectively forbid individual items\n" - " and simply want the unit to equip, say, a Helmet while leaving\n" - " the rest of the pile alone.\n" - " forceequip m bp LH\n" - " as above, but will equip ALL appropriate items onto the unit's\n" - " left hand. After running this command, it might end up wearing\n" - " a dozen left-handed mittens. Use with caution, and remember\n" - " that dwarves will tend to drop supernumary items ASAP.\n" - " forceequip m\n" - " as above, but will equip ALL appropriate items onto any\n" - " appropriate bodypart. Tends to put several boots onto the right\n" - " foot while leaving the left foot bare.\n" - " forceequip m2\n" - " as above, but will equip up to two appropriate items onto each\n" - " bodypart. Helps to balance footwear, but doesn't ensure proper\n" - " placement (e.g. left foot gets two socks, right foot gets two\n" - " shoes). For best results, use \"selected bp LH\" and\n" - " \"selected bp RH\" instead.\n" - " forceequip i\n" - " performs the standard \"equip appropriate items onto appropriate\n" - " bodyparts\" logic, but also includes items that would normally\n" - " be considered ineligible (such as a sock which is owned by\n" - " a different dwarf).\n" - " forceequip bp NECK\n" - " attempts to equip any appropriate gear onto the Neck of the\n" - " local unit. Since the plugin believes that no items are actually\n" - " appropriate for the Neck slot, this command does nothing.\n" - " forceequip i bp NECK\n" - " attempts to equip items from the local pile onto the Neck\n" - " of the local unit. Ignores appropriateness restrictions.\n" - " If there's a millstone or an albatross carcass sitting on\n" - " the same square as the targeted unit, then there's a good\n" - " chance that it will end up around his neck. For precise\n" - " control, remember that you can selectively forbid some of\n" - " the items that are piled on the ground.\n" - " forceequip i m bp NECK\n" - " as above, but equips an unlimited number of items onto the\n" - " targeted bodypart. Effectively, all unforbidden items\n" - " (including helms, millstones, boulders, etc) will be\n" - " moved from the local pile and placed in the dwarf's\n" - " inventory (specifically, on his neck). When used with\n" - " a large pile of goods, this will leave the dwarf heavily\n" - " encumbered and very slow to move.\n" - " forceequip s\n" - " requires that a single item be selected using the k menu.\n" - " This item must occupy the same square as the target unit,\n" - " and must be unforbidden. Attempts to equip this single\n" - " item onto an appropriate slot in the unit's inventory.\n" - " Can serve as a quicker alternative to the selective-\n" - " unforbidding approach described above.\n" - " forceequip s m i bp HD\n" - " equips the selected item onto the unit's head. Ignores\n" - " all possible restrictions and conflicts. If you know\n" - " exactly what you want to equip, and exactly where you\n" - " want it to go, then this is the most straightforward\n" - " and reliable option.\n" - " forceequip v bp QQQ\n" - " guaranteed to fail (and accomplish nothing) because\n" - " there are no bodyparts called QQQ. However, since the\n" - " verbose switch is used, the resulting error messages\n" - " will list every bodypart that the unit DOES possess.\n" - " May be useful if you're unfamiliar with the BP codes\n" - " used by Dwarf Fortress, or if you're experimenting\n" - " with an exotic creature.\n" - "\n" - ; - DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "forceequip", "Move items from the ground into a unit's inventory", - df_forceequip, false, - forceequip_help.c_str() - )); + "forceequip", + "Move items into a unit's inventory.", + df_forceequip)); return CR_OK; } From 367ac0064ef21089a7eca07752f3417fba6eb977 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 16:54:28 -0700 Subject: [PATCH 138/334] update docs for generated-creature-renamer --- docs/plugins/generated-creature-renamer.rst | 28 +++++++++++++-------- plugins/generated-creature-renamer.cpp | 13 +++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst index bb486a4ee..1615ba32d 100644 --- a/docs/plugins/generated-creature-renamer.rst +++ b/docs/plugins/generated-creature-renamer.rst @@ -1,15 +1,23 @@ generated-creature-renamer ========================== -Automatically renames generated creatures, such as forgotten beasts, titans, -etc, to have raw token names that match the description given in-game. +Tags: +:dfhack-keybind:`list-generated` +:dfhack-keybind:`save-generated-raws` -The ``list-generated`` command can be used to list the token names of all -generated creatures in a given save, with an optional ``detailed`` argument -to show the accompanying description. +Automatically renames generated creatures. Now, forgotten beasts, titans, +necromancer experiments, etc. will have raw token names that match the +description given in-game instead of unreadable generated strings. -The ``save-generated-raws`` command will save a sample creature graphics file in -the Dwarf Fortress root directory, to use as a start for making a graphics set -for generated creatures using the new names that they get with this plugin. +Usage: -The new names are saved with the save, and the plugin, when enabled, only runs once -per save, unless there's an update. +``enable generated-creature-renamer`` + Rename generated creatures when a world is loaded. +``list-generated [detailed]`` + List the token names of all generated creatures in the loaded save. If + ``detailed`` is specified, then also show the accompanying description. +``save-generated-raws`` + Save a sample creature graphics file in the Dwarf Fortress root directory to + use as a start for making a graphics set for generated creatures using the + new names that they get with this plugin. + +The new names are saved with the world. diff --git a/plugins/generated-creature-renamer.cpp b/plugins/generated-creature-renamer.cpp index 625c093cf..f6e9e41b6 100644 --- a/plugins/generated-creature-renamer.cpp +++ b/plugins/generated-creature-renamer.cpp @@ -27,17 +27,12 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Mon, 25 Jul 2022 16:57:08 -0700 Subject: [PATCH 139/334] fix typos in forceequip cpp/docs --- docs/plugins/forceequip.rst | 4 ++-- plugins/forceequip.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 2b8a55905..217085fe4 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -42,8 +42,8 @@ Items equipped by this plugin DO NOT become owned by the recipient. Adult dwarves are free to adjust their own wardrobe, and may promptly decide to doff your gear in favour of their owned items. Animals, as described above, will tend to discard ALL clothing immediately unless it is manually forbidden. Armor items -seem to be an exception: an animal will tend to retain an equipped suit of mail" - even if you neglect to forbid it. +seem to be an exception: an animal will tend to retain an equipped suit of mail +even if you neglect to forbid it. Please note that armored animals are quite vulnerable to ranged attacks. Unlike dwarves, animals cannot block, dodge, or deflect arrows, and they are slowed by diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 10a157608..2ee4e47a7 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -280,8 +280,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) if (p == "help" || p == "?" || p == "h" || p == "/?" || p == "info" || p == "man") { - out << forceequip_help << endl; - return CR_OK; + return CR_WRONG_USAGE; } else if (p == "here" || p == "h") { From 8cf7f59c398cbc25de7e471052e9ed4e63d97155 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 17:48:39 -0700 Subject: [PATCH 140/334] wrap ls and tags output --- library/lua/helpdb.lua | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 5e0f49920..1bf93f333 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -653,21 +653,31 @@ function help(entry) print(get_entry_long_help(entry)) end -local function get_max_width(list, min_width) - local width = min_width or 0 - for _,item in ipairs(list) do - width = math.max(width, #item) +-- prints col1 (width 20), a 2 space gap, and col2 (width 58) +-- if col1text is longer than 20 characters, col2text is printed on the next +-- line. if col2text is longer than 58 characters, it is wrapped. +local COL1WIDTH, COL2WIDTH = 20, 58 +local function print_columns(col1text, col2text) + col2text = col2text:wrap(COL2WIDTH) + local wrapped_col2 = {} + for line in col2text:gmatch('[^'..NEWLINE..']*') do + table.insert(wrapped_col2, line) + end + if #col1text > COL1WIDTH then + print(col1text) + else + print(('%-'..COL1WIDTH..'s %s'):format(col1text, wrapped_col2[1])) + end + for i=2,#wrapped_col2 do + print(('%'..COL1WIDTH..'s %s'):format(' ', wrapped_col2[i])) end - return width end -- implements the 'tags' builtin command function tags() local tags = get_tags() - local width = get_max_width(tags, 10) for _,tag in ipairs(tags) do - print((' %-'..width..'s %s'):format(tag, - get_tag_data(tag).description)) + print_columns(tag, get_tag_data(tag).description) end end @@ -675,10 +685,8 @@ end -- defined as in search_entries() above. function list_entries(skip_tags, include, exclude) local entries = search_entries(include, exclude) - local width = get_max_width(entries, 10) for _,entry in ipairs(entries) do - print((' %-'..width..'s %s'):format( - entry, get_entry_short_help(entry))) + print_columns(entry, get_entry_short_help(entry)) if not skip_tags then local tags = get_entry_tags(entry) if #tags > 0 then From b93e3365f6d8fb1281d8de49c680a38d1fe084be Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 21:50:52 -0700 Subject: [PATCH 141/334] update docs for get plants --- docs/plugins/getplants.rst | 69 ++++++++++++++++++++++++-------------- plugins/getplants.cpp | 21 ++---------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 5a719b3ab..167ac72f2 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -1,24 +1,48 @@ getplants ========= -This tool allows plant gathering and tree cutting by RAW ID. Specify the types -of trees to cut down and/or shrubs to gather by their plant names, separated -by spaces. - -Options: - -:``-t``: Tree: Select trees only (exclude shrubs) -:``-s``: Shrub: Select shrubs only (exclude trees) -:``-f``: Farming: Designate only shrubs that yield seeds for farming. Implies -s -:``-c``: Clear: Clear designations instead of setting them -:``-x``: eXcept: Apply selected action to all plants except those specified (invert - selection) -:``-a``: All: Select every type of plant (obeys ``-t``/``-s``/``-f``) -:``-v``: Verbose: Lists the number of (un)designations per plant -:``-n *``: Number: Designate up to * (an integer number) plants of each species - -Specifying both ``-t`` and ``-s`` or ``-f`` will have no effect. If no plant IDs are -specified, all valid plant IDs will be listed, with ``-t``, ``-s``, and ``-f`` -restricting the list to trees, shrubs, and farmable shrubs, respectively. +Tags: +:dfhack-keybind:`getplants` + +Designate trees for chopping and shrubs for gathering. Specify the types of +trees to cut down and/or shrubs to gather by their plant names. + +Usage: + +``getplants [-t|-s|-f]`` + List valid tree/shrub ids, optionally restricted to the specified type. +``getplants [ ...] []`` + Designate trees/shrubs of the specified types for chopping/gathering. + +Examples +-------- + +``getplants`` + List all valid IDs. +``getplants -f -a`` + Gather all plants on the map that yield seeds for farming. +``getplants NETHER_CAP -n 10`` + Designate 10 nether cap trees for chopping. + +Options +------- + +``-t`` + Tree: Select trees only (exclude shrubs). +``-s`` + Shrub: Select shrubs only (exclude trees). +``-f`` + Farming: Designate only shrubs that yield seeds for farming. +``-a`` + All: Select every type of plant (obeys ``-t``/``-s``/``-f``). +``-c`` + Clear: Clear designations instead of setting them. +``-x`` + eXcept: Apply selected action to all plants except those specified (invert + selection). +``-v`` + Verbose: Lists the number of (un)designations per plant. +``-n `` + Number: Designate up to the specified number of plants of each species. .. note:: @@ -28,10 +52,3 @@ restricting the list to trees, shrubs, and farmable shrubs, respectively. This leads to some shrubs being designated when they shouldn't be, causing a plant gatherer to walk there and do nothing (except clearing the designation). See :issue:`1479` for details. - - The implementation another known deficiency: it's incapable of detecting that - raw definitions that specify a seed extraction reaction for the structural part - but has no other use for it cannot actually yield any seeds, as the part is - never used (parts of :bug:`6940`, e.g. Red Spinach), even though DF - collects it, unless there's a workshop reaction to do it (which there isn't - in vanilla). diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 25823eb17..6819b6191 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -584,24 +584,9 @@ command_result df_getplants (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { commands.push_back(PluginCommand( - "getplants", "Cut down trees or gather shrubs by ID", - df_getplants, false, - " Specify the types of trees to cut down and/or shrubs to gather by their\n" - " plant IDs, separated by spaces.\n" - "Options:\n" - " -t - Tree: Select trees only (exclude shrubs)\n" - " -s - Shrub: Select shrubs only (exclude trees)\n" - " -f - Farming: Designate only shrubs that yield seeds for farming. Implies -s\n" - " -c - Clear: Clear designations instead of setting them\n" - " -x - eXcept: Apply selected action to all plants except those specified\n" - " -a - All: Select every type of plant (obeys -t/-s/-f)\n" - " -v - Verbose: List the number of (un)designations per plant\n" - " -n * - Number: Designate up to * (an integer number) plants of each species\n" - "Specifying both -t and -s or -f will have no effect.\n" - "If no plant IDs are specified, and the -a switch isn't given, all valid plant\n" - "IDs will be listed with -t, -s, and -f restricting the list to trees, shrubs,\n" - "and farmable shrubs, respectively.\n" - )); + "getplants", + "Designate trees for chopping and shrubs for gathering.", + df_getplants)); return CR_OK; } From 69d88a62ddd27c54d1a484808a451ed2153ade99 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 21:54:34 -0700 Subject: [PATCH 142/334] update docs for hotkeys --- docs/plugins/hotkeys.rst | 14 ++++++++++---- plugins/hotkeys.cpp | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst index 57c3b3af5..49064db0a 100644 --- a/docs/plugins/hotkeys.rst +++ b/docs/plugins/hotkeys.rst @@ -1,8 +1,14 @@ hotkeys ======= -Opens an in-game screen showing which DFHack keybindings are -active in the current context. See also `hotkey-notes`. +Tags: +:dfhack-keybind:`hotkeys` -.. image:: ../images/hotkeys.png +Show all dfhack keybindings in current context. The command opens an in-game +screen showing which DFHack keybindings are active in the current context. +See also `hotkey-notes`. -:dfhack-keybind:`hotkeys` +Usage:: + + hotkeys + +.. image:: ../images/hotkeys.png diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index a91c62bdf..84ce37eb8 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -356,8 +356,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Mon, 25 Jul 2022 22:00:56 -0700 Subject: [PATCH 143/334] update docs for infiniteSky --- docs/plugins/infiniteSky.rst | 18 ++++++++++++------ plugins/infiniteSky.cpp | 14 +------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index 328ee83bd..24e206f25 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -1,14 +1,20 @@ infiniteSky =========== -Automatically allocates new z-levels of sky at the top of the map as you build up, -or on request allocates many levels all at once. +Tags: +:dfhack-keybind:`` + +Automatically allocates new z-levels of sky at the top of the map as you build +up, or on request allocates many levels all at once. Usage: -``infiniteSky n`` - Raise the sky by n z-levels. -``infiniteSky enable/disable`` - Enables/disables monitoring of constructions. If you build anything in the second to highest z-level, it will allocate one more sky level. This is so you can continue to build stairs upward. +``enable infiniteSky`` + Enables monitoring of constructions. If you build anything in the second + highest z-level, it will allocate one more sky level. You can build stairs + up as high as you like! +``infiniteSky []`` + Raise the sky by n z-levels. If run without parameters, raises the sky by + one z-level. .. warning:: diff --git a/plugins/infiniteSky.cpp b/plugins/infiniteSky.cpp index d63eec6b8..bc51f06f1 100644 --- a/plugins/infiniteSky.cpp +++ b/plugins/infiniteSky.cpp @@ -33,19 +33,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Mon, 25 Jul 2022 22:01:28 -0700 Subject: [PATCH 144/334] add missing keybinding for infiniteSky --- docs/plugins/infiniteSky.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index 24e206f25..96f141de5 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -1,7 +1,7 @@ infiniteSky =========== Tags: -:dfhack-keybind:`` +:dfhack-keybind:`infiniteSky` Automatically allocates new z-levels of sky at the top of the map as you build up, or on request allocates many levels all at once. From c88423e65563ed66b06f7f1bc4afc6c2c60f3296 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 25 Jul 2022 22:05:32 -0700 Subject: [PATCH 145/334] remove docs for non-enableable, command-less plugin --- docs/plugins/isoworldremote.rst | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 docs/plugins/isoworldremote.rst diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst deleted file mode 100644 index 7bacc2b57..000000000 --- a/docs/plugins/isoworldremote.rst +++ /dev/null @@ -1,3 +0,0 @@ -isoworldremote -============== -A plugin that implements a `remote API ` used by Isoworld. From 0fae25fc8b505bcc49dae7693b054fdcc289ddf3 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 00:47:38 -0400 Subject: [PATCH 146/334] Only write (most) generated rst files if they actually changed Speeds up incremental builds significantly --- conf.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/conf.py b/conf.py index 814c7c2cb..f13117484 100644 --- a/conf.py +++ b/conf.py @@ -14,7 +14,9 @@ serve to show the default. # pylint:disable=redefined-builtin +import contextlib import datetime +import io from io import open import os import re @@ -132,13 +134,26 @@ def get_open_mode(): return 'w' if sys.version_info.major > 2 else 'wb' +@contextlib.contextmanager +def write_file_if_changed(path): + with io.StringIO() as buffer: + yield buffer + new_contents = buffer.getvalue() + + with open(path, 'r') as infile: + old_contents = infile.read() + + if old_contents != new_contents: + with open(path, get_open_mode()) as outfile: + outfile.write(new_contents) + + def generate_tag_indices(): os.makedirs('docs/tags', mode=0o755, exist_ok=True) - with open('docs/tags/index.rst', get_open_mode()) as topidx: + with write_file_if_changed('docs/tags/index.rst') as topidx: for tag_tuple in get_tags(): tag = tag_tuple[0] - with open(('docs/tags/{name}.rst').format(name=tag), - get_open_mode()) as tagidx: + with write_file_if_changed(('docs/tags/{name}.rst').format(name=tag)) as tagidx: tagidx.write('TODO: add links to the tools that have this tag') topidx.write(('.. _tag/{name}:\n\n').format(name=tag)) topidx.write(('{name}\n').format(name=tag)) @@ -168,7 +183,7 @@ def write_tool_docs(): # the page in one long wrapped line, similar to how the wiki does it os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), mode=0o755, exist_ok=True) - with open('docs/tools/{}.rst'.format(k[0]), get_open_mode()) as outfile: + with write_file_if_changed('docs/tools/{}.rst'.format(k[0])) as outfile: outfile.write(header) if k[0] != 'search' and k[0] != 'stonesense': outfile.write(label) From a8b40c5911ad8d028849fab43e5d38999735bb94 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 00:48:22 -0400 Subject: [PATCH 147/334] Drop Python 2 compat --- conf.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/conf.py b/conf.py index f13117484..3bb73d40d 100644 --- a/conf.py +++ b/conf.py @@ -17,7 +17,6 @@ serve to show the default. import contextlib import datetime import io -from io import open import os import re import shlex # pylint:disable=unused-import @@ -130,10 +129,6 @@ def get_tags(): return tags -def get_open_mode(): - return 'w' if sys.version_info.major > 2 else 'wb' - - @contextlib.contextmanager def write_file_if_changed(path): with io.StringIO() as buffer: @@ -144,7 +139,7 @@ def write_file_if_changed(path): old_contents = infile.read() if old_contents != new_contents: - with open(path, get_open_mode()) as outfile: + with open(path, 'w') as outfile: outfile.write(new_contents) From 6293b71e9e3b821a8e43ccb65a4dfe3c4a2be335 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 26 Jul 2022 22:01:39 -0700 Subject: [PATCH 148/334] Revert "remove docs for non-enableable, command-less plugin" This reverts commit c88423e65563ed66b06f7f1bc4afc6c2c60f3296. --- docs/plugins/isoworldremote.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/plugins/isoworldremote.rst diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst new file mode 100644 index 000000000..7bacc2b57 --- /dev/null +++ b/docs/plugins/isoworldremote.rst @@ -0,0 +1,3 @@ +isoworldremote +============== +A plugin that implements a `remote API ` used by Isoworld. From 671f10e5d835824c3dd78c0a142ee6dbc870e925 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 01:04:47 -0400 Subject: [PATCH 149/334] Fix write_file_if_changed() if target file does not exist --- conf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/conf.py b/conf.py index 3bb73d40d..fab4b286a 100644 --- a/conf.py +++ b/conf.py @@ -135,8 +135,11 @@ def write_file_if_changed(path): yield buffer new_contents = buffer.getvalue() - with open(path, 'r') as infile: - old_contents = infile.read() + try: + with open(path, 'r') as infile: + old_contents = infile.read() + except IOError: + old_contents = None if old_contents != new_contents: with open(path, 'w') as outfile: From 10f8417cc275b6fb597c56a3e1b3563018ca15d9 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 26 Jul 2022 22:22:31 -0700 Subject: [PATCH 150/334] create helpdb entries for all plugins even those that have no commands and are not enableable --- library/LuaApi.cpp | 16 ---------------- library/lua/helpdb.lua | 23 ++++++++--------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 4ee1a4d1c..d04da21ec 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3164,21 +3164,6 @@ static int internal_getCommandDescription(lua_State *L) return 1; } -static int internal_isPluginEnableable(lua_State *L) -{ - auto plugins = Core::getInstance().getPluginManager(); - - const char *name = luaL_checkstring(L, 1); - - auto plugin = plugins->getPluginByName(name); - if (plugin) - lua_pushboolean(L, plugin->can_be_enabled()); - else - lua_pushnil(L); - - return 1; -} - static int internal_threadid(lua_State *L) { std::stringstream ss; @@ -3255,7 +3240,6 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "listCommands", internal_listCommands }, { "getCommandHelp", internal_getCommandHelp }, { "getCommandDescription", internal_getCommandDescription }, - { "isPluginEnableable", internal_isPluginEnableable }, { "threadid", internal_threadid }, { "md5File", internal_md5file }, { NULL, NULL } diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 1bf93f333..2aee005ee 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -273,13 +273,11 @@ local function update_db(old_db, entry_name, text_entry, help_source, kwargs) -- already in db (e.g. from a higher-priority script dir); skip return end - if not kwargs.text_entry_only then - entrydb[entry_name] = { - entry_types=kwargs.entry_types, - short_help=kwargs.short_help, - text_entry=text_entry - } - end + entrydb[entry_name] = { + entry_types=kwargs.entry_types, + short_help=kwargs.short_help, + text_entry=text_entry + } if entry_name ~= text_entry then return end @@ -317,7 +315,7 @@ local function scan_plugins(old_db) local plugin_names = dfhack.internal.listPlugins() for _,plugin in ipairs(plugin_names) do local commands = dfhack.internal.listCommands(plugin) - local includes_plugin, has_commands = false, false + local includes_plugin = false for _,command in ipairs(commands) do local kwargs = {entry_types={[ENTRY_TYPES.COMMAND]=true}} if command == plugin then @@ -329,18 +327,13 @@ local function scan_plugins(old_db) has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.PLUGIN, kwargs) - has_commands = true end - if includes_plugin then goto continue end - local is_enableable = dfhack.internal.isPluginEnableable(plugin) - if has_commands or is_enableable then + if not includes_plugin then update_db(old_db, plugin, plugin, has_rendered_help(plugin) and HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, - {entry_types={[ENTRY_TYPES.PLUGIN]=true}, - text_entry_only=not is_enableable}) + {entry_types={[ENTRY_TYPES.PLUGIN]=true}}) end - ::continue:: end end From e677e8098d8c7a353ca5af2c8e3614268fd214cc Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 15:48:11 -0700 Subject: [PATCH 151/334] add docs for plugins that only provide Lua apis --- docs/Lua API.rst | 14 +++++++------- docs/plugins/building-hacks.rst | 6 ++++++ docs/plugins/cxxrandom.rst | 6 ++++++ docs/plugins/eventful.rst | 6 ++++++ docs/plugins/luasocket.rst | 6 ++++++ docs/plugins/map-render.rst | 6 ++++++ docs/plugins/pathable.rst | 6 ++++++ docs/plugins/xlsxreader.rst | 6 ++++++ 8 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 docs/plugins/building-hacks.rst create mode 100644 docs/plugins/cxxrandom.rst create mode 100644 docs/plugins/eventful.rst create mode 100644 docs/plugins/luasocket.rst create mode 100644 docs/plugins/map-render.rst create mode 100644 docs/plugins/pathable.rst create mode 100644 docs/plugins/xlsxreader.rst diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 107a0ba2e..94eaa1165 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -4287,7 +4287,7 @@ blueprint files: The names of the functions are also available as the keys of the ``valid_phases`` table. -.. _building-hacks: +.. _building-hacks-api: building-hacks ============== @@ -4426,7 +4426,7 @@ Native functions: The lua module file also re-exports functions from ``dfhack.burrows``. -.. _cxxrandom: +.. _cxxrandom-api: cxxrandom ========= @@ -4596,7 +4596,7 @@ The dig-now plugin exposes the following functions to Lua: command ``dig-now ``. See the `dig-now` documentation for details on default settings. -.. _eventful: +.. _eventful-api: eventful ======== @@ -4764,7 +4764,7 @@ Integrated tannery:: b=require "plugins.eventful" b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS") -.. _luasocket: +.. _luasocket-api: luasocket ========= @@ -4835,7 +4835,7 @@ A class with all the tcp functionality. Tries connecting to that address and port. Returns ``client`` object. -.. _map-render: +.. _map-render-api: map-render ========== @@ -4851,7 +4851,7 @@ Functions returns a table with w*h*4 entries of rendered tiles. The format is same as ``df.global.gps.screen`` (tile,foreground,bright,background). -.. _pathable: +.. _pathable-api: pathable ======== @@ -4885,7 +4885,7 @@ sort The `sort ` plugin does not export any native functions as of now. Instead, it calls Lua code to perform the actual ordering of list items. -.. _xlsxreader: +.. _xlsxreader-api: xlsxreader ========== diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst new file mode 100644 index 000000000..106173c47 --- /dev/null +++ b/docs/plugins/building-hacks.rst @@ -0,0 +1,6 @@ +building-hacks +============== + +Provides a Lua API for creating powered workshops. + +See `building-hacks-api` for more details. diff --git a/docs/plugins/cxxrandom.rst b/docs/plugins/cxxrandom.rst new file mode 100644 index 000000000..f3ed8f86d --- /dev/null +++ b/docs/plugins/cxxrandom.rst @@ -0,0 +1,6 @@ +cxxrandom +========= + +Provides a Lua API for random distributions. + +See `cxxrandom-api` for details. diff --git a/docs/plugins/eventful.rst b/docs/plugins/eventful.rst new file mode 100644 index 000000000..892f233bb --- /dev/null +++ b/docs/plugins/eventful.rst @@ -0,0 +1,6 @@ +eventful +======== + +Provides a Lua API for reacting to in-game events. + +See `eventful-api` for details. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst new file mode 100644 index 000000000..6f7c96cae --- /dev/null +++ b/docs/plugins/luasocket.rst @@ -0,0 +1,6 @@ +luasocket +========= + +Provides a Lua API for accessing network sockets. + +See `luasocket-api` for details. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst new file mode 100644 index 000000000..2bcb32adf --- /dev/null +++ b/docs/plugins/map-render.rst @@ -0,0 +1,6 @@ +map-render +========== + +Provides a Lua API for rerendering portions of the map. + +See `map-render-api` for details. diff --git a/docs/plugins/pathable.rst b/docs/plugins/pathable.rst new file mode 100644 index 000000000..daf7697bc --- /dev/null +++ b/docs/plugins/pathable.rst @@ -0,0 +1,6 @@ +pathable +======== + +Provides a Lua API for marking tiles that are reachable from the cursor. + +See `pathable-api` for details. diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst new file mode 100644 index 000000000..6429ceedc --- /dev/null +++ b/docs/plugins/xlsxreader.rst @@ -0,0 +1,6 @@ +xlsxreader +========== + +Provides a Lua API for reading .xlsx files. + +See `xlsxreader-api` for details. From 117efaa8147fc3b22fbf94a7562ecb0ecc5213f8 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 15:48:44 -0700 Subject: [PATCH 152/334] update short description for command-prompt --- plugins/command-prompt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 1994d66b5..94398837a 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -345,7 +345,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Wed, 27 Jul 2022 16:03:49 -0700 Subject: [PATCH 153/334] update docs for jobutils. I was unable to find an example usage for `job item-type`. I couldn't find any combination of jobs or parameters that worked. --- docs/plugins/jobutils.rst | 71 +++++++++++++++++++++------------------ plugins/jobutils.cpp | 42 ++++++----------------- 2 files changed, 49 insertions(+), 64 deletions(-) diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index 230c69f4c..8863c7a9b 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -1,40 +1,45 @@ .. _job: -job -=== -Command for general job query and manipulation. +jobutils +======== +Tags: +:dfhack-keybind:`job` +:dfhack-keybind:`job-duplicate` +:dfhack-keybind:`job-material` -Options: +Usage: -*no extra options* - Print details of the current job. The job can be selected - in a workshop, or the unit/jobs screen. -**list** +``job`` + Print details of the current job. The job can be selected in a workshop or + the unit/jobs screen. +``job list`` Print details of all jobs in the selected workshop. -**item-material ** +``job item-material `` Replace the exact material id in the job item. -**item-type ** +``job item-type `` Replace the exact item type id in the job item. - -job-duplicate -============= -In :kbd:`q` mode, when a job is highlighted within a workshop or furnace -building, calling ``job-duplicate`` instantly duplicates the job. - -:dfhack-keybind:`job-duplicate` - -job-material -============ -Alter the material of the selected job. Similar to ``job item-material ...`` - -Invoked as:: - - job-material - -:dfhack-keybind:`job-material` - -* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, - changes the material of the job. Only inorganic materials can be used - in this mode. -* In :kbd:`b` mode, during selection of building components positions the cursor - over the first available choice with the matching material. +``job-duplicate`` + Duplicates the highlighted job. Must be in :kbd:`q` mode and have a workshop + or furnace building selected. +``job-material `` + Alters the material of the selected job (in :kbd:`q` mode) or jumps to the + selected material when choosing the building component of a planned building + (in :kbd:`b` mode). Note that this form of the command can only handle + inorganic materials. + +Use the ``job`` and ``job list`` commands to discover the type and material ids +for existing jobs, or use the following commands to see the full lists:: + + lua @df.item_type + lua "for i,mat in ipairs(df.global.world.raws.inorganics) do if mat.material.flags.IS_STONE and not mat.material.flags.NO_STONE_STOCKPILE then print(i, mat.id) end end" + +Examples +-------- + +``job-material GNEISS`` + Change the selected "Construct rock Coffin" job at a Mason's workshop to + "Construct gneiss coffin". +``job item-material 2 MARBLE`` + Change the selected "Construct Traction Bench" job (which has three source + items: a table, a mechanism, and a chain) to specifically use a marble + mechanism. diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 0607b50c5..5f5994435 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -53,46 +53,26 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector \n" - " Replace the exact material id in the job item.\n" - " job item-type \n" - " Replace the exact item type id in the job item.\n" - ) - ); + "job", + "Inspect and modify job item properties.", + job_cmd)); if (ui_workshop_job_cursor || ui_build_selector) { commands.push_back( PluginCommand( - "job-material", "Alter the material of the selected job.", - job_material, job_material_hotkey, - " job-material \n" - "Intended to be used as a keybinding:\n" - " - In 'q' mode, when a job is highlighted within a workshop\n" - " or furnace, changes the material of the job. Only inorganic\n" - " materials can be used in this mode.\n" - " - In 'b' mode, during selection of building components\n" - " positions the cursor over the first available choice\n" - " with the matching material.\n" - ) - ); + "job-material", + "Alter the material of the selected job or building.", + job_material, + job_material_hotkey)); } if (ui_workshop_job_cursor && job_next_id) { commands.push_back( PluginCommand( - "job-duplicate", "Duplicate the selected job in a workshop.", - job_duplicate, Gui::workshop_job_hotkey, - " - In 'q' mode, when a job is highlighted within a workshop\n" - " or furnace building, instantly duplicates the job.\n" - ) - ); + "job-duplicate", + "Duplicate the selected job in a workshop.", + job_duplicate, + Gui::workshop_job_hotkey)); } return CR_OK; From 382ad84125195b70ed86500fb3fd99bfbd707dce Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 16:14:24 -0700 Subject: [PATCH 154/334] add example for job item-type --- docs/plugins/jobutils.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index 8863c7a9b..e8434abdc 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -43,3 +43,7 @@ Examples Change the selected "Construct Traction Bench" job (which has three source items: a table, a mechanism, and a chain) to specifically use a marble mechanism. +``job item-type 2 TABLE`` + Change the selected "Encrust furniture with blue jade" job (which has two + source items: a cut gem and a piece of improvable furniture) to specifically + use a table instead of just any furniture. From ed113466d92e298a8d53a70c47dbfa3e645f5cc0 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 16:29:54 -0700 Subject: [PATCH 155/334] update docs for isoworldremote --- docs/plugins/isoworldremote.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 7bacc2b57..2c359fac2 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -1,3 +1,5 @@ isoworldremote ============== -A plugin that implements a `remote API ` used by Isoworld. +Tags: + +Provides a `remote API ` used by Isoworld. From a053cce444893c94f84269350f71766d268e0991 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 16:30:14 -0700 Subject: [PATCH 156/334] update the "requires interactive terminal" message to make it more user friendly --- library/Core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index facd02823..1b3f1409f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1235,7 +1235,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } } else if (res == CR_NEEDS_CONSOLE) - con.printerr("%s needs interactive console to work.\n", first.c_str()); + con.printerr("%s needs an interactive console to work.\n" + "Please run this command from the DFHack terminal.\n", first.c_str()); return res; } From 58c0f94a2d4f445370016f58d5932724b9f8f908 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 17:14:00 -0700 Subject: [PATCH 157/334] order autolabor docs similarly to labormanager --- docs/plugins/autolabor.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index ca249ee19..dc14a40bb 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -70,6 +70,11 @@ Examples Advanced usage -------------- +``autolabor list`` + List current status of all labors. Use this command to see the IDs for all + labors. +``autolabor status`` + Show basic status information. ``autolabor [] []`` Set range of dwarves assigned to a labor, optionally specifying the size of the pool of most skilled dwarves that will ever be considered for this @@ -80,9 +85,5 @@ Advanced usage Turn off autolabor for a specific labor. ``autolabor reset-all| reset`` Return a labor (or all labors) to the default handling. -``autolabor list`` - List current status of all labors. -``autolabor status`` - Show basic status information. See `autolabor-artisans` for a differently-tuned setup. From e94f1891c26d457f37ce98e13e2f4fe79eb03d6c Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 17:14:18 -0700 Subject: [PATCH 158/334] update docs for labormanager --- docs/plugins/labormanager.rst | 216 ++++++++++++++------------ plugins/labormanager/labormanager.cpp | 35 +---- 2 files changed, 122 insertions(+), 129 deletions(-) diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index d16a10e33..103ecde2f 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -1,105 +1,127 @@ labormanager ============ -Automatically manage dwarf labors to efficiently complete jobs. -Labormanager is derived from autolabor (above) but uses a completely -different approach to assigning jobs to dwarves. While autolabor tries -to keep as many dwarves busy as possible, labormanager instead strives -to get jobs done as quickly as possible. +Tags: +:dfhack-keybind:`labormanager` -Labormanager frequently scans the current job list, current list of -dwarfs, and the map to determine how many dwarves need to be assigned to -what labors in order to meet all current labor needs without starving -any particular type of job. +Automatically manage dwarf labors. Labormanager is derived from `autolabor` +but uses a completely different approach to assigning jobs to dwarves. While +autolabor tries to keep as many dwarves busy as possible, labormanager instead +strives to get jobs done as quickly as possible. + +Labormanager frequently scans the current job list, current list of dwarves, and +the map to determine how many dwarves need to be assigned to what labors in +order to meet all current labor needs without starving any particular type of +job. + +Dwarves on active military duty or dwarves assigned to burrows are left +untouched. .. warning:: - *As with autolabor, labormanager will override any manual changes you - make to labors while it is enabled, including through other tools such - as Dwarf Therapist* - -Simple usage: - -:enable labormanager: Enables the plugin with default settings. - (Persistent per fortress) - -:disable labormanager: Disables the plugin. - -Anything beyond this is optional - labormanager works fairly well on the -default settings. - -The default priorities for each labor vary (some labors are higher -priority by default than others). The way the plugin works is that, once -it determines how many of each labor is needed, it then sorts them by -adjusted priority. (Labors other than hauling have a bias added to them -based on how long it's been since they were last used, to prevent job -starvation.) The labor with the highest priority is selected, the "best -fit" dwarf for that labor is assigned to that labor, and then its -priority is *halved*. This process is repeated until either dwarfs or -labors run out. - -Because there is no easy way to detect how many haulers are actually -needed at any moment, the plugin always ensures that at least one dwarf -is assigned to each of the hauling labors, even if no hauling jobs are -detected. At least one dwarf is always assigned to construction removing -and cleaning because these jobs also cannot be easily detected. Lever -pulling is always assigned to everyone. Any dwarfs for which there are -no jobs will be assigned hauling, lever pulling, and cleaning labors. If -you use animal trainers, note that labormanager will misbehave if you -assign specific trainers to specific animals; results are only guaranteed -if you use "any trainer", and animal trainers will probably be -overallocated in any case. - -Labormanager also sometimes assigns extra labors to currently busy -dwarfs so that when they finish their current job, they will go off and -do something useful instead of standing around waiting for a job. - -There is special handling to ensure that at least one dwarf is assigned -to haul food whenever food is detected left in a place where it will rot -if not stored. This will cause a dwarf to go idle if you have no -storepiles to haul food to. - -Dwarfs who are unable to work (child, in the military, wounded, -handless, asleep, in a meeting) are entirely excluded from labor -assignment. Any dwarf explicitly assigned to a burrow will also be -completely ignored by labormanager. - -The fitness algorithm for assigning jobs to dwarfs generally attempts to -favor dwarfs who are more skilled over those who are less skilled. It -also tries to avoid assigning female dwarfs with children to jobs that -are "outside", favors assigning "outside" jobs to dwarfs who are -carrying a tool that could be used as a weapon, and tries to minimize -how often dwarfs have to reequip. - -Labormanager automatically determines medical needs and reserves health -care providers as needed. Note that this may cause idling if you have -injured dwarfs but no or inadequate hospital facilities. - -Hunting is never assigned without a butchery, and fishing is never -assigned without a fishery, and neither of these labors is assigned -unless specifically enabled. + As with autolabor, labormanager will override any manual changes you make to + labors while it is enabled, including through other tools such as Dwarf + Therapist. Do not run both autolabor and labormanager at the same time! + +Usage:: + + enable labormanager + +Anything beyond this is optional - labormanager works well with the default +settings. Once you have enabled it in a fortress, it stays enabled until you +explicitly disable it, even if you save and reload your game. + +The default priorities for each labor vary (some labors are higher priority by +default than others). The way the plugin works is that, once it determines how +many jobs of each labor is needed, it then sorts them by adjusted priority. +(Labors other than hauling have a bias added to them based on how long it's been +since they were last used to prevent job starvation.) The labor with the highest +priority is selected, the "best fit" dwarf for that labor is assigned to that +labor, and then its priority is *halved*. This process is repeated until either +dwarves or labors run out. + +Because there is no easy way to detect how many haulers are actually needed at +any moment, the plugin always ensures that at least one dwarf is assigned to +each of the hauling labors, even if no hauling jobs are detected. At least one +dwarf is always assigned to construction removing and cleaning because these +jobs also cannot be easily detected. Lever pulling is always assigned to +everyone. Any dwarves for which there are no jobs will be assigned hauling, +lever pulling, and cleaning labors. If you use animal trainers, note that +labormanager will misbehave if you assign specific trainers to specific animals; +results are only guaranteed if you use "any trainer". + +Labormanager also sometimes assigns extra labors to currently busy dwarfs so +that when they finish their current job, they will go off and do something +useful instead of standing around waiting for a job. + +There is special handling to ensure that at least one dwarf is assigned to haul +food whenever food is detected left in a place where it will rot if not stored. +This will cause a dwarf to go idle if you have no stockpiles to haul food to. + +Dwarves who are unable to work (child, in the military, wounded, handless, +asleep, in a meeting) are entirely excluded from labor assignment. Any dwarf +explicitly assigned to a burrow will also be completely ignored by labormanager. + +The fitness algorithm for assigning jobs to dwarves generally attempts to favor +dwarves who are more skilled over those who are less skilled. It also tries to +avoid assigning female dwarfs with children to jobs that are "outside", favors +assigning "outside" jobs to dwarfs who are carrying a tool that could be used as +a weapon, and tries to minimize how often dwarves have to reequip. + +Labormanager automatically determines medical needs and reserves health care +providers as needed. Note that this may cause idling if you have injured dwarves +but no or inadequate hospital facilities. + +Hunting is never assigned without a butchery, and fishing is never assigned +without a fishery, and neither of these labors is assigned unless specifically +enabled (see below). The method by which labormanager determines what labor is needed for a -particular job is complicated and, in places, incomplete. In some -situations, labormanager will detect that it cannot determine what labor -is required. It will, by default, pause and print an error message on -the dfhack console, followed by the message "LABORMANAGER: Game paused -so you can investigate the above message.". If this happens, please open -an issue on github, reporting the lines that immediately preceded this -message. You can tell labormanager to ignore this error and carry on by -typing ``labormanager pause-on-error no``, but be warned that some job may go -undone in this situation. - -Advanced usage: - -:labormanager enable: Turn plugin on. -:labormanager disable: Turn plugin off. -:labormanager priority : Set the priority value (see above) for labor to . -:labormanager reset : Reset the priority value of labor to its default. -:labormanager reset-all: Reset all priority values to their defaults. -:labormanager allow-fishing: Allow dwarfs to fish. *Warning* This tends to result in most of the fort going fishing. -:labormanager forbid-fishing: Forbid dwarfs from fishing. Default behavior. -:labormanager allow-hunting: Allow dwarfs to hunt. *Warning* This tends to result in as many dwarfs going hunting as you have crossbows. -:labormanager forbid-hunting: Forbid dwarfs from hunting. Default behavior. -:labormanager list: Show current priorities and current allocation stats. -:labormanager pause-on-error yes: Make labormanager pause if the labor inference engine fails. See above. -:labormanager pause-on-error no: Allow labormanager to continue past a labor inference engine failure. +particular job is complicated and, in places, incomplete. In some situations, +labormanager will detect that it cannot determine what labor is required. It +will, by default, pause and print an error message on the dfhack console, +followed by the message "LABORMANAGER: Game paused so you can investigate the +above message.". If this happens, please open an :issue:`` on GitHub, +reporting the lines that immediately preceded this message. You can tell +labormanager to ignore this error and carry on by running +``labormanager pause-on-error no``, but be warned that some job may go undone in +this situation. + +Examples +-------- + +``labormanager priority BREWER 500`` + Boost the priority of brewing jobs. +``labormanager max FISH 1`` + Only assign fishing to one dwarf at a time. Note that you also have to run + ``labormanager allow-fishing`` for any dwarves to be assigned fishing at + all. + +Advanced usage +-------------- + +``labormanager list`` + Show current priorities and current allocation stats. Use this command to + see the IDs for all labors. +``labormanager status`` + Show basic status information. +``labormanager priority `` + Set the priority value for labor to . +``labormanager max `` + Set maximum number of dwarves that can be assigned to a labor. +``labormanager max none`` + Unrestrict the number of dwarves that can be assigned to a labor. +``labormanager max disable`` + Don't manage the specified labor. Dwarves who you have manually enabled this + labor on will be less likely to have managed labors assigned to them. +``labormanager reset-all|reset `` + Return a labor (or all labors) to the default priority. +``labormanager allow-fishing|forbid-fishing`` + Allow/disallow fisherdwarves. *Warning* Allowing fishing tends to result in + most of the fort going fishing. Fishing is forbidden by default. +``labormanager allow-hunting|forbid-hunting`` + Allow/disallow hunterdwarves. *Warning* Allowing hunting tends to result in + as many dwarves going hunting as you have crossbows. Hunting is forbidden by + default. +``labormanager pause-on-error yes|no`` + Make labormanager pause/continue if the labor inference engine fails. See + the above section for details. diff --git a/plugins/labormanager/labormanager.cpp b/plugins/labormanager/labormanager.cpp index 02569fd8b..f508c9797 100644 --- a/plugins/labormanager/labormanager.cpp +++ b/plugins/labormanager/labormanager.cpp @@ -845,38 +845,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector \n" - " Set max number of dwarves assigned to a labor.\n" - " labormanager max unmanaged\n" - " labormanager max disable\n" - " Don't attempt to manage this labor.\n" - " Any dwarves with unmanaged labors assigned will be less\n" - " likely to have managed labors assigned to them.\n" - " labormanager max none\n" - " Unrestrict the number of dwarves assigned to a labor.\n" - " labormanager priority \n" - " Change the assignment priority of a labor (default is 100)\n" - " labormanager reset \n" - " Return a labor to the default handling.\n" - " labormanager reset-all\n" - " Return all labors to the default handling.\n" - " labormanager list\n" - " List current status of all labors.\n" - " labormanager status\n" - " Show basic status information.\n" - "Function:\n" - " When enabled, labormanager periodically checks your dwarves and enables or\n" - " disables labors. Generally, each dwarf will be assigned exactly one labor.\n" - " Warning: labormanager will override any manual changes you make to labors\n" - " while it is enabled, except where the labor is marked as unmanaged.\n" - " Do not try to run both autolabor and labormanager at the same time.\n" - )); + "labormanager", + "Automatically manage dwarf labors.", + labormanager)); generate_labor_to_skill_map(); From 52a0b0f2ca8758dc792e5402f0393f75d8e4b9c7 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 17:14:30 -0700 Subject: [PATCH 159/334] update docs for lair --- docs/plugins/lair.rst | 19 ++++++++++++------- plugins/lair.cpp | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index b80e71e3a..b4a7ed524 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -1,12 +1,17 @@ lair ==== -This command allows you to mark the map as a monster lair, preventing item -scatter on abandon. When invoked as ``lair reset``, it does the opposite. +Tags: +:dfhack-keybind:`lair` -Unlike `reveal`, this command doesn't save the information about tiles - you -won't be able to restore state of real monster lairs using ``lair reset``. +Mark the map as a monster lair. This avoids item scatter when the fortress is +abandoned. -Options: +Usage: -:lair: Mark the map as monster lair -:lair reset: Mark the map as ordinary (not lair) +``lair`` + Mark the map as monster lair. +``lair reset`` + Mark the map as ordinary (not lair). + +This command doesn't save the information about tiles - you won't be able to +restore the state of a real monster lairs using ``lair reset``. diff --git a/plugins/lair.cpp b/plugins/lair.cpp index 6fb167988..7c3b1953f 100644 --- a/plugins/lair.cpp +++ b/plugins/lair.cpp @@ -61,7 +61,9 @@ command_result lair(color_ostream &out, std::vector & params) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("lair","Mark the map as a monster lair (avoids item scatter)",lair, false, - "Usage: 'lair' to mark entire map as monster lair, 'lair reset' to undo the operation.\n")); + commands.push_back(PluginCommand( + "lair", + "Mark the map as a monster lair to avoid item scatter.", + lair)); return CR_OK; } From ddae1aa90050a2917f6af008ad19470d324461cc Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 17:14:40 -0700 Subject: [PATCH 160/334] update docs for liquids --- docs/plugins/liquids.rst | 51 ++++++++++++++++++++++------------------ plugins/liquids.cpp | 18 +++++++------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index 75e41da5b..61e4bcfe2 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -2,30 +2,42 @@ liquids ======= -Allows adding magma, water and obsidian to the game. It replaces the normal -dfhack command line and can't be used from a hotkey. Settings will be remembered -as long as dfhack runs. Intended for use in combination with the command -``liquids-here`` (which can be bound to a hotkey). See also :issue:`80`. +Tags: +:dfhack-keybind:`liquids` +:dfhack-keybind:`liquids-here` -.. warning:: +Place magma, water or obsidian. See `gui/liquids` for an in-game interface for +this functionality. + +Also, if you only want to add or remove water or magma from a single tile, the +`source` script may be easier to use. + +Usage: - Spawning and deleting liquids can mess up pathing data and - temperatures (creating heat traps). You've been warned. +``liquids`` + Start the interactive terminal settings interpreter. This command must be + called from the DFHack terminal and not from any in-game interface. +``liquids-here`` + Run the liquid spawner with the current/last settings made in ``liquids`` + (if no settings in ``liquids`` were made, then it paints a point of 7/7 + magma by default). This command is intended to be used as keybinding, and it + requires an active in-game cursor. -.. note:: +.. warning:: - `gui/liquids` is an in-game UI for this script. + Spawning and deleting liquids can mess up pathing data and temperatures + (creating heat traps). You've been warned. -Settings will be remembered until you quit DF. You can call `liquids-here` to execute -the last configured action, which is useful in combination with keybindings. +Interactive interpreter +----------------------- -Usage: point the DF cursor at a tile you want to modify and use the commands. +The interpreter replaces the normal dfhack command line and can't be used from a +hotkey. Settings will be remembered as long as dfhack runs. It is intended for +use in combination with the command ``liquids-here`` (which *can* be bound to a +hotkey). -If you only want to add or remove water or magma from one tile, -`source` may be easier to use. +You can enter the following commands at the prompt. -Commands --------- Misc commands: :q: quit @@ -65,10 +77,3 @@ Brush size and shape: :block: DF map block with cursor in it (regular spaced 16x16x1 blocks) :column: Column from cursor, up through free space :flood: Flood-fill water tiles from cursor (only makes sense with wclean) - -liquids-here ------------- -Run the liquid spawner with the current/last settings made in liquids (if no -settings in liquids were made it paints a point of 7/7 magma by default). - -Intended to be used as keybinding. Requires an active in-game cursor. diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index 76c098c8a..980deb747 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -65,17 +65,15 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Wed, 27 Jul 2022 22:03:03 -0400 Subject: [PATCH 161/334] docs/build.py: Add support for xml and pseudoxml output formats Useful for debugging layout of generated docutils nodes --- docs/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/build.py b/docs/build.py index 2a42d0968..582279bcb 100755 --- a/docs/build.py +++ b/docs/build.py @@ -28,6 +28,8 @@ OUTPUT_FORMATS = { 'html': SphinxOutputFormat('html', pre_args=['-b', 'html']), 'text': SphinxOutputFormat('text', pre_args=['-b', 'text']), 'pdf': SphinxOutputFormat('pdf', pre_args=['-M', 'latexpdf']), + 'xml': SphinxOutputFormat('xml', pre_args=['-b', 'xml']), + 'pseudoxml': SphinxOutputFormat('pseudoxml', pre_args=['-b', 'pseudoxml']), } def _parse_known_args(parser, source_args): From 6aabf5f3c3ec84551fda0cb5118066de1c33a375 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 27 Jul 2022 17:21:34 -0700 Subject: [PATCH 162/334] no commas in index params --- docs/plugins/command-prompt.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 3b46e1b33..8e6c9e885 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -3,8 +3,8 @@ command-prompt Tags: :dfhack-keybind:`command-prompt` -:index:`An in-game DFHack terminal, where you can enter other commands. -` +:index:`An in-game DFHack terminal where you can enter other commands. +` Usage:: From 756900393f35a415af124da075e9cf2e2221a783 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 02:46:48 -0700 Subject: [PATCH 163/334] update docs for misery --- docs/plugins/misery.rst | 22 +++++++++++++--------- plugins/misery.cpp | 19 ++++--------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 1c146a10f..9a3a632f2 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -1,14 +1,18 @@ misery ====== -When enabled, fake bad thoughts will be added to all dwarves. +Tags: +:dfhack-keybind:`misery` + +Increase the intensity of negative dwarven thoughts. + +When enabled, negative thoughts that your dwarves have will multiply by the +specified factor. Usage: -:misery enable n: enable misery with optional magnitude n. If specified, n must - be positive. -:misery n: same as "misery enable n" -:misery enable: same as "misery enable 1" -:misery disable: stop adding new negative thoughts. This will not remove - existing negative thoughts. Equivalent to "misery 0". -:misery clear: remove fake thoughts, even after saving and reloading. Does - not change factor. +``enable misery`` + Start multiplying negative thoughts. +``misery `` + Change the multiplicative factor of bad thoughts. The default is ``2``. +``misery clear`` + Clear away negative thoughts added by ``misery``. diff --git a/plugins/misery.cpp b/plugins/misery.cpp index b8e11f5c0..224060c6b 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -128,21 +128,10 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) { } DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { - commands.push_back(PluginCommand("misery", "increase the intensity of negative dwarven thoughts", - &misery, false, - "misery: When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).\n" - "Usage:\n" - " misery enable n\n" - " enable misery with optional magnitude n. If specified, n must be positive.\n" - " misery n\n" - " same as \"misery enable n\"\n" - " misery enable\n" - " same as \"misery enable 2\"\n" - " misery disable\n" - " stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to \"misery 1\"\n" - " misery clear\n" - " remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.\n\n" - )); + commands.push_back(PluginCommand( + "misery", + "Increase the intensity of negative dwarven thoughts.", + misery)); return CR_OK; } From b6f20ee61a543423e7f2906c44d99b01dd5b15fe Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 02:49:46 -0700 Subject: [PATCH 164/334] update docs for manipulator --- docs/plugins/manipulator.rst | 43 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 024438d85..305499308 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -1,13 +1,20 @@ manipulator =========== -An in-game equivalent to the popular program Dwarf Therapist. +Tags: + +An in-game labor management interface. It is equivalent to the popular Dwarf +Therapist utility. To activate, open the unit screen and press :kbd:`l`. +Usage:: + + enable manipulator + .. image:: ../images/manipulator.png -The far left column displays the unit's Happiness (color-coded based on its -value), Name, Profession/Squad, and the right half of the screen displays each +The far left column displays the unit's name, happiness (color-coded based on +its value), profession, or squad, and the right half of the screen displays each dwarf's labor settings and skill levels (0-9 for Dabbling through Professional, A-E for Great through Grand Master, and U-Z for Legendary through Legendary+5). @@ -58,12 +65,14 @@ The manipulator plugin supports saving professions: a named set of labors that c quickly applied to one or multiple dwarves. To save a profession, highlight a dwarf and press :kbd:`P`. The profession will be saved using -the custom profession name of the dwarf, or the default for that dwarf if no custom profession -name has been set. +the custom profession name of the dwarf, or the default profession name for that dwarf if no +custom profession name has been set. To apply a profession, either highlight a single dwarf or select multiple with :kbd:`x`, and press :kbd:`p` to select the profession to apply. All labors for -the selected dwarves will be reset to the labors of the chosen profession. +the selected dwarves will be reset to the labors of the chosen profession and +the custom profession names for those dwarves will be set to the applied +profession. Professions are saved as human-readable text files in the ``dfhack-config/professions`` folder within the DF folder, and can be edited or @@ -96,13 +105,8 @@ Chef 0 3 Buchery, Tanning, and Cooking. It is important to Craftsdwarf 0 4-6 All labors used at Craftsdwarf's workshops, Glassmaker's workshops, and kilns. Doctor 0 2-4 The full suite of medical labors, plus Animal - Caretaking for those using the dwarfvet plugin. -Farmer 1 4 Food- and animal product-related labors. This - profession also has the ``Alchemist`` labor - enabled since they need to focus on food-related - jobs, though you might want to disable - ``Alchemist`` for your first farmer until there - are actual farming duties to perform. + Caretaking for those using the `dwarfvet` plugin. +Farmer 1 4 Food- and animal product-related labors. Fisherdwarf 0 0-1 Fishing and fish cleaning. If you assign this profession to any dwarf, be prepared to be inundated with fish. Fisherdwarves *never stop @@ -117,24 +121,21 @@ Hauler 0 >20 All hauling labors plus Siege Operating, Mechanic hauling labors for other dwarves so they can focus on their skilled tasks. You may also want to restrict your Mechanic's workshops to only - skilled mechanics so your haulers don't make - low-quality mechanisms. + skilled mechanics so your unskilled haulers don't + make low-quality mechanisms. Laborer 0 10-12 All labors that don't improve quality with skill, such as Soapmaking and furnace labors. Marksdwarf 0 10-30 Similar to Hauler. See the description for Meleedwarf below for more details. -Mason 2 2-4 Masonry and Gem Cutting/Encrusting. In the early - game, you may need to run "`prioritize` - ConstructBuilding" to get your masons to build - wells and bridges if they are too busy crafting - stone furniture. +Mason 2 2-4 Masonry and Gem Cutting/Encrusting. Meleedwarf 0 20-50 Similar to Hauler, but without most civilian labors. This profession is separate from Hauler so you can find your military dwarves easily. Meleedwarves and Marksdwarves have Mechanics and hauling labors enabled so you can temporarily deactivate your military after sieges and allow - your military dwarves to help clean up. + your military dwarves to help clean up and reset + traps. Migrant 0 0 You can assign this profession to new migrants temporarily while you sort them into professions. Like Marksdwarf and Meleedwarf, the purpose of From bcab483b57dce9f50846910e4af80ce454f7cf32 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 02:50:37 -0700 Subject: [PATCH 165/334] update docs for mode --- docs/plugins/mode.rst | 33 +++++++++++++++++++++++++-------- plugins/mode.cpp | 10 ++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 9b3fc6984..225a49e1e 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -1,5 +1,8 @@ mode ==== +Tags: +:dfhack-keybind:`mode` + This command lets you see and change the game mode directly. .. warning:: @@ -9,13 +12,27 @@ This command lets you see and change the game mode directly. Not all combinations are good for every situation and most of them will produce undesirable results. There are a few good ones though. -Examples: +Usage: + +``mode`` + Print the current game mode. +``mode set`` + Enter an interactive commandline menu where you can set the game mode. + +Examples +-------- + +Scenario 1: + +* You are in fort game mode, managing your fortress and paused. +* You switch to the arena game mode, *assume control of a creature* and then +* switch to adventure game mode. + +You just lost a fortress and gained an adventurer. + +Scenario 2: - * You are in fort game mode, managing your fortress and paused. - * You switch to the arena game mode, *assume control of a creature* and then - * switch to adventure game mode(1). - You just lost a fortress and gained an adventurer. Alternatively: +* You are in fort game mode, managing your fortress and paused at the Esc menu. +* You switch to the adventure game mode, assume control of a creature, then save or retire. - * You are in fort game mode, managing your fortress and paused at the esc menu. - * You switch to the adventure game mode, assume control of a creature, then save or retire. - * You just created a returnable mountain home and gained an adventurer. +You just created a returnable mountain home and gained an adventurer. diff --git a/plugins/mode.cpp b/plugins/mode.cpp index 5e58c50a0..aeda6380b 100644 --- a/plugins/mode.cpp +++ b/plugins/mode.cpp @@ -19,12 +19,10 @@ DFHACK_PLUGIN("mode"); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "mode","View, change and track game mode.", - mode, true, - " Without any parameters, this command prints the current game mode\n" - " You can interactively set the game mode with 'mode set'.\n" - "!!Setting the game modes can be dangerous and break your game!!\n" - )); + "mode", + "View, change and track game mode.", + mode, + true)); return CR_OK; } From f68b58c46047396cc2132618d0b0729f752d3ae3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 03:22:32 -0700 Subject: [PATCH 166/334] update docs for mousequery --- docs/Tags.rst | 35 ++++++++++++++++---------------- docs/plugins/mousequery.rst | 40 +++++++++++++++++++++++++++---------- plugins/mousequery.cpp | 16 +++------------ 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 432632ba1..294358bd8 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,19 +1,20 @@ :orphan: -- `tag/adventure`: Tools that are useful while in adventure mode -- `tag/fort`: Tools that are useful while in fort mode -- `tag/legends`: Tools that are useful while in legends mode -- `tag/items`: Tools that create or modify in-game items -- `tag/units`: Tools that create or modify units -- `tag/jobs`: Tools that create or modify jobs -- `tag/labors`: Tools that deal with labor assignment -- `tag/auto`: Tools that automatically manage some aspect of your fortress -- `tag/map`: Map modification -- `tag/system`: Tools related to working with DFHack commands or the core DFHack library -- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster -- `tag/animals`: Tools that help you manage animals -- `tag/fix`: Tools that fix specific bugs -- `tag/inspection`: Tools that let you inspect game data -- `tag/buildings`: Tools that help you work wtih placing or configuring buildings and furniture -- `tag/quickfort`: Tools that are involved in creating and playing back blueprints -- `tag/dev`: Tools useful for develpers and modders +- `tag/adventure`: Tools that are useful while in adventure mode. +- `tag/fort`: Tools that are useful while in fort mode. +- `tag/legends`: Tools that are useful while in legends mode. +- `tag/items`: Tools that create or modify in-game items. +- `tag/units`: Tools that create or modify units. +- `tag/jobs`: Tools that create or modify jobs. +- `tag/labors`: Tools that deal with labor assignment. +- `tag/auto`: Tools that automatically manage some aspect of your fortress. +- `tag/map`: Map modification. +- `tag/system`: Tools related to working with DFHack commands or the core DFHack library. +- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. +- `tag/animals`: Tools that help you manage animals. +- `tag/fix`: Tools that fix specific bugs. +- `tag/inspection`: Tools that let you inspect game data. +- `tag/buildings`: Tools that help you work wtih placing or configuring buildings and furniture. +- `tag/quickfort`: Tools that are involved in creating and playing back blueprints. +- `tag/dev`: Tools useful for develpers and modders. +- `tag/interface`: Tools that modify or extend the user interface. diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index 2f613acaa..eedca7646 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -1,16 +1,36 @@ mousequery ========== -Adds mouse controls to the DF interface, e.g. click-and-drag designations. +Tags: `tag/fort`, `tag/interface` +:dfhack-keybind:`mousequery` -Options: - -:plugin: enable/disable the entire plugin -:rbutton: enable/disable right mouse button -:track: enable/disable moving cursor in build and designation mode -:edge: enable/disable active edge scrolling (when on, will also enable tracking) -:live: enable/disable query view when unpaused -:delay: Set delay when edge scrolling in tracking mode. Omit amount to display current setting. +Adds mouse controls to the DF interface. For example, with ``mousequery`` you +can click on buildings to configure them, hold the mouse button to draw dig +designations, or click and drag to move the map around. Usage:: - mousequery [plugin] [rbutton] [track] [edge] [live] [enable|disable] + enable mousequery + mousequery [rbutton|track|edge|live] [enable|disable] + mousequery drag [left|right|disable] + mousequery delay [] + +:rbutton: When the right mouse button is clicked, cancel out of menus or + scroll the main map if you r-click near an edge. +:track: Move the cursor with the mouse instead of the cursor keys when you + are in build or designation modes. +:edge: Scroll the map when you move the cursor to a map edge. See ``delay`` + below. If enabled also enables ``track``. +:delay: Set delay in milliseconds for map edge scrolling. Omit the amount to + display the current setting. +:live: Display information in the lower right corner of the screen about + the items/building/tile under the cursor, even while unpaused. + +Examples +-------- + +``mousequery rbutton enable`` + Enable using the right mouse button to cancel out of menus and scroll the + map. +``mousequery edge enable`` +``mousequery delay 300`` + Enable edge scrolling and set the delay to 300ms. diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 618252ad3..cd85de2bc 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -916,19 +916,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector \n" - " Set delay when edge scrolling in tracking mode. Omit amount to display current setting.\n" - )); + "mousequery", + "Add mouse functionality to Dwarf Fortress.", + mousequery_cmd)); return CR_OK; } From 88648284b0d72a2c25086c4fcd1a0d5486179a04 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 03:25:00 -0700 Subject: [PATCH 167/334] update docs for nestboxes --- docs/plugins/nestboxes.rst | 11 +++++++++-- plugins/nestboxes.cpp | 11 ++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst index 2b633f31b..fb9df20a9 100644 --- a/docs/plugins/nestboxes.rst +++ b/docs/plugins/nestboxes.rst @@ -1,5 +1,12 @@ nestboxes ========= +Tags: -Automatically scan for and forbid fertile eggs incubating in a nestbox. -Toggle status with `enable` or `disable `. +Protect fertile eggs incubating in a nestbox. This plugin will automatically +scan for and forbid fertile eggs incubating in a nestbox so that dwarves won't +come to collect them for eating. The eggs will hatch normally, even when +forbidden. + +Usage:: + + enable nestboxes diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 8b9a33289..403a8cb37 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -69,13 +69,10 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector Date: Thu, 28 Jul 2022 03:48:27 -0700 Subject: [PATCH 168/334] update docs for orders --- docs/plugins/orders.rst | 95 +++++++++++++++++++++++++---------------- plugins/orders.cpp | 17 +------- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index eb0e8a7ca..2506bc582 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -1,31 +1,51 @@ orders ====== - -A plugin for manipulating manager orders. - -Subcommands: - -:list: Shows the list of previously exported orders, including the orders library. -:export NAME: Exports the current list of manager orders to a file named ``dfhack-config/orders/NAME.json``. -:import NAME: Imports manager orders from a file named ``dfhack-config/orders/NAME.json``. -:clear: Deletes all manager orders in the current embark. -:sort: Sorts current manager orders by repeat frequency so daily orders don't - prevent other orders from ever being completed: one-time orders first, then - yearly, seasonally, monthly, then finally daily. +Tags: +:dfhack-keybind:`orders` + +Manage manager orders. + +Usage: + +``orders orders list`` + Shows the list of previously exported orders, including the orders library. +``orders export `` + Saves all the current manager orders in a file. +``orders import `` + Imports the specified manager orders. Note this adds to your current set of + manager orders. It will not clear the orders that already exist. +``orders clear`` + Deletes all manager orders in the current embark. +``orders sort`` + Sorts current manager orders by repeat frequency so repeating orders don't + prevent one-time orders from ever being completed. The sorting order is: + one-time orders first, then yearly, seasonally, monthly, and finally, daily. You can keep your orders automatically sorted by adding the following command to your ``onMapLoad.init`` file:: repeat -name orders-sort -time 1 -timeUnits days -command [ orders sort ] +Exported orders are saved in the ``dfhack-config/orders`` directory, where you +can view, edit, and delete them, if desired. + +Examples +-------- + +``orders export myorders`` + Export the current manager orders to a file named + ``dfhack-config/orders/myorders.json``. +``orders import library/basic`` + Import manager orders from the library that keep your fort stocked with + basic essentials. The orders library ------------------ DFHack comes with a library of useful manager orders that are ready for import: -:source:`basic.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/basic ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This collection of orders handles basic fort necessities: @@ -45,12 +65,12 @@ This collection of orders handles basic fort necessities: You should import it as soon as you have enough dwarves to perform the tasks. Right after the first migration wave is usually a good time. -:source:`furnace.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/furnace ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This collection creates basic items that require heat. It is separated out from -``basic.json`` to give players the opportunity to set up magma furnaces first in -order to save resources. It handles: +``library/basic`` to give players the opportunity to set up magma furnaces first +in order to save resources. It handles: - charcoal (including smelting of bituminous coal and lignite) - pearlash @@ -61,8 +81,8 @@ order to save resources. It handles: Orders are missing for plaster powder until DF :bug:`11803` is fixed. -:source:`military.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/military ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This collection adds high-volume smelting jobs for military-grade metal ores and produces weapons and armor: @@ -83,33 +103,36 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. -:source:`smelting.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/smelting ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This collection adds smelting jobs for all ores. It includes handling the ores -already managed by ``military.json``, but has lower limits. This ensures all -ores will be covered if a player imports ``smelting`` but not ``military``, but -the higher-volume ``military`` orders will take priority if both are imported. +already managed by ``library/military``, but has lower limits. This ensures all +ores will be covered if a player imports ``library/smelting`` but not +``library/military``, but the higher-volume ``library/military`` orders will +take priority if both are imported. -:source:`rockstock.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/rockstock ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This collection of orders keeps a small stock of all types of rock furniture. This allows you to do ad-hoc furnishings of guildhalls, libraries, temples, or other rooms with `buildingplan` and your masons will make sure there is always stock on hand to fulfill the plans. -:source:`glassstock.json ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`library/glassstock ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Similar to ``rockstock`` above, this collection keeps a small stock of all types -of glass furniture. If you have a functioning glass industry, this is more -sustainable than ``rockstock`` since you can never run out of sand. If you have -plenty of rock and just want the variety, you can import both ``rockstock`` and -``glassstock`` to get a mixture of rock and glass furnishings in your fort. +Similar to ``library/rockstock`` above, this collection keeps a small stock of +all types of glass furniture. If you have a functioning glass industry, this is +more sustainable than ``library/rockstock`` since you can never run out of sand. +If you have plenty of rock and just want the variety, you can import both +``library/rockstock`` and ``library/glassstock`` to get a mixture of rock and +glass furnishings in your fort. -There are a few items that ``glassstock`` produces that ``rockstock`` does not, -since there are some items that can not be made out of rock, for example: +There are a few items that ``library/glassstock`` produces that +``library/rockstock`` does not, since there are some items that can not be made +out of rock, for example: - tubes and corkscrews for building magma-safe screw pumps - windows diff --git a/plugins/orders.cpp b/plugins/orders.cpp index 9e1bf3e1a..800a0db6e 100644 --- a/plugins/orders.cpp +++ b/plugins/orders.cpp @@ -49,21 +49,8 @@ DFhackCExport command_result plugin_init(color_ostream & out, std::vector Date: Thu, 28 Jul 2022 03:49:52 -0700 Subject: [PATCH 169/334] fix formatting in mousequery docs --- docs/plugins/mousequery.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index eedca7646..39f401bb2 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -31,6 +31,6 @@ Examples ``mousequery rbutton enable`` Enable using the right mouse button to cancel out of menus and scroll the map. -``mousequery edge enable`` ``mousequery delay 300`` - Enable edge scrolling and set the delay to 300ms. + When run after ``mousequery edge enable``, sets the edge scrolling delay to + 300ms. From a6fb509cc7076c1f304571fb2c960b80b2b0be01 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 04:05:41 -0700 Subject: [PATCH 170/334] convert table to list so it's readable in text rendering --- docs/plugins/manipulator.rst | 140 +++++++++++++++++------------------ 1 file changed, 66 insertions(+), 74 deletions(-) diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 305499308..c8140a3bf 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -90,82 +90,74 @@ want to import to a dwarf. Once you have assigned a profession to at least one dwarf, you can select "Import Professions from DF" in the DT "File" menu. The professions will then be available for use in DT. -In the charts below, the "At Start" and "Max" columns indicate the approximate -number of dwarves of each profession that you are likely to need at the start of -the game and how many you are likely to need in a mature fort. These are just +In the list below, the "needed" range indicates the approximate number of +dwarves of each profession that you are likely to need at the start of the game +and how many you are likely to need in a mature fort. These are just approximations. Your playstyle may demand more or fewer of each profession. -============= ======== ===== ================================================= -Profession At Start Max Description -============= ======== ===== ================================================= -Chef 0 3 Buchery, Tanning, and Cooking. It is important to - focus just a few dwarves on cooking since - well-crafted meals make dwarves very happy. They - are also an excellent trade good. -Craftsdwarf 0 4-6 All labors used at Craftsdwarf's workshops, - Glassmaker's workshops, and kilns. -Doctor 0 2-4 The full suite of medical labors, plus Animal - Caretaking for those using the `dwarfvet` plugin. -Farmer 1 4 Food- and animal product-related labors. -Fisherdwarf 0 0-1 Fishing and fish cleaning. If you assign this - profession to any dwarf, be prepared to be - inundated with fish. Fisherdwarves *never stop - fishing*. Be sure to also run ``prioritize -a - PrepareRawFish ExtractFromRawFish`` or else - caught fish will just be left to rot. -Hauler 0 >20 All hauling labors plus Siege Operating, Mechanic - (so haulers can assist in reloading traps) and - Architecture (so haulers can help build massive - windmill farms and pump stacks). As you - accumulate enough Haulers, you can turn off - hauling labors for other dwarves so they can - focus on their skilled tasks. You may also want - to restrict your Mechanic's workshops to only - skilled mechanics so your unskilled haulers don't - make low-quality mechanisms. -Laborer 0 10-12 All labors that don't improve quality with skill, - such as Soapmaking and furnace labors. -Marksdwarf 0 10-30 Similar to Hauler. See the description for - Meleedwarf below for more details. -Mason 2 2-4 Masonry and Gem Cutting/Encrusting. -Meleedwarf 0 20-50 Similar to Hauler, but without most civilian - labors. This profession is separate from Hauler - so you can find your military dwarves easily. - Meleedwarves and Marksdwarves have Mechanics and - hauling labors enabled so you can temporarily - deactivate your military after sieges and allow - your military dwarves to help clean up and reset - traps. -Migrant 0 0 You can assign this profession to new migrants - temporarily while you sort them into professions. - Like Marksdwarf and Meleedwarf, the purpose of - this profession is so you can find your new - dwarves more easily. -Miner 2 2-10 Mining and Engraving. This profession also has - the ``Alchemist`` labor enabled, which disables - hauling for those using the `autohauler` plugin. - Once the need for Miners tapers off in the late - game, dwarves with this profession make good - military dwarves, wielding their picks as - weapons. -Outdoorsdwarf 1 2-4 Carpentry, Bowyery, Woodcutting, Animal Training, - Trapping, Plant Gathering, Beekeeping, and Siege - Engineering. -Smith 0 2-4 Smithing labors. You may want to specialize your - Smiths to focus on a single smithing skill to - maximize equipment quality. -StartManager 1 0 All skills not covered by the other starting - professions (Miner, Mason, Outdoorsdwarf, and - Farmer), plus a few overlapping skills to - assist in critical tasks at the beginning of the - game. Individual labors should be turned off as - migrants are assigned more specialized - professions that cover them, and the StartManager - dwarf can eventually convert to some other - profession. -Tailor 0 2 Textile industry labors: Dying, Leatherworking, - Weaving, and Clothesmaking. -============= ======== ===== ================================================= +- ``Chef`` (needed: 0, 3) + Buchery, Tanning, and Cooking. It is important to focus just a few dwarves + on cooking since well-crafted meals make dwarves very happy. They are also + an excellent trade good. +- ``Craftsdwarf`` (needed: 0, 4-6) + All labors used at Craftsdwarf's workshops, Glassmaker's workshops, and + kilns. +``Doctor`` (needed: 0, 2-4) + The full suite of medical labors, plus Animal Caretaking for those using + the `dwarfvet` plugin. +``Farmer`` (needed 1, 4) + Food- and animal product-related labors. +``Fisherdwarf`` (needed 0, 0-1) + Fishing and fish cleaning. If you assign this profession to any dwarf, be + prepared to be inundated with fish. Fisherdwarves *never stop fishing*. Be + sure to also run ``prioritize -a PrepareRawFish ExtractFromRawFish`` or else + caught fish will just be left to rot. +``Hauler`` (needed 0, >20) + All hauling labors plus Siege Operating, Mechanic (so haulers can assist in + reloading traps) and Architecture (so haulers can help build massive + windmill farms and pump stacks). As you accumulate enough Haulers, you can + turn off hauling labors for other dwarves so they can focus on their skilled + tasks. You may also want to restrict your Mechanic's workshops to only + skilled mechanics so your unskilled haulers don't make low-quality + mechanisms. +``Laborer`` (needed 0, 10-12) + All labors that don't improve quality with skill, such as Soapmaking and + furnace labors. +``Marksdwarf`` (needed 0, 10-30) + Similar to ``Hauler``. See the description for ``Meleedwarf`` below for more + details. +``Mason`` (needed 2, 2-4) + Masonry and Gem Cutting/Encrusting. +``Meleedwarf`` (needed 0, 20-50) + Similar to ``Hauler``, but without most civilian labors. This profession is + separate from ``Hauler`` so you can find your military dwarves easily. + ``Meleedwarves`` and ``Marksdwarves`` have Mechanics and hauling labors + enabled so you can temporarily deactivate your military after sieges and + allow your military dwarves to help clean up and reset traps. +``Migrant`` (needed 0, 0) + You can assign this profession to new migrants temporarily while you sort + them into professions. Like ``Marksdwarf`` and ``Meleedwarf``, the purpose + of this profession is so you can find your new dwarves more easily. +``Miner`` (needed 2, 2-10) + Mining and Engraving. This profession also has the ``Alchemist`` labor + enabled, which disables hauling for those using the `autohauler` plugin. + Once the need for Miners tapers off in the late game, dwarves with this + profession make good military dwarves, wielding their picks as weapons. +``Outdoorsdwarf`` (needed 1, 2-4) + Carpentry, Bowyery, Woodcutting, Animal Training, Trapping, Plant Gathering, + Beekeeping, and Siege Engineering. +``Smith`` (needed 0, 2-4) + Smithing labors. You may want to specialize your Smiths to focus on a single + smithing skill to maximize equipment quality. +``StartManager`` (needed 1, 0) + All skills not covered by the other starting professions (``Miner``, + ``Mason``, ``Outdoorsdwarf``, and ``Farmer``), plus a few overlapping skills + to assist in critical tasks at the beginning of the game. Individual labors + should be turned off as migrants are assigned more specialized professions + that cover them, and the StartManager dwarf can eventually convert to some + other profession. +``Tailor`` (needed 0, 2) + Textile industry labors: Dying, Leatherworking, Weaving, and Clothesmaking. A note on autohauler ~~~~~~~~~~~~~~~~~~~~ From 5954d7a19a2875571aec7b742a4ddb3149968f53 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 05:32:30 -0700 Subject: [PATCH 171/334] make docs for plug match the implementation --- docs/builtins/plug.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 26699d400..963660bbb 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -11,5 +11,4 @@ Usage: ``plug`` Lists available plugins and whether they are enabled. ``plug [ ...]`` - Shows the commands implemented by the named plugins and whether the plugins - are enabled. + Lists only the named plugins. From ac2d943fb129037c9bd580eae958bf7d3f4b23ca Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 05:41:29 -0700 Subject: [PATCH 172/334] remove commas from cleaners index entry --- docs/plugins/cleaners.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 305bf0db6..4a6aee1e3 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -7,11 +7,11 @@ Tags: :dfhack-keybind:`clean` :dfhack-keybind:`spotclean` -:index:`Removes contaminants from tiles, items, and units. -` More specifically, -it cleans all the splatter that get scattered all over the map and that clings -to your items and units. In an old fortress, this can significantly reduce FPS -lag. It can also spoil your !!FUN!!, so think before you use it. +:index:`Removes contaminants. ` More +specifically, it cleans all the splatter that get scattered all over the map and +that clings to your items and units. In an old fortress, cleaning with this tool +can significantly reduce FPS lag. It can also spoil your !!FUN!!, so think +before you use it. Usage:: From 380b003b5664211ad9aa0b86a7aa660be26f5f8d Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 06:05:35 -0700 Subject: [PATCH 173/334] fix list formatting for manipulator --- docs/plugins/manipulator.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index c8140a3bf..87ac4e088 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -102,17 +102,17 @@ approximations. Your playstyle may demand more or fewer of each profession. - ``Craftsdwarf`` (needed: 0, 4-6) All labors used at Craftsdwarf's workshops, Glassmaker's workshops, and kilns. -``Doctor`` (needed: 0, 2-4) +- ``Doctor`` (needed: 0, 2-4) The full suite of medical labors, plus Animal Caretaking for those using the `dwarfvet` plugin. -``Farmer`` (needed 1, 4) +- ``Farmer`` (needed 1, 4) Food- and animal product-related labors. -``Fisherdwarf`` (needed 0, 0-1) +- ``Fisherdwarf`` (needed 0, 0-1) Fishing and fish cleaning. If you assign this profession to any dwarf, be prepared to be inundated with fish. Fisherdwarves *never stop fishing*. Be sure to also run ``prioritize -a PrepareRawFish ExtractFromRawFish`` or else caught fish will just be left to rot. -``Hauler`` (needed 0, >20) +- ``Hauler`` (needed 0, >20) All hauling labors plus Siege Operating, Mechanic (so haulers can assist in reloading traps) and Architecture (so haulers can help build massive windmill farms and pump stacks). As you accumulate enough Haulers, you can @@ -120,43 +120,43 @@ approximations. Your playstyle may demand more or fewer of each profession. tasks. You may also want to restrict your Mechanic's workshops to only skilled mechanics so your unskilled haulers don't make low-quality mechanisms. -``Laborer`` (needed 0, 10-12) +- ``Laborer`` (needed 0, 10-12) All labors that don't improve quality with skill, such as Soapmaking and furnace labors. -``Marksdwarf`` (needed 0, 10-30) +- ``Marksdwarf`` (needed 0, 10-30) Similar to ``Hauler``. See the description for ``Meleedwarf`` below for more details. -``Mason`` (needed 2, 2-4) +- ``Mason`` (needed 2, 2-4) Masonry and Gem Cutting/Encrusting. -``Meleedwarf`` (needed 0, 20-50) +- ``Meleedwarf`` (needed 0, 20-50) Similar to ``Hauler``, but without most civilian labors. This profession is separate from ``Hauler`` so you can find your military dwarves easily. ``Meleedwarves`` and ``Marksdwarves`` have Mechanics and hauling labors enabled so you can temporarily deactivate your military after sieges and allow your military dwarves to help clean up and reset traps. -``Migrant`` (needed 0, 0) +- ``Migrant`` (needed 0, 0) You can assign this profession to new migrants temporarily while you sort them into professions. Like ``Marksdwarf`` and ``Meleedwarf``, the purpose of this profession is so you can find your new dwarves more easily. -``Miner`` (needed 2, 2-10) +- ``Miner`` (needed 2, 2-10) Mining and Engraving. This profession also has the ``Alchemist`` labor enabled, which disables hauling for those using the `autohauler` plugin. Once the need for Miners tapers off in the late game, dwarves with this profession make good military dwarves, wielding their picks as weapons. -``Outdoorsdwarf`` (needed 1, 2-4) +- ``Outdoorsdwarf`` (needed 1, 2-4) Carpentry, Bowyery, Woodcutting, Animal Training, Trapping, Plant Gathering, Beekeeping, and Siege Engineering. -``Smith`` (needed 0, 2-4) +- ``Smith`` (needed 0, 2-4) Smithing labors. You may want to specialize your Smiths to focus on a single smithing skill to maximize equipment quality. -``StartManager`` (needed 1, 0) +- ``StartManager`` (needed 1, 0) All skills not covered by the other starting professions (``Miner``, ``Mason``, ``Outdoorsdwarf``, and ``Farmer``), plus a few overlapping skills to assist in critical tasks at the beginning of the game. Individual labors should be turned off as migrants are assigned more specialized professions that cover them, and the StartManager dwarf can eventually convert to some other profession. -``Tailor`` (needed 0, 2) +- ``Tailor`` (needed 0, 2) Textile industry labors: Dying, Leatherworking, Weaving, and Clothesmaking. A note on autohauler From a09b35c2bd5e273a4fe7666ee7791e3f3a8d4272 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 15:32:26 -0700 Subject: [PATCH 174/334] ignore generated directories --- .gitignore | 2 ++ conf.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 5155f2bb9..0711d9b17 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,10 @@ build/VC2010 docs/changelogs/ docs/html/ docs/pdf/ +docs/pseudoxml/ docs/text/ docs/tools/ +docs/xml/ # in-place build build/Makefile diff --git a/conf.py b/conf.py index fab4b286a..b01027a6b 100644 --- a/conf.py +++ b/conf.py @@ -310,7 +310,10 @@ exclude_patterns = [ 'docs/tags/*', 'docs/text/*', 'docs/builtins/*', + 'docs/pdf/*', 'docs/plugins/*', + 'docs/pseudoxml/*', + 'docs/xml/*', 'scripts/docs/*', ] From 2e2abbe87ad4610999fea2a03e064881bc74951f Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 16:46:10 -0700 Subject: [PATCH 175/334] update docs for petcapRemover and make it actually start running when it is enabled --- docs/plugins/petcapRemover.rst | 41 ++++++++++++++++++++++------------ plugins/petcapRemover.cpp | 18 +++++---------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index 65ebaeeab..e3ce47916 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -1,19 +1,32 @@ petcapRemover ============= -Allows you to remove or raise the pet population cap. In vanilla -DF, pets will not reproduce unless the population is below 50 and the number of -children of that species is below a certain percentage. This plugin allows -removing the second restriction and removing or raising the first. Pets still -require PET or PET_EXOTIC tags in order to reproduce. Type ``help petcapRemover`` -for exact usage. In order to make population more stable and avoid sudden -population booms as you go below the raised population cap, this plugin counts -pregnancies toward the new population cap. It can still go over, but only in the -case of multiple births. +Tags: +:dfhack-keybind:`petcapRemover` + +Modify the pet population cap. In vanilla DF, pets will not reproduce unless the +population is below 50 and the number of children of that species is below a +certain percentage. This plugin allows removing these restrictions and setting +your own thresholds. Pets still require PET or PET_EXOTIC tags in order to +reproduce. In order to make population more stable and avoid sudden population +booms as you go below the raised population cap, this plugin counts pregnancies +toward the new population cap. It can still go over, but only in the case of +multiple births. Usage: -:petcapRemover: cause pregnancies now and schedule the next check -:petcapRemover every n: set how often in ticks the plugin checks for possible pregnancies -:petcapRemover cap n: set the new cap to n. if n = 0, no cap -:petcapRemover pregtime n: sets the pregnancy duration to n ticks. natural pregnancies are - 300000 ticks for the current race and 200000 for everyone else +``enable petcapRemover`` + Enables the plugin and starts running with default settings. +``petcapRemover cap `` + Set the new population cap per species to the specified value. If set to 0, + then there is no cap (good luck with all those animals!). The default cap + is 100. +``petcapRemover`` + Impregnate female pets that have access to a compatible male, up to the + population cap. +``petcapRemover every `` + Set how often the plugin will cause pregnancies. The default frequency is + every 10,000 ticks (a little over 8 game days). +``petcapRemover pregtime `` + Sets the pregnancy duration to the specified number of ticks. The default + value is 200,000 ticks, which is the natural pet pregnancy duration. + diff --git a/plugins/petcapRemover.cpp b/plugins/petcapRemover.cpp index 7d6588cbc..2c1de318d 100644 --- a/plugins/petcapRemover.cpp +++ b/plugins/petcapRemover.cpp @@ -36,18 +36,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector params; + return petcapRemover(out, params); } } From 2d400fb571c37c2681ff0b8f112cde9d8d106077 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 22:51:17 -0700 Subject: [PATCH 176/334] update docs for plants n.b. original docs were a lie. --- docs/plugins/plants.rst | 34 ++++++++++++++++++++++------------ plugins/plants.cpp | 19 ++++++------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index 0111b0770..4fc87f353 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -1,18 +1,28 @@ .. _plant: -plant -===== -A tool for creating shrubs, growing, or getting rid of them. +plants +====== +Tags: +:dfhack-keybind:`plant` -Subcommands: +Grow shrubs or trees. -:create: Creates a new sapling under the cursor. Takes a raw ID as argument - (e.g. TOWER_CAP). The cursor must be located on a dirt or grass floor tile. -:grow: Turns saplings into trees; under the cursor if a sapling is selected, - or every sapling on the map if the cursor is hidden. +Usage: -For mass effects, use one of the additional options: +``plant create `` + Creates a new plant of the specified type at the active cursor position. + The cursor must be on a dirt or grass floor tile. +``plant grow`` + Grows saplings into trees. If the cursor is active, it only affects the + sapling under the cursor. If no cursor is active, it affect all saplings + on the map. -:shrubs: affect all shrubs on the map -:trees: affect all trees on the map -:all: affect every plant! +To see the full list of plant ids, run the following command:: + + devel/query --table df.global.world.raws.plants.all --search ^id --maxdepth 1 + +Example +------- + +``plant create TOWER_CAP`` + Create a Tower Cap sapling at the cursor position. diff --git a/plugins/plants.cpp b/plugins/plants.cpp index 69e712ef8..2325c01e3 100644 --- a/plugins/plants.cpp +++ b/plugins/plants.cpp @@ -32,10 +32,7 @@ command_result df_grow (color_ostream &out, vector & parameters) { if(parameters[i] == "help" || parameters[i] == "?") { - out.print("Usage:\n" - "This command turns all living saplings on the map into full-grown trees.\n" - "With active cursor, work on the targetted one only.\n"); - return CR_OK; + return CR_WRONG_USAGE; } } @@ -91,11 +88,7 @@ command_result df_createplant (color_ostream &out, vector & parameters) { if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?")) { - out.print("Usage:\n" - "Create a new plant at the cursor.\n" - "Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n" - "Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"); - return CR_OK; + return CR_WRONG_USAGE; } CoreSuspender suspend; @@ -201,10 +194,10 @@ command_result df_plant (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false, - "Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n" - "plant grow help - Grows saplings into trees.\n" - "plant create help - Create a new plant.\n")); + commands.push_back(PluginCommand( + "plant", + "Grow shrubs or trees.", + df_plant)); return CR_OK; } From 1bbe996d581a617afeb1b34019665cdcb437e763 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 22:55:20 -0700 Subject: [PATCH 177/334] update docs for power-meter --- docs/plugins/power-meter.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 163ef91be..9443f2a93 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -1,5 +1,13 @@ power-meter =========== +Tags: + +Provides the backend for `gui/power-meter`. + +Usage:: + + enable power-meter + The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles. From cf69a1a2cf6ca82f5bff87247902c0e13f39b215 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 23:04:07 -0700 Subject: [PATCH 178/334] update docs for probe --- docs/plugins/probe.rst | 26 +++++++++++++++++--------- plugins/probe.cpp | 18 ++++++------------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/plugins/probe.rst b/docs/plugins/probe.rst index 1dcc28336..614d0bc81 100644 --- a/docs/plugins/probe.rst +++ b/docs/plugins/probe.rst @@ -1,13 +1,21 @@ probe ===== +Tags: +:dfhack-keybind:`probe` +:dfhack-keybind:`bprobe` +:dfhack-keybind:`cprobe` -This plugin provides multiple commands that print low-level properties of the -selected objects. +Display low-level properties of selected objects. -* ``probe``: prints some properties of the tile selected with :kbd:`k`. Some of - these properties can be passed into `tiletypes`. -* ``cprobe``: prints some properties of the unit selected with :kbd:`v`, as well - as the IDs of any worn items. `gui/gm-unit` and `gui/gm-editor` are more - complete in-game alternatives. -* ``bprobe``: prints some properties of the building selected with :kbd:`q` or - :kbd:`t`. `gui/gm-editor` is a more complete in-game alternative. +Usage: + +``probe`` + Displays properties of the tile selected with :kbd:`k`. Some of these + properties can be passed into `tiletypes`. +``bprobe`` + Displays properties of the building selected with :kbd:`q` or :kbd:`t`. + For deeper inspection of the building, see `gui/gm-editor`. +``cprobe`` + Displays properties of the unit selected with :kbd:`v`. It also displays the + IDs of any worn items. For deeper inspection of the unit and inventory items, + see `gui/gm-unit` and `gui/gm-editor`. diff --git a/plugins/probe.cpp b/plugins/probe.cpp index ba7c42ede..b9b98ee75 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -49,20 +49,14 @@ command_result df_bprobe (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("probe", - "A tile probe", - df_probe, - false, - "Hover the cursor over a tile to view its properties.\n")); + "Display information about the selected tile.", + df_probe)); commands.push_back(PluginCommand("cprobe", - "A creature probe", - df_cprobe, - false, - "Select a creature to view its properties.\n")); + "Display information about the selected creature.", + df_cprobe)); commands.push_back(PluginCommand("bprobe", - "A simple building probe", - df_bprobe, - false, - "Select a building to view its properties.\n")); + "Display information about the selected building.", + df_bprobe)); return CR_OK; } From 1270cf3f0a6f67685b8d007995edaa1e9ef7bb74 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 23:12:11 -0700 Subject: [PATCH 179/334] update docs for prospector --- docs/plugins/prospector.rst | 59 +++++++++++++++++++++---------------- plugins/prospector.cpp | 43 ++------------------------- 2 files changed, 36 insertions(+), 66 deletions(-) diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 03dd193f8..95436abec 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -1,43 +1,50 @@ .. _prospect: -prospect -======== +prospector +========== +Tags: +:dfhack-keybind:`prospect` -**Usage:** +Shows a summary of resources that exist on the map or an estimate of resources +available in the selected embark area. - ``prospect [all|hell] []`` +Usage:: -Shows a summary of resources that exist on the map. By default, only the visible -part of the map is scanned. Include the ``all`` keyword if you want ``prospect`` -to scan the whole map as if it were revealed. Use ``hell`` instead of ``all`` if -you also want to see the Z range of HFS tubes in the 'features' report section. + prospect [all|hell] [] -**Options:** +By default, only the visible part of the map is scanned. Include the ``all`` +keyword if you want ``prospect`` to scan the whole map as if it were revealed. +Use ``hell`` instead of ``all`` if you also want to see the Z range of HFS +tubes in the 'features' report section. -:``-h``, ``--help``: - Shows this help text. -:``-s``, ``--show ``: - Shows only the named comma-separated list of report sections. Report section - names are: summary, liquids, layers, features, ores, gems, veins, shrubs, - and trees. If run during pre-embark, only the layers, ores, gems, and veins - report sections are available. -:``-v``, ``--values``: - Includes material value in the output. Most useful for the 'gems' report - section. - -**Examples:** +Examples +-------- ``prospect all`` Shows the entire report for the entire map. ``prospect hell --show layers,ores,veins`` Shows only the layers, ores, and other vein stone report sections, and - includes information on HFS tubes when a fort is loaded. + includes information on HFS tubes (if run on a fortress map and not the + pre-embark screen). ``prospect all -sores`` Show only information about ores for the pre-embark or fortress map report. -**Pre-embark estimate:** +Options +------- + +``-s``, ``--show `` + Shows only the named comma-separated list of report sections. Report section + names are: summary, liquids, layers, features, ores, gems, veins, shrubs, + and trees. If run during pre-embark, only the layers, ores, gems, and veins + report sections are available. +``-v``, ``--values`` + Includes material value in the output. Most useful for the 'gems' report + section. + +Pre-embark estimate +------------------- If prospect is called during the embark selection screen, it displays an estimate of layer stone availability. If the ``all`` keyword is specified, it @@ -48,6 +55,6 @@ the embark rectangle. The results of pre-embark prospect are an *estimate*, and can at best be expected to be somewhere within +/- 30% of the true amount; sometimes it - does a lot worse. Especially, it is not clear how to precisely compute how - many soil layers there will be in a given embark tile, so it can report a - whole extra layer, or omit one that is actually present. + does a lot worse. In particular, it is not clear how to precisely compute + how many soil layers there will be in a given embark tile, so it can report + a whole extra layer, or omit one that is actually present. diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index 0516dd552..e75c967dc 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -260,46 +260,9 @@ command_result prospector (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "prospect", "Show stats of available raw resources.", - prospector, false, - " prospect [all|hell] []\n" - "\n" - " Shows a summary of resources that exist on the map. By default,\n" - " only the visible part of the map is scanned. Include the 'all' keyword\n" - " if you want prospect to scan the whole map as if it were revealed.\n" - " Use 'hell' instead of 'all' if you also want to see the Z range of HFS\n" - " tubes in the 'features' report section.\n" - "\n" - "Options:\n" - " -h,--help\n" - " Shows this help text.\n" - " -s,--show \n" - " Shows only the named comma-separated list of report sections.\n" - " Report section names are: summary, liquids, layers, features, ores,\n" - " gems, veins, shrubs, and trees. If run during pre-embark, only the\n" - " layers, ores, gems, and veins report sections are available.\n" - " -v,--values\n" - " Includes material value in the output. Most useful for the 'gems'\n" - " report section.\n" - "\n" - "Examples:\n" - " prospect all\n" - " Shows the entire report for the entire map.\n" - "\n" - " prospect hell --show layers,ores,veins\n" - " Shows only the layers, ores, and other vein stone report sections,\n" - " and includes information on HFS tubes when a fort is loaded.\n" - "\n" - " prospect all -sores\n" - " Show only information about ores for the pre-embark or fortress map\n" - " report.\n" - "\n" - "Pre-embark estimate:\n" - " If called during the embark selection screen, displays a rough\n" - " estimate of layer stone availability. If the 'all' keyword is\n" - " specified, also estimates ores, gems, and other vein material. The\n" - " estimate covers all tiles of the embark rectangle.\n" - )); + "prospect", + "Show raw resources available on the map.", + prospector)); return CR_OK; } From e513253d8d9b7b4b9c78a2dd18c945ce19575191 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 28 Jul 2022 23:20:24 -0700 Subject: [PATCH 180/334] update docs for regrass --- docs/plugins/regrass.rst | 13 ++++++++++++- plugins/regrass.cpp | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index f3d00d8e1..d47e98d19 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -1,3 +1,14 @@ regrass ======= -Regrows all the grass. Not much to it ;) +Tags: +:dfhack-keybind:`regrass` + +Regrows all the grass. Use this command if your grazers have eaten everything +down to the dirt. + +Usage:: + + regrass [max] + +Specify the 'max' option to pack more grass onto a tile than what the game +normally allows to give your grazers extra chewing time. diff --git a/plugins/regrass.cpp b/plugins/regrass.cpp index 0cac5bd77..5d2a6450c 100644 --- a/plugins/regrass.cpp +++ b/plugins/regrass.cpp @@ -29,8 +29,10 @@ command_result df_regrass (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("regrass", "Regrows surface grass.", df_regrass, false, - "Specify parameter 'max' to set all grass types to full density, otherwise only one type of grass will be restored per tile.\n")); + commands.push_back(PluginCommand( + "regrass", + "Regrows surface grass.", + df_regrass)); return CR_OK; } From 7a4e8ea18ef0f4b2fde767c36add8f2fade839a0 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 16:05:49 -0700 Subject: [PATCH 181/334] some plugins can't be directly enabled by the user --- docs/plugins/RemoteFortressReader.rst | 2 -- docs/plugins/autoclothing.rst | 9 +++++---- docs/plugins/autogems.rst | 3 ++- docs/plugins/burrows.rst | 6 +----- docs/plugins/power-meter.rst | 13 +++---------- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index 103c904db..f3bf505ac 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -10,8 +10,6 @@ remote fortress visualization. See :forums:`Armok Vision <146473>`. Usage: -``enable RemoteFortressReader`` - Enable the plugin. ``RemoteFortressReader_version`` Print the loaded RemoteFortressReader version. ``load-art-image-chunk `` diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index bbfe14cc9..2e0d03ec1 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -9,14 +9,15 @@ set how many of each clothing type every citizen should have. Usage:: - enable autoclothing - autoclothing [number] + autoclothing + autoclothing [quantity] ``material`` can be "cloth", "silk", "yarn", or "leather". The ``item`` can be anything your civilization can produce, such as "dress" or "mitten". -When invoked without a number, it shows the current configuration for that -material and item. +When invoked without parameters, it shows a summary of all managed clothing +orders. When invoked with a material and item, but without a quantity, it shows +the current configuration for that material and item. Examples -------- diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 72b850754..ee1bc8be6 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -10,7 +10,8 @@ orders for cutting them at a Jeweler's Workshop. Usage: ``enable autogems`` - Enables the plugin + Enables the plugin and starts autocutting gems according to its + configuration. ``autogems-reload`` :index:`Reloads the autogems configuration file. ` You might need diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 733f784d0..29b81330d 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -15,15 +15,11 @@ You can also use the ``burrow`` command to Usage: -``enable burrows`` - Enable the plugin for the auto-grow feature (see - ``burrow enable auto-grow`` below) ``burrow enable auto-grow`` When a wall inside a burrow with a name ending in '+' is dug out, the burrow will be extended to newly-revealed adjacent walls. This final '+' may be omitted in burrow name args of other ``burrows`` commands. Note that digging - 1-wide corridors with the miner inside the burrow is SLOW. Be sure to also - run ``enable burrows`` for this feature to work. + 1-wide corridors with the miner inside the burrow is SLOW. ``burrow disable auto-grow`` Disables auto-grow processing. ``burrow clear-unit [ ...]`` diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 9443f2a93..e099a7903 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -2,13 +2,6 @@ power-meter =========== Tags: -Provides the backend for `gui/power-meter`. - -Usage:: - - enable power-meter - -The power-meter plugin implements a modified pressure plate that detects power being -supplied to gear boxes built in the four adjacent N/S/W/E tiles. - -The configuration front-end is implemented by `gui/power-meter`. +Allows presure plates to measure power. If you run `gui/power-meter` while +building a pressure plate, the pressure plate can be modified to detects power +being supplied to gear boxes built in the four adjacent N/S/W/E tiles. From eef7812bf67fa828cb595827b8be629e4d20f5ee Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 16:08:00 -0700 Subject: [PATCH 182/334] update docs for rename --- docs/plugins/rename.rst | 41 ++++++++++++++++++++++++++--------------- plugins/rename.cpp | 13 +++---------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 55534e636..0c4bc9ddb 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -1,19 +1,30 @@ rename ====== -Allows renaming various things. Use `gui/rename` for an in-game interface. +Tags: +:dfhack-keybind:`rename` -Options: +Easily rename things. Use `gui/rename` for an in-game interface. -``rename squad "name"`` - Rename squad by index to 'name'. -``rename hotkey \"name\"`` - Rename hotkey by index. This allows assigning - longer commands to the DF hotkeys. -``rename unit "nickname"`` - Rename a unit/creature highlighted in the DF user interface. -``rename unit-profession "custom profession"`` - Change proffession name of the highlighted unit/creature. -``rename building "name"`` - Set a custom name for the selected building. - The building must be one of stockpile, workshop, furnace, trap, - siege engine or an activity zone. +Usage: + +``rename squad ""`` + Rename the indicated squad. The ordinal is the number that corresponds to + the list of squads in the squads menu (:kbd:`s`). The first squad is ordinal + ``1``. +``rename hotkey ""`` + Rename the indicated hotkey. The ordinal the the number that corresponds to + the list of hotkeys in the hotkeys menu (:kbd:`H`). The first hotkey is + ordinal ``1``. +``rename unit ""`` + Give the selected unit the given nickname. +``rename unit-profession ""`` + Give the selected unit the given profession name. +``rename building ""`` + Set a custom name to the selected building. The building must be a + stockpile, workshop, furnace, trap, siege engine, or activity zone. + +Example +------- + +``rename squad 1 "The Whiz Bonkers"`` + Rename the first squad to The Whiz Bonkers. diff --git a/plugins/rename.cpp b/plugins/rename.cpp index 0ef3e02d5..7c46fc908 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -61,16 +61,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector \"name\"\n" - " rename hotkey \"name\"\n" - " (identified by ordinal index)\n" - " rename unit \"nickname\"\n" - " rename unit-profession \"custom profession\"\n" - " (a unit must be highlighted in the ui)\n" - " rename building \"nickname\"\n" - " (a building must be highlighted via 'q')\n" - )); + "rename", + "Easily rename things.", + rename)); if (Core::getInstance().isWorldLoaded()) plugin_onstatechange(out, SC_WORLD_LOADED); From a7011421b4a59f2cb472397744d2a88d084ac741 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 16:31:37 -0700 Subject: [PATCH 183/334] update docs for rendermax --- docs/plugins/rendermax.rst | 36 +++++++++++++++++++++++---------- plugins/rendermax/rendermax.cpp | 15 ++++---------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index a173a75db..641a7a248 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -1,18 +1,32 @@ rendermax ========= -A collection of renderer replacing/enhancing filters. For better effect try changing the -black color in palette to non totally black. See :forums:`128487` for more info. +Tags: +:dfhack-keybind:`rendermax` -Options: +Modify the map lighting. This plugin provides a collection of OpenGL lighting +filters that affect how the map is drawn to the screen. -:trippy: Randomizes the color of each tiles. Used for fun, or testing. -:light: Enable lighting engine. -:light reload: Reload the settings file. -:light sun |cycle: Set time to (in hours) or set it to df time cycle. -:occlusionON, occlusionOFF: Show debug occlusion info. -:disable: Disable any filter that is enabled. +Usage: -An image showing lava and dragon breath. Not pictured here: sunlight, shining items/plants, -materials that color the light etc... +``rendermax light`` + Light the map tiles realisitically. Outside tiles are light during the day + and dark at night. Inside tiles are always dark unless a nearby unit is + lighting it up, as if they were carrying torches. +``rendermax light sun |cycle`` + Set the outside lighting to correspond with the specified day hour (1-24), + or specify ``cycle`` to have the lighting follow the sun (which is the + default). +``rendermax light reload`` + Reload the lighting settings file. +``rendermax trippy`` + Randomize the color of each tile. Used for fun, or testing. +``rendermax disable`` + Disable any ``rendermax`` lighting filters that are currently active. + +An image showing lava and dragon breath. Not pictured here: sunlight, shining +items/plants, materials that color the light etc. .. image:: ../images/rendermax.png + +For better visibility, try changing the black color in palette to non totally +black. See :forums:`128487` for more info. diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index d391bdb36..b7c703e09 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -49,16 +49,9 @@ static command_result rendermax(color_ostream &out, vector & parameters DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "rendermax", "switch rendering engine.", rendermax, false, - " rendermax trippy\n" - " rendermax truecolor red|green|blue|white\n" - " rendermax lua\n" - " rendermax light - lighting engine\n" - " rendermax light reload - reload the settings file\n" - " rendermax light sun |cycle - set time to x (in hours) or cycle (same effect if x<0)\n" - " rendermax light occlusionON|occlusionOFF - debug the occlusion map\n" - " rendermax disable\n" - )); + "rendermax", + "Modify the map lighting.", + rendermax)); return CR_OK; } @@ -331,7 +324,7 @@ static command_result rendermax(color_ostream &out, vector & parameters return CR_WRONG_USAGE; if(!enabler->renderer->uses_opengl()) { - out.printerr("Sorry, this plugin needs open gl enabled printmode. Try STANDARD or other non-2D\n"); + out.printerr("Sorry, this plugin needs open GL-enabled printmode. Try STANDARD or other non-2D.\n"); return CR_FAILURE; } string cmd=parameters[0]; From c6d5fcb37873438c4851d0ff823192b50f744466 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 17:12:01 -0700 Subject: [PATCH 184/334] update docs for resume --- docs/plugins/resume.rst | 15 +++++++++++++-- plugins/resume.cpp | 15 +++------------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index ccca39e28..d8feb0896 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -1,4 +1,15 @@ resume ====== -Allows automatic resumption of suspended constructions, along with colored -UI hints for construction status. +Tags: +:dfhack-keybind:`` + +Color planned buildings based on their suspend status. When enabled, this plugin +will display a colored 'X' over suspended buildings. When run as a command, it +can resume all suspended building jobs, allowing you to quickly recover if a +bunch of jobs were suspended due to the workers getting scared off by wildlife +or items temporarily blocking buildling sites. + +Usage:: + + enable resume + resume all diff --git a/plugins/resume.cpp b/plugins/resume.cpp index 6b8ad5e7d..21a4b521e 100644 --- a/plugins/resume.cpp +++ b/plugins/resume.cpp @@ -302,18 +302,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Fri, 29 Jul 2022 17:12:15 -0700 Subject: [PATCH 185/334] update docs for reveal --- docs/plugins/reveal.rst | 58 +++++++++++++++++++++++++---------------- plugins/reveal.cpp | 42 ++++++++++++++++------------- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index 42f490aa4..f8611d033 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -2,29 +2,41 @@ reveal ====== -This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use ``reveal hell`` to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use ``reveal demons``. +Tags: +:dfhack-keybind:`reveal` +:dfhack-keybind:`unreveal` +:dfhack-keybind:`revforget` +:dfhack-keybind:`revtoggle` +:dfhack-keybind:`revflood` +:dfhack-keybind:`nopause` -Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run ``unreveal``. +Reveals the map. This reveals all z-layers in fort mode. It also works in +adventure mode, but any of its effects are negated once you move. When you use +it this way, you don't need to run ``unreveal`` to hide the map again. -Usage and related commands: +Usage: -:reveal: Reveal the whole map, except for HFS to avoid demons spawning -:reveal hell: Also show hell, but requires ``unreveal`` before unpausing -:reveal demon: Reveals everything and allows unpausing - good luck! -:unreveal: Reverts the effects of ``reveal`` -:revtoggle: Switches between ``reveal`` and ``unreveal`` -:revflood: Hide everything, then reveal tiles with a path to the cursor. - Note that tiles behind constructed walls are also revealed as a - workaround for :bug:`1871`. -:revforget: Discard info about what was visible before revealing the map. - Only useful where (e.g.) you abandoned with the fort revealed - and no longer want the data. - -nopause -======= -Disables pausing (both manual and automatic) with the exception of pause forced -by `reveal` ``hell``. This is nice for digging under rivers. +``reveal [hell|demon]`` + Reveal the whole map. If ``hell`` is specified, also reveal HFS areas, but + you are required to run ``unreveal`` before unpausing is allowed in order + to prevent the demons from spawning. If you really want to unpause with hell + revealed, specify ``demon`` instead of ``hell``. +``unreveal`` + Reverts the effects of ``reveal``. +``revtoggle`` + Switches between ``reveal`` and ``unreveal``. Convenient to bind to a + hotkey. +``revforget`` + Discard info about what was visible before revealing the map. Only useful + where (for example) you abandoned with the fort revealed and no longer need + the saved map data when you load a new fort. +``revflood`` + Hide everything, then reveal tiles with a path to the cursor. This allows + reparing maps that you accidentally saved while they were revealed. Note + that tiles behind constructed walls are also revealed as a workaround for + :bug:`1871`. +``nopause 1|0`` + Disables pausing (both manual and automatic) with the exception of the pause + forced by `reveal` ``hell``. This is nice for digging under rivers. Use + ``nopause 1`` to prevent pausing and ``nopause 0`` to allow pausing like + normal. diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 39720926c..8d76bd54b 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -82,24 +82,30 @@ command_result nopause(color_ostream &out, vector & params); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { - commands.push_back(PluginCommand("reveal","Reveal the map.",reveal,false, - "Reveals the map, by default ignoring hell.\n" - "Options:\n" - "hell - also reveal hell, while forcing the game to pause.\n" - "demon - reveal hell, do not pause.\n")); - commands.push_back(PluginCommand("unreveal","Revert the map to its previous state.",unreveal,false, - "Reverts the previous reveal operation, hiding the map again.\n")); - commands.push_back(PluginCommand("revtoggle","Reveal/unreveal depending on state.",revtoggle,false, - "Toggles between reveal and unreveal.\n")); - commands.push_back(PluginCommand("revflood","Hide all, and reveal tiles reachable from the cursor.",revflood,false, - "This command hides the whole map. Then, starting from the cursor,\n" - "reveals all accessible tiles. Allows repairing perma-revealed maps.\n" - "Note that constructed walls are considered passable to work around DF bug 1871.\n")); - commands.push_back(PluginCommand("revforget", "Forget the current reveal data.",revforget,false, - "Forget the current reveal data, allowing to use reveal again.\n")); - commands.push_back(PluginCommand("nopause","Disable manual and automatic pausing.",nopause,false, - "Disable pausing (doesn't affect pause forced by reveal).\n" - "Activate with 'nopause 1', deactivate with 'nopause 0'.\n")); + commands.push_back(PluginCommand( + "reveal", + "Reveal the map.", + reveal)); + commands.push_back(PluginCommand( + "unreveal", + "Revert a revealed map to its unrevealed state.", + unreveal)); + commands.push_back(PluginCommand( + "revtoggle", + "Switch betwen reveal and unreveal.", + revtoggle)); + commands.push_back(PluginCommand( + "revflood", + "Hide all, then reveal tiles reachable from the cursor.", + revflood)); + commands.push_back(PluginCommand( + "revforget", + "Forget the current reveal data.", + revforget)); + commands.push_back(PluginCommand( + "nopause", + "Disable manual and automatic pausing.", + nopause)); return CR_OK; } From aa3a389b6fa06b08e12f365ac8789cb9b84a32b2 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 17:37:50 -0700 Subject: [PATCH 186/334] fix parsing bold text and indenting of ls output --- library/lua/helpdb.lua | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 2aee005ee..cf8e86af5 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -179,14 +179,15 @@ local function update_entry(entry, iterator, opts) end if not header_found and line:find('%w') then header_found = true - elseif not tags_found and line:find('^Tags: [%w, ]+$') then - local _,_,tags = line:trim():find('Tags: (.*)') + elseif not tags_found and line:find('^[*]*Tags:[*]* [%w, ]+$') then + local _,_,tags = line:trim():find('[*]*Tags:[*]* *(.*)') entry.tags = {} for _,tag in ipairs(tags:split('[ ,]+')) do entry.tags[tag] = true end tags_found = true - elseif not short_help_found and not line:find('^Keybinding:') and + elseif not short_help_found and + not line:find('^[*]*Keybinding:') and line:find('%w') then if in_short_help then entry.short_help = entry.short_help .. ' ' .. line @@ -646,9 +647,11 @@ function help(entry) print(get_entry_long_help(entry)) end --- prints col1 (width 20), a 2 space gap, and col2 (width 58) --- if col1text is longer than 20 characters, col2text is printed on the next --- line. if col2text is longer than 58 characters, it is wrapped. +-- prints col1text (width 21), a one space gap, and col2 (width 58) +-- if col1text is longer than 21 characters, col2text is printed starting on the +-- next line. if col2text is longer than 58 characters, it is wrapped. col2text +-- lines on lines below the col1text output are indented by one space further +-- than the col2text on the first line. local COL1WIDTH, COL2WIDTH = 20, 58 local function print_columns(col1text, col2text) col2text = col2text:wrap(COL2WIDTH) @@ -656,12 +659,14 @@ local function print_columns(col1text, col2text) for line in col2text:gmatch('[^'..NEWLINE..']*') do table.insert(wrapped_col2, line) end + local col2_start_line = 1 if #col1text > COL1WIDTH then print(col1text) else - print(('%-'..COL1WIDTH..'s %s'):format(col1text, wrapped_col2[1])) + print(('%-'..COL1WIDTH..'s %s'):format(col1text, wrapped_col2[1])) + col2_start_line = 2 end - for i=2,#wrapped_col2 do + for i=col2_start_line,#wrapped_col2 do print(('%'..COL1WIDTH..'s %s'):format(' ', wrapped_col2[i])) end end From f785a910ffd492f198fe5ad09d185a3cc7d94a98 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 29 Jul 2022 17:38:23 -0700 Subject: [PATCH 187/334] update docs for search --- docs/plugins/search.rst | 48 ++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index 57b9e5df4..c8f5d68b3 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -1,40 +1,44 @@ - .. _search-plugin: search ====== -The search plugin adds search to the Stocks, Animals, Trading, Stockpile, -Noble (assignment candidates), Military (position candidates), Burrows -(unit list), Rooms, Announcements, Job List and Unit List screens. +Tags: + +The search plugin adds search capabilities to the Stocks, Animals, Trading, +Stockpile, Noble (assignment candidates), Military (position candidates), +Burrows (unit list), Rooms, Announcements, Job List, and Unit List screens. + +Usage:: + + enable search .. image:: ../images/search.png Searching works the same way as the search option in :guilabel:`Move to Depot`. -You will see the Search option displayed on screen with a hotkey (usually :kbd:`s`). -Pressing it lets you start typing a query and the relevant list will start -filtering automatically. +You will see the Search option displayed on screen with a hotkey +(usually :kbd:`s`). Pressing it lets you start typing a query and the relevant +list will start filtering automatically. -Pressing :kbd:`Enter`, :kbd:`Esc` or the arrow keys will return you to browsing the now -filtered list, which still functions as normal. You can clear the filter -by either going back into search mode and backspacing to delete it, or -pressing the "shifted" version of the search hotkey while browsing the -list (e.g. if the hotkey is :kbd:`s`, then hitting :kbd:`Shift`:kbd:`s` will clear any -filter). +Pressing :kbd:`Enter`, :kbd:`Esc` or the arrow keys will return you to browsing +the now filtered list, which still functions as normal. You can clear the filter +by either going back into search mode and backspacing to delete it, or pressing +the "shifted" version of the search hotkey while browsing the list (e.g. if the +hotkey is :kbd:`s`, then hitting :kbd:`Shift`:kbd:`s` will clear any filter). Leaving any screen automatically clears the filter. -In the Trade screen, the actual trade will always only act on items that -are actually visible in the list; the same effect applies to the Trade -Value numbers displayed by the screen. Because of this, the :kbd:`t` key is -blocked while search is active, so you have to reset the filters first. -Pressing :kbd:`Alt`:kbd:`C` will clear both search strings. +In the Trade screen, the actual trade will always only act on items that are +actually visible in the list; the same effect applies to the Trade Value numbers +displayed by the screen. Because of this, the :kbd:`t` key is blocked while +search is active, so you have to reset the filters first. Pressing +:kbd:`Alt`:kbd:`C` will clear both search strings. In the stockpile screen the option only appears if the cursor is in the rightmost list: .. image:: ../images/search-stockpile.png -Note that the 'Permit XXX'/'Forbid XXX' keys conveniently operate only -on items actually shown in the rightmost list, so it is possible to select -only fat or tallow by forbidding fats, then searching for fat/tallow, and -using Permit Fats again while the list is filtered. +Note that the 'Permit XXX'/'Forbid XXX' keys conveniently operate only on items +actually shown in the rightmost list, so it is possible to select only fat or +tallow by forbidding fats, then searching for fat/tallow, and using Permit Fats +again while the list is filtered. From 8cc0cee9a85664129aad3a46b1212168bf9b4c1a Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 30 Jul 2022 07:37:46 -0700 Subject: [PATCH 188/334] Fix grammar in power meter docs --- docs/plugins/power-meter.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index e099a7903..7186a413d 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -2,6 +2,6 @@ power-meter =========== Tags: -Allows presure plates to measure power. If you run `gui/power-meter` while -building a pressure plate, the pressure plate can be modified to detects power +Allow presure plates to measure power. If you run `gui/power-meter` while +building a pressure plate, the pressure plate can be modified to detect power being supplied to gear boxes built in the four adjacent N/S/W/E tiles. From feda5851e995ebce3bf983d844b324aa469869b5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:28:01 -0700 Subject: [PATCH 189/334] add example for ruby plugin --- docs/plugins/ruby.rst | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst index bdd5521a4..beb9be4a1 100644 --- a/docs/plugins/ruby.rst +++ b/docs/plugins/ruby.rst @@ -2,5 +2,22 @@ ruby ==== -Ruby language plugin, which evaluates the following arguments as a ruby string. -Best used as ``:rb [string]``, for the special parsing mode. Alias ``rb_eval``. +Tags: +:dfhack-keybind:`rb` +:dfhack-keybind:`rb_eval` + +Allow Ruby scripts to be executed. When invoked as a command, you can Eval() a +ruby string. + +Usage:: + + enable ruby + rb "ruby expression" + rb_eval "ruby expression" + :rb ruby expression + +Example +------- + +``:rb puts df.unit_find(:selected).name`` + Print the name of the selected unit. From edb7bd3168ba633681a3d16890891d1b90c698e5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:28:29 -0700 Subject: [PATCH 190/334] update docs for seedwatch --- docs/plugins/seedwatch.rst | 54 ++++++++++++++++++++++++------------ plugins/seedwatch.cpp | 56 +++++--------------------------------- 2 files changed, 43 insertions(+), 67 deletions(-) diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index 1d5ab9e62..2b04b3fa6 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -1,27 +1,45 @@ seedwatch ========= -Watches the numbers of seeds available and enables/disables seed and plant cooking. +Tags: +:dfhack-keybind:`seedwatch` -Each plant type can be assigned a limit. If their number falls below that limit, -the plants and seeds of that type will be excluded from cookery. -If the number rises above the limit + 20, then cooking will be allowed. +Manages seed and plant cooking based on seed stock levels. -The plugin needs a fortress to be loaded and will deactivate automatically otherwise. -You have to reactivate with 'seedwatch start' after you load the game. +Each seed type can be assigned a target. If the number of seeds of that type +falls below that target, then the plants and seeds of that type will be excluded +from cookery. If the number rises above the target + 20, then cooking will be +allowed. -Options: +The plugin needs a fortress to be loaded and will deactivate automatically +otherwise. You have to reactivate with ``enable seedwatch`` after you load a +fort. -:all: Adds all plants from the abbreviation list to the watch list. -:start: Start watching. -:stop: Stop watching. -:info: Display whether seedwatch is watching, and the watch list. -:clear: Clears the watch list. +Usage: -Examples: +``enable seedwatch`` + Start managing seed and plant cooking. By default, no types are watched. + You have to add them with further ``seedwatch`` commands. +``seedwatch `` + Adds the specifiied type to the watchlist (if it's not already there) and + sets the target number of seeds to the specified number. You can pass the + keyword ``all`` instead of a specific type to set the target for all types. +``seedwatch `` + Removes the specified type from the watch list. +``seedwatch clear`` + Clears all types from the watch list. +``seedwatch info`` + Display whether seedwatch is enabled and prints out the watch list. + +To print out a list of all plant types, you can run this command:: + + devel/query --table df.global.world.raws.plants.all --search ^id --maxdepth 1 + +Examples +-------- -``seedwatch MUSHROOM_HELMET_PLUMP 30`` - add ``MUSHROOM_HELMET_PLUMP`` to the watch list, limit = 30 -``seedwatch MUSHROOM_HELMET_PLUMP`` - removes ``MUSHROOM_HELMET_PLUMP`` from the watch list. ``seedwatch all 30`` - adds all plants from the abbreviation list to the watch list, the limit being 30. + Adds all seeds to the watch list and sets the targets to 30. +``seedwatch MUSHROOM_HELMET_PLUMP 50`` + Add Plump Helmets to the watch list and sets the target to 50. +``seedwatch MUSHROOM_HELMET_PLUMP`` + removes Plump Helmets from the watch list. diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index 478a0899a..17a765bcf 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -49,47 +49,6 @@ bool ignoreSeeds(df::item_flags& f) // seeds with the following flags should not f.bits.in_job; }; -void printHelp(color_ostream &out) // prints help -{ - out.print( - "Watches the numbers of seeds available and enables/disables seed and plant cooking.\n" - "Each plant type can be assigned a limit. If their number falls below,\n" - "the plants and seeds of that type will be excluded from cookery.\n" - "If the number rises above the limit + %i, then cooking will be allowed.\n", buffer - ); - out.printerr( - "The plugin needs a fortress to be loaded and will deactivate automatically otherwise.\n" - "You have to reactivate with 'seedwatch start' after you load the game.\n" - ); - out.print( - "Options:\n" - "seedwatch all - Adds all plants from the abbreviation list to the watch list.\n" - "seedwatch start - Start watching.\n" - "seedwatch stop - Stop watching.\n" - "seedwatch info - Display whether seedwatch is watching, and the watch list.\n" - "seedwatch clear - Clears the watch list.\n\n" - ); - if(!abbreviations.empty()) - { - out.print("You can use these abbreviations for the plant tokens:\n"); - for(map::const_iterator i = abbreviations.begin(); i != abbreviations.end(); ++i) - { - out.print("%s -> %s\n", i->first.c_str(), i->second.c_str()); - } - } - out.print( - "Examples:\n" - "seedwatch MUSHROOM_HELMET_PLUMP 30\n" - " add MUSHROOM_HELMET_PLUMP to the watch list, limit = 30\n" - "seedwatch MUSHROOM_HELMET_PLUMP\n" - " removes MUSHROOM_HELMET_PLUMP from the watch list.\n" - "seedwatch ph 30\n" - " is the same as 'seedwatch MUSHROOM_HELMET_PLUMP 30'\n" - "seedwatch all 30\n" - " adds all plants from the abbreviation list to the watch list, the limit being 30.\n" - ); -}; - // searches abbreviations, returns expansion if so, returns original if not string searchAbbreviations(string in) { @@ -142,8 +101,7 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) if(gm.g_mode != game_mode::DWARF || !World::isFortressMode(gm.g_type)) { // just print the help - printHelp(out); - return CR_OK; + return CR_WRONG_USAGE; } string par; @@ -151,14 +109,12 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) switch(parameters.size()) { case 0: - printHelp(out); return CR_WRONG_USAGE; case 1: par = parameters[0]; if ((par == "help") || (par == "?")) { - printHelp(out); return CR_WRONG_USAGE; } else if(par == "start") @@ -180,11 +136,11 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) out.print("seedwatch Info:\n"); if(running) { - out.print("seedwatch is supervising. Use 'seedwatch stop' to stop supervision.\n"); + out.print("seedwatch is supervising. Use 'disable seedwatch' to stop supervision.\n"); } else { - out.print("seedwatch is not supervising. Use 'seedwatch start' to start supervision.\n"); + out.print("seedwatch is not supervising. Use 'enable seedwatch' to start supervision.\n"); } map watchMap; Kitchen::fillWatchMap(watchMap); @@ -246,7 +202,6 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) } break; default: - printHelp(out); return CR_WRONG_USAGE; break; } @@ -256,7 +211,10 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) DFhackCExport command_result plugin_init(color_ostream &out, vector& commands) { - commands.push_back(PluginCommand("seedwatch", "Toggles seed cooking based on quantity available", df_seedwatch)); + commands.push_back(PluginCommand( + "seedwatch", + "Toggles seed cooking based on quantity available.", + df_seedwatch)); // fill in the abbreviations map, with abbreviations for the standard plants abbreviations["bs"] = "SLIVER_BARB"; abbreviations["bt"] = "TUBER_BLOATED"; From 8f7a23ee4d1061b20e6eeaf2b133e9cf3b30c2d6 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:28:41 -0700 Subject: [PATCH 191/334] update docs for showmood --- docs/plugins/showmood.rst | 9 ++++++++- plugins/showmood.cpp | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst index 9276766c4..8afe4c629 100644 --- a/docs/plugins/showmood.rst +++ b/docs/plugins/showmood.rst @@ -1,3 +1,10 @@ showmood ======== -Shows all items needed for the currently active strange mood. +Tags: +:dfhack-keybind:`showmood` + +Shows all items needed for the active strange mood. + +Usage:: + + showmood diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index c7d620676..abeb2e98e 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -297,8 +297,10 @@ command_result df_showmood (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("showmood", "Shows items needed for current strange mood.", df_showmood, false, - "Run this command without any parameters to display information on the currently active Strange Mood.")); + commands.push_back(PluginCommand( + "showmood", + "Shows all items needed for active strange mood.", + df_showmood)); return CR_OK; } From a1f937e354678661bc4ca622e45ccfb51ebdbe82 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:28:55 -0700 Subject: [PATCH 192/334] update docs for siege-engine --- docs/plugins/siege-engine.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index 0e9511d3d..a4cfed2ba 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -1,12 +1,19 @@ siege-engine ============ -Siege engines in DF haven't been updated since the game was 2D, and can -only aim in four directions. To make them useful above-ground, -this plugin allows you to: +Tags: + +Extend the functionality and usability of siege engines. Siege engines in DF +haven't been updated since the game was 2D, and can only aim in four +directions. To make them useful above-ground, this plugin allows you to: * link siege engines to stockpiles * restrict operator skill levels (like workshops) * load any object into a catapult, not just stones * aim at a rectangular area in any direction, and across Z-levels -The front-end is implemented by `gui/siege-engine`. +Usage:: + + enable siege-engine + +You can use the new features by selecting a built siege engine and running +`gui/siege-engine`. From 72619148265d8836ecb45f6f8252fd0db077e5ab Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:29:08 -0700 Subject: [PATCH 193/334] update docs for sort --- docs/plugins/sort.rst | 62 ++++++++++++++++++++++++++++--------------- plugins/sort.cpp | 20 ++------------ 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst index cc86b3ddd..12684175f 100644 --- a/docs/plugins/sort.rst +++ b/docs/plugins/sort.rst @@ -1,33 +1,51 @@ -sort-items -========== -Sort the visible item list:: - - sort-items order [order...] +sort +==== +Tags: +:dfhack-keybind:`sort-items` +:dfhack-keybind:`sort-units` -Sort the item list using the given sequence of comparisons. -The ``<`` prefix for an order makes undefined values sort first. -The ``>`` prefix reverses the sort order for defined values. +Sort the visible item or unit list. -Item order examples:: +Usage:: - description material wear type quality + sort-items [ ...] + sort-units [ ...] -The orderings are defined in ``hack/lua/plugins/sort/*.lua`` +Both commands sort the visible list using the given sequence of comparisons. +Each property can be prefixed with a ``<`` or ``>`` character to indicate +whether elements that don't have the given property defined go first or last +(respectively) in the sorted list. -sort-units -========== -Sort the visible unit list:: +Examples +-------- - sort-units order [order...] +``sort-items material type quality`` + Sort a list of items by material, then by type, then by quality +``sort-units profession name`` + Sort a list of units by profession, then by name -Sort the unit list using the given sequence of comparisons. -The ``<`` prefix for an order makes undefined values sort first. -The ``>`` prefix reverses the sort order for defined values. +Properties +---------- -Unit order examples:: +Items can be sorted by the following properties: - name age arrival squad squad_position profession +- ``type`` +- ``description`` +- ``base_quality`` +- ``quality`` +- ``improvement`` +- ``wear`` +- ``material`` -The orderings are defined in ``hack/lua/plugins/sort/*.lua`` +Units can be sorted by the following properties: -:dfhack-keybind:`sort-units` +- ``name`` +- ``age`` +- ``arrival`` +- ``noble`` +- ``profession`` +- ``profession_class`` +- ``race`` +- ``squad`` +- ``squad_position`` +- ``happiness`` diff --git a/plugins/sort.cpp b/plugins/sort.cpp index ab2829655..630ac9e53 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -56,25 +56,9 @@ static command_result sort_items(color_ostream &out, vector & parameter DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "sort-units", "Sort the visible unit list.", sort_units, unit_list_hotkey, - " sort-units order [order...]\n" - " Sort the unit list using the given sequence of comparisons.\n" - " The '<' prefix for an order makes undefined values sort first.\n" - " The '>' prefix reverses the sort order for defined values.\n" - " Unit order examples:\n" - " name, age, arrival, squad, squad_position, profession\n" - "The orderings are defined in hack/lua/plugins/sort/*.lua\n" - )); + "sort-units", "Sort the visible unit list.", sort_units, unit_list_hotkey)); commands.push_back(PluginCommand( - "sort-items", "Sort the visible item list.", sort_items, item_list_hotkey, - " sort-items order [order...]\n" - " Sort the item list using the given sequence of comparisons.\n" - " The '<' prefix for an order makes undefined values sort first.\n" - " The '>' prefix reverses the sort order for defined values.\n" - " Item order examples:\n" - " description, material, wear, type, quality\n" - "The orderings are defined in hack/lua/plugins/sort/*.lua\n" - )); + "sort-items", "Sort the visible item list.", sort_items, item_list_hotkey)); return CR_OK; } From af0631cbd9d38263dcfe4dcd940d42f114ec8a6b Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:29:18 -0700 Subject: [PATCH 194/334] update docs for spectate --- docs/plugins/spectate.rst | 13 ++++++++++--- plugins/spectate.cpp | 7 +------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 6c2ca9e24..983086c94 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -1,5 +1,12 @@ spectate ======== -Simple plugin to automate following random dwarves. Most of the time things will -be weighted towards z-levels with the highest job activity. Simply enter the -``spectate`` command to toggle the plugin's state. +Tags: + +Automatically follow exciting dwarves. + +Usage:: + + enable spectate + +The plugin will automatically switch which dwarf is being followed periodically, +preferring dwarves on z-levels with the highest job activity. diff --git a/plugins/spectate.cpp b/plugins/spectate.cpp index aae48cf5a..1161c3f32 100644 --- a/plugins/spectate.cpp +++ b/plugins/spectate.cpp @@ -42,12 +42,7 @@ command_result spectate (color_ostream &out, std::vector & paramet DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("spectate", "Automated spectator mode.", - spectate, - false, - "" - " spectate\n" - " toggles spectator mode\n" - "\n")); + spectate)); return CR_OK; } From 479494e5a6a135605e0cc6722c163226aea11cb9 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:29:28 -0700 Subject: [PATCH 195/334] update docs for steam-engine --- docs/plugins/steam-engine.rst | 106 +++++++++++++++++----------------- plugins/steam-engine.cpp | 76 ------------------------ 2 files changed, 54 insertions(+), 128 deletions(-) diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index 416ac9ad1..35cd490f5 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -1,40 +1,47 @@ steam-engine ============ -The steam-engine plugin detects custom workshops with STEAM_ENGINE in -their token, and turns them into real steam engines. +Tags: -The vanilla game contains only water wheels and windmills as sources of -power, but windmills give relatively little power, and water wheels require -flowing water, which must either be a real river and thus immovable and +Allow modded steam engine buildings to function. The steam-engine plugin detects +custom workshops with STEAM_ENGINE in their token, and turns them into real +steam engines! + +The plugin auto-enables itself when it detects the relevant tags in the world +raws. It does not need to be enabled with the `enable` command. + +Rationale +--------- +The vanilla game contains only water wheels and windmills as sources of power, +but windmills give relatively little power, and water wheels require flowing +water, which must either be a real river and thus immovable and limited in supply, or actually flowing and thus laggy. -Compared to the :wiki:`water reactor ` -exploit, steam engines make a lot of sense! +Compared to the +:wiki:`dwarven water reactor ` exploit, +steam engines make a lot of sense! Construction ------------ -The workshop needs water as its input, which it takes via a -passable floor tile below it, like usual magma workshops do. -The magma version also needs magma. +The workshop needs water as its input, which it takes via a passable floor tile +below it, like usual magma workshops do. The magma version also needs magma. -Due to DFHack limits, the workshop will collapse over true open space. -However down stairs are passable but support machines, so you can use them. +Due to DF game limits, the workshop will collapse over true open space. However, +down stairs are passable but support machines, so you can use them. -After constructing the building itself, machines can be connected -to the edge tiles that look like gear boxes. Their exact position -is extracted from the workshop raws. +After constructing the building itself, machines can be connected to the edge +tiles that look like gear boxes. Their exact position is extracted from the +workshop raws. -Like with collapse above, due to DFHack limits the workshop -can only immediately connect to machine components built AFTER it. -This also means that engines cannot be chained without intermediate -axles built after both engines. +Like with collapse above, due to DF game limits the workshop can only +immediately connect to machine components built AFTER it. This also means that +engines cannot be chained without intermediate axles built after both engines. Operation --------- -In order to operate the engine, queue the Stoke Boiler job (optionally -on repeat). A furnace operator will come, possibly bringing a bar of fuel, -and perform it. As a result, a "boiling water" item will appear -in the :kbd:`t` view of the workshop. +In order to operate the engine, queue the Stoke Boiler job (optionally on +repeat). A furnace operator will come, possibly bringing a bar of fuel, and +perform it. As a result, a "boiling water" item will appear in the :kbd:`t` +view of the workshop. .. note:: @@ -44,41 +51,36 @@ in the :kbd:`t` view of the workshop. have infinite power. However, liquid consumption should be slow enough that water can be supplied by a pond zone bucket chain. -Every such item gives 100 power, up to a limit of 300 for coal, -and 500 for a magma engine. The building can host twice that -amount of items to provide longer autonomous running. When the -boiler gets filled to capacity, all queued jobs are suspended; -once it drops back to 3+1 or 5+1 items, they are re-enabled. - -While the engine is providing power, steam is being consumed. -The consumption speed includes a fixed 10% waste rate, and -the remaining 90% are applied proportionally to the actual -load in the machine. With the engine at nominal 300 power with -150 load in the system, it will consume steam for actual +Every such item gives 100 power, up to a limit of 300 for coal, or 500 for a +magma engine. The building can host twice that amount of items to provide longer +autonomous running. When the boiler gets filled to capacity, all queued jobs are +suspended. Once it drops back to 3+1 or 5+1 items, they are re-enabled. + +While the engine is providing power, steam is being consumed. The consumption +speed includes a fixed 10% waste rate, and the remaining 90% is applied +proportionally to the actual load in the machine. With the engine at nominal 300 +power with 150 load in the system, it will consume steam for actual 300*(10% + 90%*150/300) = 165 power. -Masterpiece mechanism and chain will decrease the mechanical -power drawn by the engine itself from 10 to 5. Masterpiece -barrel decreases waste rate by 4%. Masterpiece piston and pipe -decrease it by further 4%, and also decrease the whole steam -use rate by 10%. +A masterpiece mechanism and chain will decrease the mechanical power drawn by +the engine itself from 10 to 5. A masterpiece barrel decreases waste rate by 4%. +A masterpiece piston and pipe decrease it by further 4%, and also decrease the +whole steam use rate by 10%. Explosions ---------- -The engine must be constructed using barrel, pipe and piston -from fire-safe, or in the magma version magma-safe metals. +The engine must be constructed using barrel, pipe, and piston from fire-safe, +or, in the magma version, magma-safe metals. -During operation weak parts get gradually worn out, and -eventually the engine explodes. It should also explode if -toppled during operation by a building destroyer, or a -tantruming dwarf. +During operation, weak parts gradually wear out, and eventually the engine +explodes. It should also explode if toppled during operation by a building +destroyer or a tantruming dwarf. Save files ---------- -It should be safe to load and view engine-using fortresses -from a DF version without DFHack installed, except that in such -case the engines won't work. However actually making modifications -to them, or machines they connect to (including by pulling levers), -can easily result in inconsistent state once this plugin is -available again. The effects may be as weird as negative power -being generated. +It should be safe to load and view engine-using fortresses from a DF version +without DFHack installed, except that in such case the engines, of course, won't +work. However actually making modifications to them or machines they connect to +(including by pulling levers) can easily result in inconsistent state once this +plugin is available again. The effects may be as weird as negative power being +generated. diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index fbc575e36..6e3c317d3 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -37,82 +37,6 @@ #include "df/workshop_type.h" #include "df/world.h" -/* - * This plugin implements a steam engine workshop. It activates - * if there are any workshops in the raws with STEAM_ENGINE in - * their token, and provides the necessary behavior. - * - * Construction: - * - * The workshop needs water as its input, which it takes via a - * passable floor tile below it, like usual magma workshops do. - * The magma version also needs magma. - * - * ISSUE: Since this building is a machine, and machine collapse - * code cannot be modified, it would collapse over true open space. - * As a loophole, down stair provides support to machines, while - * being passable, so use them. - * - * After constructing the building itself, machines can be connected - * to the edge tiles that look like gear boxes. Their exact position - * is extracted from the workshop raws. - * - * ISSUE: Like with collapse above, part of the code involved in - * machine connection cannot be modified. As a result, the workshop - * can only immediately connect to machine components built AFTER it. - * This also means that engines cannot be chained without intermediate - * short axles that can be built later. - * - * Operation: - * - * In order to operate the engine, queue the Stoke Boiler job. - * A furnace operator will come, possibly bringing a bar of fuel, - * and perform it. As a result, a "boiling water" item will appear - * in the 't' view of the workshop. - * - * Note: The completion of the job will actually consume one unit - * of appropriate liquids from below the workshop. - * - * Every such item gives 100 power, up to a limit of 300 for coal, - * and 500 for a magma engine. The building can host twice that - * amount of items to provide longer autonomous running. When the - * boiler gets filled to capacity, all queued jobs are suspended; - * once it drops back to 3+1 or 5+1 items, they are re-enabled. - * - * While the engine is providing power, steam is being consumed. - * The consumption speed includes a fixed 10% waste rate, and - * the remaining 90% are applied proportionally to the actual - * load in the machine. With the engine at nominal 300 power with - * 150 load in the system, it will consume steam for actual - * 300*(10% + 90%*150/300) = 165 power. - * - * Masterpiece mechanism and chain will decrease the mechanical - * power drawn by the engine itself from 10 to 5. Masterpiece - * barrel decreases waste rate by 4%. Masterpiece piston and pipe - * decrease it by further 4%, and also decrease the whole steam - * use rate by 10%. - * - * Explosions: - * - * The engine must be constructed using barrel, pipe and piston - * from fire-safe, or in the magma version magma-safe metals. - * - * During operation weak parts get gradually worn out, and - * eventually the engine explodes. It should also explode if - * toppled during operation by a building destroyer, or a - * tantruming dwarf. - * - * Save files: - * - * It should be safe to load and view fortresses using engines - * from a DF version without DFHack installed, except that in such - * case the engines won't work. However actually making modifications - * to them, or machines they connect to (including by pulling levers), - * can easily result in inconsistent state once this plugin is - * available again. The effects may be as weird as negative power - * being generated. - */ - using std::vector; using std::string; using std::stack; From 08d434d16ea24d902c0f0b9fe8da5ac9e09d5972 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:29:40 -0700 Subject: [PATCH 196/334] update docs for stockflow --- docs/plugins/stockflow.rst | 39 +++++++++++++++++++------------------- plugins/stockflow.cpp | 32 +++++-------------------------- 2 files changed, 25 insertions(+), 46 deletions(-) diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index c3db7a673..0e2f5a1ad 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -1,24 +1,14 @@ stockflow ========= -Allows the fortress bookkeeper to queue jobs through the manager, -based on space or items available in stockpiles. +Tags: +:dfhack-keybind:`stockflow` -Inspired by `workflow`. +Queue manager jobs based on free space in stockpiles. With this plugin, the +fortress bookkeeper can tally up free space in specific stockpiles and queue +jobs through the manager to produce items to fill the free space. -Usage: - -``stockflow enable`` - Enable the plugin. -``stockflow disable`` - Disable the plugin. -``stockflow fast`` - Enable the plugin in fast mode. -``stockflow list`` - List any work order settings for your stockpiles. -``stockflow status`` - Display whether the plugin is enabled. - -While enabled, the :kbd:`q` menu of each stockpile will have two new options: +When the plugin is enabled, the :kbd:`q` menu of each stockpile will have two +new options: * :kbd:`j`: Select a job to order, from an interface like the manager's screen. * :kbd:`J`: Cycle between several options for how many such jobs to order. @@ -27,5 +17,16 @@ Whenever the bookkeeper updates stockpile records, new work orders will be placed on the manager's queue for each such selection, reduced by the number of identical orders already in the queue. -In fast mode, new work orders will be enqueued once per day, instead of -waiting for the bookkeeper. +This plugin is similar to `workflow`, but uses stockpiles to manage job triggers +instead of abstract stock quantities. + +Usage: + +``enable stockflow`` + Enable the plugin. +``stockflow status`` + Display whether the plugin is enabled. +``stockflow list`` + List any work order settings for your stockpiles. +``stockflow fast`` + Enqueue orders once per day instead of waiting for the bookkeeper. diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp index aede089ad..65223f603 100644 --- a/plugins/stockflow.cpp +++ b/plugins/stockflow.cpp @@ -29,30 +29,6 @@ REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(ui); bool fast = false; -const char *tagline = "Allow the bookkeeper to queue manager jobs."; -const char *usage = ( - " stockflow enable\n" - " Enable the plugin.\n" - " stockflow disable\n" - " Disable the plugin.\n" - " stockflow fast\n" - " Enable the plugin in fast mode.\n" - " stockflow list\n" - " List any work order settings for your stockpiles.\n" - " stockflow status\n" - " Display whether the plugin is enabled.\n" - "\n" - "While enabled, the 'q' menu of each stockpile will have two new options:\n" - " j: Select a job to order, from an interface like the manager's screen.\n" - " J: Cycle between several options for how many such jobs to order.\n" - "\n" - "Whenever the bookkeeper updates stockpile records, new work orders will\n" - "be placed on the manager's queue for each such selection, reduced by the\n" - "number of identical orders already in the queue.\n" - "\n" - "In fast mode, new work orders will be enqueued once per day, instead of\n" - "waiting for the bookkeeper.\n" -); /* * Lua interface. @@ -342,8 +318,7 @@ static command_result stockflow_cmd(color_ostream &out, vector & parame desired = true; fast = true; } else if (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?") { - out.print("%s: %s\nUsage:\n%s", plugin_name, tagline, usage); - return CR_OK; + return CR_WRONG_USAGE; } else if (parameters[0] == "list") { if (!enabled) { out.printerr("Stockflow is not currently enabled.\n"); @@ -416,7 +391,10 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Sun, 31 Jul 2022 13:29:53 -0700 Subject: [PATCH 197/334] update docs for stockpiles --- docs/plugins/stockpiles.rst | 52 ++++++++++++++++++++----------- plugins/stockpiles/stockpiles.cpp | 48 +++++++++------------------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 000527ce5..76d52117d 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -2,27 +2,41 @@ stockpiles ========== -Offers the following commands to save and load stockpile settings. -See `gui/stockpiles` for an in-game interface. +Tags: +:dfhack-keybind:`copystock` +:dfhack-keybind:`savestock` +:dfhack-keybind:`loadstock` -:copystock: Copies the parameters of the currently highlighted stockpile to the custom - stockpile settings and switches to custom stockpile placement mode, effectively - allowing you to copy/paste stockpiles easily. - :dfhack-keybind:`copystock` +Import and export stockpile settings. When the plugin is enabled, the :kbd:`q` +menu of each stockpile will have an option for saving or loading the stockpile +settings. See `gui/stockpiles` for an in-game interface. -:savestock: Saves the currently highlighted stockpile's settings to a file in your Dwarf - Fortress folder. This file can be used to copy settings between game saves or - players. e.g.: ``savestock food_settings.dfstock`` +Usage: -:loadstock: Loads a saved stockpile settings file and applies it to the currently selected - stockpile. e.g.: ``loadstock food_settings.dfstock`` +``enable stockpiles`` + Add a hotkey that you can hit to easily save and load settings from + stockpiles selected in :kbd:`q` mode. +``copystock`` + Copies the parameters of the currently highlighted stockpile to the custom + stockpile settings and switches to custom stockpile placement mode, + effectively allowing you to copy/paste stockpiles easily. +``savestock `` + Saves the currently highlighted stockpile's settings to a file in your + Dwarf Fortress folder. This file can be used to copy settings between game + saves or players. +``loadstock `` + Loads a saved stockpile settings file and applies it to the currently + selected stockpile. -To use savestock and loadstock, use the :kbd:`q` command to highlight a stockpile. -Then run savestock giving it a descriptive filename. Then, in a different (or -the same!) gameworld, you can highlight any stockpile with :kbd:`q` then execute the -``loadstock`` command passing it the name of that file. The settings will be -applied to that stockpile. +Filenames with spaces are not supported. Generated materials, divine metals, +etc. are not saved as they are different in every world. -Note that files are relative to the DF folder, so put your files there or in a -subfolder for easy access. Filenames should not have spaces. Generated materials, -divine metals, etc are not saved as they are different in every world. +Examples +-------- + +``savestock food_settings.dfstock`` + Export the stockpile settings for the stockpile currently selected in + :kbd:`q` mode to a file named ``food_settings.dfstock``. +``loadstock food_settings.dfstock`` + Set the selected stockpile settings to those saved in the + ``food_settings.dfstock`` file. diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 05e5c9f96..aa36e20bd 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -61,39 +61,21 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector : filename to save stockpile settings to (will be overwritten!)\n" - ) - ); - commands.push_back ( - PluginCommand ( - "loadstock", "Load and apply stockpile settings from a file.", - loadstock, loadstock_guard, - "Must be in 'q' mode and have a stockpile selected.\n" - "example: 'loadstock food.dfstock' will load the settings from 'food.dfstock'\n" - "in your stockpile folder and apply them to the selected stockpile.\n" - " -d, --debug: enable debug output\n" - " : filename to load stockpile settings from\n" - ) - ); + commands.push_back(PluginCommand( + "copystock", + "Copy stockpile under cursor.", + copystock, + copystock_guard)); + commands.push_back(PluginCommand( + "savestock", + "Save the active stockpile's settings to a file.", + savestock, + savestock_guard)); + commands.push_back(PluginCommand( + "loadstock", + "Load and apply stockpile settings from a file.", + loadstock, + loadstock_guard)); } return CR_OK; From e1e245b4b41784f0c1f5733938e65a708ac3c84d Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:30:04 -0700 Subject: [PATCH 198/334] update docs for stocks --- docs/plugins/stocks.rst | 19 +++++++++++++++++-- plugins/stocks.cpp | 8 ++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index 404c96a6d..076f2ab6f 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -1,5 +1,20 @@ stocks ====== -Replaces the DF stocks screen with an improved version. - +Tags: :dfhack-keybind:`stocks` + +Enhanced fortress stock management interface. When the plugin is enabled, two +new hotkeys become available: + +* :kbd:`e` on the vanilla DF stocks screen (:kbd:`z` and then select Stocks) + will launch the fortress-wide stock management screen. +* :kbd:`i` when a stockpile is selected in :kbd:`q` mode will launch the + stockpile inventory management screen. + +Usage:: + + enable stocks + stocks show + +Running ``stocks show`` will bring you to the fortress-wide stock management +screen from whereever you are. diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 7c715d956..62c13dcd5 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -1469,10 +1469,10 @@ static command_result stocks_cmd(color_ostream &out, vector & parameter DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back( - PluginCommand( - "stocks", "An improved stocks display screen", - stocks_cmd, false, "Run 'stocks show' open the stocks display screen, or 'stocks version' to query the plugin version.")); + commands.push_back(PluginCommand( + "stocks", + "An improved stocks management screen.", + stocks_cmd)); ViewscreenStocks::reset(); From 464f566928b4d6c883176a18f93620ddb63048a2 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:30:19 -0700 Subject: [PATCH 199/334] update docs for stonesense --- conf.py | 3 +- docs/plugins/stonesense.rst | 81 ++++++++++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/conf.py b/conf.py index b01027a6b..08c2e6bd8 100644 --- a/conf.py +++ b/conf.py @@ -183,7 +183,7 @@ def write_tool_docs(): mode=0o755, exist_ok=True) with write_file_if_changed('docs/tools/{}.rst'.format(k[0])) as outfile: outfile.write(header) - if k[0] != 'search' and k[0] != 'stonesense': + if k[0] != 'search': outfile.write(label) outfile.write(include) @@ -315,6 +315,7 @@ exclude_patterns = [ 'docs/pseudoxml/*', 'docs/xml/*', 'scripts/docs/*', + 'plugins/*', ] # The reST default role (used for this markup: `text`) to use for all diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index b6c154e66..59212444b 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -1,10 +1,79 @@ -.. _plugin-stonesense: - stonesense ========== -An isometric visualizer that runs in a second window. Usage: +Tags: +:dfhack-keybind:`stonesense` +:dfhack-keybind:`ssense` + +A 3D isometric visualizer that runs in a second window. + +Usage: + +``stonesense`` or ``ssense`` + Open the visualiser in a new window. +``ssense overlay`` + Overlay DF window, replacing the map area. + +The viewer window has read-only access to the game, and can follow the game view +or be moved independently. Configuration for stonesense can be set in the +``stonesense/init.txt`` file in your DF game directory. If the window refresh +rate is too low, change ``SEGMENTSIZE_Z`` to ``2`` in this file, and if you are +unable to see the edges of the map with the overlay active, try decreasing the +value for ``SEGMENTSIZE_XY`` -- normal values are ``50`` to ``80``, depending +on your screen resolution. + +If you replace the map section of your DF window with ``ssense overlay``, be +aware that it's not (yet) suitable for use as your only interface. Use DF's +``[PRINT_MODE:2D]`` init option (in ``data/init/init.txt``) for stability. + +.. figure:: ../images/stonesense-roadtruss.jpg + :align: center + :target: http://www.bay12forums.com/smf/index.php?topic=48172.msg3198664#msg3198664 + + The above-ground part of the fortress *Roadtruss*. + +Controls +-------- +Mouse controls are hard-coded and cannot be changed. + +:Left click: Move debug cursor (if available) +:Right click: Recenter screen +:Scrollwheel: Move up and down +:Ctrl-Scroll: Increase/decrease Z depth shown + +Follow mode makes the Stonesense view follow the location of the DF +window. The offset can be adjusted by holding :kbd:`Ctrl` while using the +keyboard window movement keys. When you turn on cursor follow mode, the +Stonesense debug cursor will follow the DF cursor when the latter exists. + +You can take screenshots with :kbd:`F5`, larger screenshots with +:kbd:`Ctrl`:kbd:`F5`, and screenshot the whole map at full resolution with +:kbd:`Ctrl`:kbd:`Shift`:kbd:`F5`. Screenshots are saved to the DF directory. +Note that feedback is printed to the DFHack console, and you may need +to zoom out before taking very large screenshots. + +See ``stonesense/keybinds.txt`` to learn or set keybindings, including +zooming, changing the dimensions of the rendered area, toggling various +views, fog, and rotation. Here's the important section: + +.. include:: ../../plugins/stonesense/resources/keybinds.txt + :literal: + :end-before: VALID ACTIONS: + +Known Issues +------------ +If Stonesense gives an error saying that it can't load +:file:`creatures/large_256/*.png`, your video card cannot handle the high +detail sprites used. Either open :file:`creatures/init.txt` and remove the +line containing that folder, or :dffd:`use these smaller sprites <6096>`. -:stonesense: Open the visualiser in a new window. Alias ``ssense``. -:ssense overlay: Overlay DF window, replacing the map area. +Stonesense requires working graphics acceleration, and we recommend +at least a dual core CPU to avoid slowing down your game of DF. -For more information, see `the full Stonesense README `. +Useful links +------------ +- :forums:`Official Stonesense thread <106497>` for feedback, + questions, requests or bug reports +- :forums:`Screenshots thread <48172>` +- :wiki:`Main wiki page ` +- :wiki:`How to add content ` +- `Stonesense on Github `_ From 3d0eab1a9b20f460d6fa26e2ce4d4988d243e3a3 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:30:33 -0700 Subject: [PATCH 200/334] update docs for strangemood --- docs/plugins/strangemood.rst | 56 +++++++++++++++++++++++++----------- plugins/strangemood.cpp | 14 +++------ 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index 0d578c363..d6065f8eb 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -1,19 +1,41 @@ strangemood =========== -Creates a strange mood job the same way the game itself normally does it. - -Options: - -:-force: Ignore normal strange mood preconditions (no recent mood, minimum - moodable population, artifact limit not reached). -:-unit: Make the strange mood strike the selected unit instead of picking - one randomly. Unit eligibility is still enforced. -:-type : Force the mood to be of a particular type instead of choosing randomly based on happiness. - Valid values for T are "fey", "secretive", "possessed", "fell", and "macabre". -:-skill S: Force the mood to use a specific skill instead of choosing the highest moodable skill. - Valid values are "miner", "carpenter", "engraver", "mason", "tanner", "weaver", - "clothier", "weaponsmith", "armorsmith", "metalsmith", "gemcutter", "gemsetter", - "woodcrafter", "stonecrafter", "metalcrafter", "glassmaker", "leatherworker", - "bonecarver", "bowyer", and "mechanic". - -Known limitations: if the selected unit is currently performing a job, the mood will not be started. +Tags: +:dfhack-keybind:`strangemood` + +Triggers a strange mood. + +Usage:: + + stangemood [] + +Examples +-------- + +``strangemood -force -unit -type secretive -skill armorsmith`` + Trigger a strange mood for the selected unit that will cause them to become + a legendary armorsmith. + +Options +------- + +``-force`` + Ignore normal strange mood preconditions (no recent mood, minimum moodable + population, artifact limit not reached, etc.). +``-unit`` + Make the strange mood strike the selected unit instead of picking one + randomly. Unit eligibility is still enforced (unless ``-force`` is also + specified). +``-type `` + Force the mood to be of a particular type instead of choosing randomly based + on happiness. Valid values are "fey", "secretive", "possessed", "fell", and + "macabre". +``-skill `` + Force the mood to use a specific skill instead of choosing the highest + moodable skill. Valid values are "miner", "carpenter", "engraver", "mason", + "tanner", "weaver", "clothier", "weaponsmith", "armorsmith", "metalsmith", + "gemcutter", "gemsetter", "woodcrafter", "stonecrafter", "metalcrafter", + "glassmaker", "leatherworker", "bonecarver", "bowyer", and "mechanic". + +Known limitations: if the selected unit is currently performing a job, the mood +will not be triggered. diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp index de7fa426d..a5b8e74a4 100644 --- a/plugins/strangemood.cpp +++ b/plugins/strangemood.cpp @@ -1234,16 +1234,10 @@ command_result df_strangemood (color_ostream &out, vector & parameters) DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("strangemood", "Force a strange mood to happen.", df_strangemood, false, - "Options:\n" - " -force - Ignore standard mood preconditions.\n" - " -unit - Use the selected unit instead of picking one randomly.\n" - " -type - Force the mood to be of a specific type.\n" - " Valid types: fey, secretive, possessed, fell, macabre\n" - " -skill - Force the mood to use a specific skill.\n" - " Skill name must be lowercase and without spaces.\n" - " Example: miner, gemcutter, metalcrafter, bonecarver, mason\n" - )); + commands.push_back(PluginCommand( + "strangemood", + "Trigger a strange mood.", + df_strangemood)); rng.init(); return CR_OK; From b240748684a953d9279667c019a100ce0f698e31 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:30:46 -0700 Subject: [PATCH 201/334] update docs for tailor --- docs/plugins/tailor.rst | 40 +++++++++++++++++++++++++++++++++------- plugins/tailor.cpp | 28 +++++----------------------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index ee1567fdb..19574bc3e 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -1,11 +1,37 @@ tailor ====== +Tags: +:dfhack-keybind:`tailor` -Whenever the bookkeeper updates stockpile records, this plugin will scan every unit in the fort, -count up the number that are worn, and then order enough more made to replace all worn items. -If there are enough replacement items in inventory to replace all worn items, the units wearing them -will have the worn items confiscated (in the same manner as the `cleanowned` plugin) so that they'll -reeequip with replacement items. +Automatically keep your dwarves in fresh clothing. Whenever the bookkeeper +updates stockpile records, this plugin will scan the fort. If there are +fresh cloths available, dwarves who are wearing tattered clothing will have +their rags confiscated (in the same manner as the `cleanowned` tool) so that +they'll reequip with replacement clothes. -Use the `enable` and `disable ` commands to toggle this plugin's status, or run -``tailor status`` to check its current status. +If there are not enough clothes available, manager orders will be generated +to manufacture some more. ``tailor`` will intelligently create orders using +raw materials that you have on hand in the fort. For example, if you have +lots of silk, but no cloth, then ``tailor`` will order only silk clothing to +be made. + +Usage:: + + enable tailor + tailor status + tailor materials [ ...] + +By default, ``tailor`` will prefer using materials in this order:: + + silk cloth yarn leather + +but you can use the ``tailor materials`` command to restrict which materials +are used, and in what order. + +Example +------- + +``tailor materials silk cloth yarn`` + Restrict the materials used for automatically manufacturing clothing to + silk, cloth, and yarn, preferred in that order. This saves leather for + other uses, like making armor. diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index a48bea2c6..bd11988d0 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -41,26 +41,6 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(ui); -const char* tagline = "Allow the bookkeeper to queue jobs to keep dwarfs in adequate clothing."; -const char* usage = ( - " tailor enable\n" - " Enable the plugin.\n" - " tailor disable\n" - " Disable the plugin.\n" - " tailor status\n" - " Display plugin status\n" - " tailor materials ...\n" - " for example: tailor materials silk cloth yarn leather\n" - " Set allowed material list to the specified list.\n" - " The example sets the list to silk, cloth, yarn, leather, in that order, which is the default.\n" - "\n" - "Whenever the bookkeeper updates stockpile records, this plugin will scan every unit in the fort,\n" - "count up the number that are worn, and then order enough more made to replace all worn items.\n" - "If there are enough replacement items in inventory to replace all worn items, the units wearing them\n" - "will have the worn items confiscated (in the same manner as the _cleanowned_ plugin) so that they'll\n" - "reeequip with replacement items.\n" - ); - class Tailor { // ARMOR, SHOES, HELM, GLOVES, PANTS @@ -581,8 +561,7 @@ static command_result tailor_cmd(color_ostream& out, vector & parameters } else if (parameters.size() == 1 && (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?")) { - out.print("%s: %s\nUsage:\n%s", plugin_name, tagline, usage); - return CR_OK; + return CR_WRONG_USAGE; } else if (parameters.size() == 1 && parameters[0] == "test") { @@ -649,7 +628,10 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector Date: Sun, 31 Jul 2022 13:30:58 -0700 Subject: [PATCH 202/334] update docs for tiletypes --- docs/plugins/tiletypes.rst | 253 +++++++++++++++++++++---------------- plugins/tiletypes.cpp | 10 +- 2 files changed, 146 insertions(+), 117 deletions(-) diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 74af00240..cbdbd6cc5 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -3,116 +3,149 @@ tiletypes ========= -Can be used for painting map tiles and is an interactive command, much like -`liquids`. Some properties of existing tiles can be looked up with `probe`. If -something goes wrong, `fixveins` may help. - -The tool works with two set of options and a brush. The brush determines which -tiles will be processed. First set of options is the filter, which can exclude -some of the tiles from the brush by looking at the tile properties. The second -set of options is the paint - this determines how the selected tiles are -changed. - -Both paint and filter can have many different properties including things like -general shape (WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, -etc.), state of 'designated', 'hidden' and 'light' flags. - -The properties of filter and paint can be partially defined. This means that -you can for example turn all stone fortifications into floors, preserving the -material:: - - filter material STONE - filter shape FORTIFICATION - paint shape FLOOR - -Or turn mineral vein floors back into walls:: - - filter shape FLOOR - filter material MINERAL - paint shape WALL - -The tool also allows tweaking some tile flags:: - - paint hidden 1 - paint hidden 0 - -This will hide previously revealed tiles (or show hidden with the 0 option). - -More recently, the tool supports changing the base material of the tile to -an arbitrary stone from the raws, by creating new veins as required. Note -that this mode paints under ice and constructions, instead of overwriting -them. To enable, use:: - - paint stone MICROCLINE - -This mode is incompatible with the regular ``material`` setting, so changing -it cancels the specific stone selection:: - - paint material ANY - -Since different vein types have different drop rates, it is possible to choose -which one to use in painting:: - - paint veintype CLUSTER_SMALL - -When the chosen type is ``CLUSTER`` (the default), the tool may automatically -choose to use layer stone or lava stone instead of veins if its material matches -the desired one. - -Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:: - - paint hidden ANY - paint shape ANY - filter material any - filter shape any - filter any - -You can use several different brushes for painting tiles: - -:point: a single tile -:range: a rectangular range -:column: a column ranging from current cursor to the first solid tile above -:block: a DF map block - 16x16 tiles, in a regular grid - -Example:: - - range 10 10 1 - -This will change the brush to a rectangle spanning 10x10 tiles on one z-level. -The range starts at the position of the cursor and goes to the east, south and -up. - -For more details, use ``tiletypes help``. - -tiletypes-command ------------------ -Runs tiletypes commands, separated by ``;``. This makes it possible to change -tiletypes modes from a hotkey or via dfhack-run. - -Example:: - - tiletypes-command p any ; p s wall ; p sp normal - -This resets the paint filter to unsmoothed walls. - -tiletypes-here-point --------------------- -Apply the current tiletypes options at the in-game cursor position to a single -tile. Can be used from a hotkey. - -This command supports the same options as `tiletypes-here` above. - -tiletypes-here --------------- -Apply the current tiletypes options at the in-game cursor position, including -the brush. Can be used from a hotkey. - -Options: - -:``-c``, ``--cursor ,,``: +Tags: +:dfhack-keybind:`tiletypes` +:dfhack-keybind:`tiletypes-command` +:dfhack-keybind:`tiletypes-here` +:dfhack-keybind:`tiletypes-here-point` + +Paints tiles of specified types onto the map. You can use the `probe` command +to discover properties of existing tiles that you'd like to copy. If you +accidentally paint over a vein that you want back, `fixveins` may help. + +The tool works with a brush, a filter, and a paint specification. The brush +determines the shape of the area to affect, the filter selects which tiles to +affect, and the paint specification determines how to affect those tiles. + +Both paint and filter can have many different properties, like general shape +(WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, etc.), specific +materials (MICROCLINE, MARBLE, etc.), state of 'designated', 'hidden', and +'light' flags, and many others. + +Usage: + +``tiletypes`` + Start the interactive terminal prompt where you can iteratively modify + the brush, filter, and paint specification and get help on syntax + elements. When in the interactive prompt, type ``quit`` to get out. +``tiletypes-command [; ...]`` + Run ``tiletypes`` commands from outside the interactive prompt. You can + use this form from hotkeys or `dfhack-run` to set specific tiletypes + properties. You can run multiple commands on one line by separating them + with :literal:`\ ; \ ` -- that's a semicolon with a space on either side. + See the Commands_ section below for an overview of commands you can run. +``tiletypes-here []`` + Apply the current options set in ``tiletypes`` and/or ``tiletypes-command`` + at the in-game cursor position, including the brush. Can be used from a + hotkey. +``tiletypes-here-point []`` + Apply the current options set in ``tiletypes`` and/or ``tiletypes-command`` + at the in-game cursor position to a single tile (ignoring brush settings). + Can be used from a hotkey. + +Examples +-------- + +``tiletypes-command filter material STONE ; f shape WALL ; paint shape FLOOR`` + Turn all stone walls into floors, preserving the material. +``tiletypes-command p any ; p s wall ; p sp normal`` + Clear the paint specificaiton and set it to unsmoothed walls. +``tiletypes-command f any ; p stone marble ; p sh wall ; p sp normal ; r 10 10`` + Prepare to paint a 10x10 area of marble walls, ready for harvesting for + flux. +``tiletypes-command f any ; f designated 1 ; p any ; p hidden 0 ; block ; run`` + Set the filter to match designated tiles, the paint specification to unhide + them, and the brush to cover all tiles in the current block. Then run itThis is useful + for unhiding tiles you wish to dig out of an aquifer so the game doesn't + pause and undesignate adjacent tiles every time a new damp tile is + "discovered". + +Options +------- + +``-c``, ``--cursor ,,`` Use the specified map coordinates instead of the current cursor position. If this option is specified, then an active game map cursor is not necessary. -:``-h``, ``--help``: - Show command help text. -:``-q``, ``--quiet``: +``-q``, ``--quiet`` Suppress non-error status output. + +Commands +-------- + +Commands can set the brush or modify the filter or paint options. When at the +interactive ``tiletypes>`` prompt, the command ``run`` (or hitting enter on an +empty line) will apply the current filter and paint specification with the +current brush at the current cursor position. The command ``quit`` will exit. + +Brush commands +`````````````` + +``p``, ``point`` + Use the point brush. +``r``, ``range []`` + Use the range brush with the specified width, height, and depth. If not + specified, depth is 1, meaning just the current z-level. The range starts at + the position of the cursor and goes to the east, south and up (towards the + sky). +``block`` + Use the block brush, which includes all tiles in the 16x16 block that + includes the cursor. +``column`` + Use the column brush, which ranges from the current cursor position to the + first solid tile above it. This is useful for filling the empty space in a + cavern. + +Filter and paint commands +````````````````````````` + +The general forms for modifying the filter or paint specification are: + +``f``, ``filter `` + Modify the filter. +``p``, ``paint `` + Modify the paint specification. + +The options identify the property of the tile and the value of that property: + +``any`` + Reset to default (no filter/paint). +``s``, ``sh``, ``shape `` + Tile shape information. Run ``:lua @df.tiletype_shape`` to see valid shapes, + or use a shape of ``any`` to clear the current setting. +``m``, ``mat``, ``material `` + Tile material information. Run ``:lua @df.tiletype_material`` to see valid + materials, or use a material of ``any`` to clear the current setting. +``sp``, ``special `` + Tile special information. Run ``:lua @df.tiletype_special`` to see valid + special values, or use a special value of ``any`` to clear the current + setting. +``v``, ``var``, ``variant `` + Tile variant information. Run ``:lua @df.tiletype_variant`` to see valid + variant values, or use a variant value of ``any`` to clear the current + setting. +``a``, ``all [] [] [] []`` + Set values for any or all of shape, material, special, and/or variant, in + any order. +``d``, ``designated 0|1`` + Only useful for the filter, since you can't "paint" designations. +``h``, ``hidden 0|1`` + Whether a tile is hidden. A value of ``0`` means "revealed". +``l``, ``light 0|1`` + Whether a tile is marked as "Light". A value of ``0`` means "dark". +``st``, ``subterranean 0|1`` + Whether a tile is marked as "Subterranean". +``sv``, ``skyview 0|1`` + Whether a tile is marked as "Outside". A value of ``0`` means "inside". +``aqua``, ``aquifer 0|1`` + Whether a tile is marked as an aquifer. +``stone `` + Set a particular type of stone, creating veins as required. To see a list of + valid stone types, run: ``:lua for _,mat in ipairs(df.global.world.raws.inorganics) do if mat.material.flags.IS_STONE and not mat.material.flags.NO_STONE_STOCKPILE then print(mat.id) end end`` + Note that this command paints under ice and constructions, instead of + overwriting them. Also note that specifying a specific ``stone`` will cancel + out anything you have specified for ``material``, and vice-versa. +``veintype `` + Set a particular vein type for the ``stone`` option to take advantage of the + different boulder drop rates. To see valid vein types, run + ``:lua @df.inclusion_type``, or use vein type ``CLUSTER`` to reset to the + default. diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index b8bbc8d04..f3773b0fe 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -85,10 +85,10 @@ command_result df_tiletypes_here_point (color_ostream &out, vector & pa DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { tiletypes_hist.load(HISTORY_FILE); - commands.push_back(PluginCommand("tiletypes", "Paint map tiles freely, similar to liquids.", df_tiletypes, true)); + commands.push_back(PluginCommand("tiletypes", "Paints tiles of specified types onto the map.", df_tiletypes, true)); commands.push_back(PluginCommand("tiletypes-command", "Run tiletypes commands (seperated by ' ; ')", df_tiletypes_command)); commands.push_back(PluginCommand("tiletypes-here", "Repeat tiletypes command at cursor (with brush)", df_tiletypes_here)); - commands.push_back(PluginCommand("tiletypes-here-point", "Repeat tiletypes command at cursor (without brush)", df_tiletypes_here_point)); + commands.push_back(PluginCommand("tiletypes-here-point", "Repeat tiletypes command at cursor (with single tile brush)", df_tiletypes_here_point)); return CR_OK; } @@ -1000,11 +1000,7 @@ command_result df_tiletypes (color_ostream &out_, vector & parameters) { if(parameters[i] == "help" || parameters[i] == "?") { - out_.print("This tool allows painting tiles types with a brush, using an optional filter.\n" - "The tool is interactive, similarly to the liquids tool.\n" - "Further help is available inside.\n" - ); - return CR_OK; + return CR_WRONG_USAGE; } } From 2362d60a3aa9d200a234d5a6c2d330eedc1b9a00 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:31:15 -0700 Subject: [PATCH 203/334] update docs for title-folder and title-version --- docs/plugins/title-folder.rst | 8 +++++++- docs/plugins/title-version.rst | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index 4e1ef40ec..3156a9e1c 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -1,3 +1,9 @@ title-folder ============= -Displays the DF folder name in the window title bar when enabled. +Tags: + +Displays the DF folder name in the window title bar. + +Usage:: + + enable title-folder diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index aed7a02e0..267179f14 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -1,3 +1,9 @@ title-version ============= -Displays the DFHack version on DF's title screen when enabled. +Tags: + +Displays the DFHack version on DF's title screen. + +Usage:: + + enable title-version From a6bdd9e3dc20fe0b6d1b9644e3b9eb5118df0b63 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:31:26 -0700 Subject: [PATCH 204/334] update docs for trackstop --- docs/plugins/trackstop.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index b012df34d..72466a1bf 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -1,5 +1,12 @@ trackstop ========= -Adds a :kbd:`q` menu for track stops, which is completely blank by default. +Tags: + +Adds dynamic configuration options for track stops. When enabled, this plugin +adds a :kbd:`q` menu for track stops, which is completely blank in vanilla DF. This allows you to view and/or change the track stop's friction and dump direction settings, using the keybindings from the track stop building interface. + +Usage:: + + enable trackstop From a5a57a86312352e150f40dfaae66545971ee933e Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:31:40 -0700 Subject: [PATCH 205/334] update docs for tubefill --- docs/plugins/tubefill.rst | 15 +++++++++------ plugins/tubefill.cpp | 7 ++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index 99c0b76bd..15a9ba2f2 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -1,11 +1,14 @@ tubefill ======== -Fills all the adamantine veins again. Veins that were hollow will be left -alone. +Tags: +:dfhack-keybind:`tubefill` -Options: +Replentishes mined-out adamantine. Veins that were hollow will be left alone. -:hollow: fill in naturally hollow veins too +Usage:: -Beware that filling in hollow veins will trigger a demon invasion on top of -your miner when you dig into the region that used to be hollow. + tubefill [hollow] + +Specify ``hollow`` to fill in naturally hollow veins too, but be aware that this +will trigger a demon invasion on top of your miner when you dig into the region +that used to be hollow. You have been warned! diff --git a/plugins/tubefill.cpp b/plugins/tubefill.cpp index 1803ed520..f86e77c76 100644 --- a/plugins/tubefill.cpp +++ b/plugins/tubefill.cpp @@ -40,9 +40,10 @@ command_result tubefill(color_ostream &out, std::vector & params); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("tubefill","Fill in all the adamantine tubes again.",tubefill, false, - "Replenishes mined out adamantine but does not fill hollow adamantine tubes.\n" - "Specify 'hollow' to fill hollow tubes, but beware glitchy HFS spawns.\n")); + commands.push_back(PluginCommand( + "tubefill", + "Replentishes mined-out adamantine.", + tubefill)); return CR_OK; } From e4c5b146701803eede95af8c2f62a1193ba2a32a Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:31:54 -0700 Subject: [PATCH 206/334] update docs for tweak --- docs/plugins/tweak.rst | 208 +++++++++++------- plugins/tweak/tweak.cpp | 468 +--------------------------------------- 2 files changed, 130 insertions(+), 546 deletions(-) diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index 4ce7b6d38..56d465f78 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -1,91 +1,137 @@ tweak ===== +Tags: +:dfhack-keybind:`tweak` + Contains various tweaks for minor bugs. -One-shot subcommands: +Usage:: + + tweak [disable] + +Run the ``tweak`` command to run the tweak or enable its effects. For tweaks +that have persistent effects, append the ``disable`` keyword to disable them. + +One-shot commands: -:clear-missing: Remove the missing status from the selected unit. - This allows engraving slabs for ghostly, but not yet - found, creatures. -:clear-ghostly: Remove the ghostly status from the selected unit and mark - it as dead. This allows getting rid of bugged ghosts - which do not show up in the engraving slab menu at all, - even after using clear-missing. It works, but is - potentially very dangerous - so use with care. Probably - (almost certainly) it does not have the same effects like - a proper burial. You've been warned. -:fixmigrant: Remove the resident/merchant flag from the selected unit. - Intended to fix bugged migrants/traders who stay at the - map edge and don't enter your fort. Only works for - dwarves (or generally the player's race in modded games). - Do NOT abuse this for 'real' caravan merchants (if you - really want to kidnap them, use 'tweak makeown' instead, - otherwise they will have their clothes set to forbidden etc). -:makeown: Force selected unit to become a member of your fort. - Can be abused to grab caravan merchants and escorts, even if - they don't belong to the player's race. Foreign sentients - (humans, elves) can be put to work, but you can't assign rooms - to them and they don't show up in DwarfTherapist because the - game treats them like pets. Grabbing draft animals from - a caravan can result in weirdness (animals go insane or berserk - and are not flagged as tame), but you are allowed to mark them - for slaughter. Grabbing wagons results in some funny spam, then - they are scuttled. +``clear-missing`` + Remove the missing status from the selected unit. This allows engraving + slabs for ghostly, but not yet found, creatures. +``clear-ghostly`` + Remove the ghostly status from the selected unit and mark it as dead. This + allows getting rid of bugged ghosts which do not show up in the engraving + slab menu at all, even after using ``clear-missing``. It works, but is + potentially very dangerous - so use with care. Probably (almost certainly) + it does not have the same effects like a proper burial. You've been warned. +``fixmigrant`` + Remove the resident/merchant flag from the selected unit. Intended to fix + bugged migrants/traders who stay at the map edge and don't enter your fort. + Only works for dwarves (or generally the player's race in modded games). + Do NOT abuse this for 'real' caravan merchants (if you really want to kidnap + them, use ``tweak makeown`` instead, otherwise they will have their clothes + set to forbidden). +``makeown`` + Force selected unit to become a member of your fort. Can be abused to grab + caravan merchants and escorts, even if they don't belong to the player's + race. Foreign sentients (humans, elves) can be put to work, but you can't + assign rooms to them and they don't show up in labor management programs + (like `manipulator` or Dwarf Therapist) because the game treats them like + pets. Grabbing draft animals from a caravan can result in weirdness + (animals go insane or berserk and are not flagged as tame), but you are + allowed to mark them for slaughter. Grabbing wagons results in some funny + spam, then they are scuttled. -Subcommands that persist until disabled or DF quits: +Commands that persist until disabled or DF quits: -.. comment: sort these alphabetically +.. comment: please sort these alphabetically -:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (:bug:`6481`). -:advmode-contained: Works around :bug:`6202`, custom reactions with container inputs - in advmode. The issue is that the screen tries to force you to select - the contents separately from the container. This forcefully skips child - reagents. -:block-labors: Prevents labors that can't be used from being toggled -:burrow-name-cancel: Implements the "back" option when renaming a burrow, - which currently does nothing (:bug:`1518`) -:cage-butcher: Adds an option to butcher units when viewing cages with :kbd:`q` -:civ-view-agreement: Fixes overlapping text on the "view agreement" screen -:condition-material: Fixes a crash in the work order contition material list (:bug:`9905`). -:craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`). - With this tweak, items made from cloth and leather will gain a level of - wear every 20 years. -:do-job-now: Adds a job priority toggle to the jobs list -:embark-profile-name: Allows the use of lowercase letters when saving embark profiles -:eggs-fertile: Displays a fertility indicator on nestboxes -:farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus -:fast-heat: Further improves temperature update performance by ensuring that 1 degree - of item temperature is crossed in no more than specified number of frames - when updating from the environment temperature. This reduces the time it - takes for stable-temp to stop updates again when equilibrium is disturbed. -:fast-trade: Makes Shift-Down in the Move Goods to Depot and Trade screens select - the current item (fully, in case of a stack), and scroll down one line. -:fps-min: Fixes the in-game minimum FPS setting -:hide-priority: Adds an option to hide designation priority indicators -:hotkey-clear: Adds an option to clear currently-bound hotkeys (in the :kbd:`H` menu) -:import-priority-category: - Allows changing the priority of all goods in a - category when discussing an import agreement with the liaison -:kitchen-prefs-all: Adds an option to toggle cook/brew for all visible items in kitchen preferences -:kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences -:kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs (:bug:`9000`) -:max-wheelbarrow: Allows assigning more than 3 wheelbarrows to a stockpile -:military-color-assigned: - Color squad candidates already assigned to other squads in yellow/green - to make them stand out more in the list. +``adamantine-cloth-wear`` + Prevents adamantine clothing from wearing out while being worn + (:bug:`6481`). +``advmode-contained`` + Fixes custom reactions with container inputs in advmode + (:bug:`6202`) in advmode. The issue is that the screen tries to force you to + select the contents separately from the container. This forcefully skips + child reagents. +``block-labors`` + Prevents labors that can't be used from being toggled. +``burrow-name-cancel`` + Implements the "back" option when renaming a burrow, which currently does + nothing in vanilla DF (:bug:`1518`). +``cage-butcher`` + Adds an option to butcher units when viewing cages with :kbd:`q`. +``civ-view-agreement`` + Fixes overlapping text on the "view agreement" screen. +``condition-material`` + Fixes a crash in the work order contition material list (:bug:`9905`). +``craft-age-wear`` + Fixes crafted items not wearing out over time (:bug:`6003`). With this + tweak, items made from cloth and leather will gain a level of wear every 20 + years. +``do-job-now`` + Adds a job priority toggle to the jobs list. +``embark-profile-name`` + Allows the use of lowercase letters when saving embark profiles. +``eggs-fertile`` + Displays a fertility indicator on nestboxes. +``farm-plot-select`` + Adds "Select all" and "Deselect all" options to farm plot menus. +``fast-heat`` + Improves temperature update performance by ensuring that 1 degree of item + temperature is crossed in no more than specified number of frames when + updating from the environment temperature. This reduces the time it takes + for ``tweak stable-temp`` to stop updates again when equilibrium is + disturbed. +``fast-trade`` + Makes Shift-Down in the Move Goods to Depot and Trade screens toggle the + current item (fully, in case of a stack), and scroll down one line. Shift-Up + undoes the last Shift-Down by scrolling up one line and then toggle the item. +``fps-min`` + Fixes the in-game minimum FPS setting (:bug:`6277`). +``hide-priority`` + Adds an option to hide designation priority indicators. +``hotkey-clear`` + Adds an option to clear currently-bound hotkeys (in the :kbd:`H` menu). +``import-priority-category`` + When meeting with a liaison, makes Shift+Left/Right arrow adjust all items + in category when discussing an import agreement with the liaison. +``kitchen-prefs-all`` + Adds an option to toggle cook/brew for all visible items in kitchen + preferences. +``kitchen-prefs-color`` + Changes color of enabled items to green in kitchen preferences. +``kitchen-prefs-empty`` + Fixes a layout issue with empty kitchen tabs (:bug:`9000`). +``max-wheelbarrow`` + Allows assigning more than 3 wheelbarrows to a stockpile. +``military-color-assigned`` + Color squad candidates already assigned to other squads in yellow/green to + make them stand out more in the list. - .. image:: ../images/tweak-mil-color.png + .. image:``../images/tweak-mil-color.png -:military-stable-assign: - Preserve list order and cursor position when assigning to squad, - i.e. stop the rightmost list of the Positions page of the military - screen from constantly resetting to the top. -:nestbox-color: Fixes the color of built nestboxes -:partial-items: Displays percentages on partially-consumed items such as hospital cloth -:reaction-gloves: Fixes reactions to produce gloves in sets with correct handedness (:bug:`6273`) -:shift-8-scroll: Gives Shift-8 (or :kbd:`*`) priority when scrolling menus, instead of scrolling the map -:stable-cursor: Saves the exact cursor position between t/q/k/d/b/etc menus of fortress mode, if the - map view is near enough to its previous position. -:stone-status-all: Adds an option to toggle the economic status of all stones -:title-start-rename: Adds a safe rename option to the title screen "Start Playing" menu -:tradereq-pet-gender: Displays pet genders on the trade request screen +``military-stable-assign`` + Preserve list order and cursor position when assigning to squad, i.e. stop + the rightmost list of the Positions page of the military screen from + constantly resetting to the top. +``nestbox-color`` + Makes built nestboxes use the color of their material. +``partial-items`` + Displays percentages on partially-consumed items such as hospital cloth. +``pausing-fps-counter`` + Replace fortress mode FPS counter with one that stops counting when paused. +``reaction-gloves`` + Fixes reactions to produce gloves in sets with correct handedness + (:bug:`6273`). +``shift-8-scroll`` + Gives Shift-8 (or :kbd:`*`) priority when scrolling menus, instead of + scrolling the map. +``stable-cursor`` + Saves the exact cursor position between t/q/k/d/b/etc menus of fortress + mode, if the map view is near enough to its previous position. +``stone-status-all`` + Adds an option to toggle the economic status of all stones. +``title-start-rename`` + Adds a safe rename option to the title screen "Start Playing" menu. +``tradereq-pet-gender`` + Displays pet genders on the trade request screen. diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index 38ece6970..6d2a46fa8 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -23,7 +23,6 @@ #include "../uicommon.h" #include "df/ui.h" #include "df/world.h" -#include "df/squad.h" #include "df/unit.h" #include "df/unit_soul.h" #include "df/historical_entity.h" @@ -37,7 +36,6 @@ #include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_kitchenprefst.h" #include "df/viewscreen_layer_unit_actionst.h" -#include "df/squad_order_trainst.h" #include "df/ui_build_selector.h" #include "df/ui_sidebar_menus.h" #include "df/building_trapst.h" @@ -67,17 +65,10 @@ #include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_layer_militaryst.h" -#include "df/squad_position.h" #include "df/job.h" #include "df/general_ref_building_holderst.h" #include "df/unit_health_info.h" #include "df/caste_body_info.h" -#include "df/activity_entry.h" -#include "df/activity_event_combat_trainingst.h" -#include "df/activity_event_individual_skill_drillst.h" -#include "df/activity_event_skill_demonstrationst.h" -#include "df/activity_event_sparringst.h" -//#include "df/building_hivest.h" #include #include @@ -162,113 +153,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector \n" - " Further improves temperature updates by ensuring that 1 degree of\n" - " item temperature is crossed in no more than specified number of frames\n" - " when updating from the environment temperature. Use 0 to disable.\n" - " tweak fast-trade [disable]\n" - " Makes Shift-Enter in the Move Goods to Depot and Trade screens select\n" - " the current item (fully, in case of a stack), and scroll down one line.\n" - " tweak fps-min [disable]\n" - " Fixes the in-game minimum FPS setting (bug 6277)\n" - " tweak hide-priority [disable]\n" - " Adds an option to hide designation priority indicators\n" - " tweak hotkey-clear [disable] \n" - " Adds an option to clear currently-bound hotkeys\n" - " tweak import-priority-category [disable]\n" - " When meeting with a liaison, makes Shift+Left/Right arrow adjust\n" - " the priority of an entire category of imports.\n" - " tweak kitchen-prefs-all [disable]\n" - " Adds an option to toggle cook/brew for all visible items in\n" - " kitchen preferences\n" - " tweak kitchen-prefs-color [disable]\n" - " Changes color of enabled items to green in kitchen preferences\n" - " tweak kitchen-prefs-empty [disable]\n" - " Fixes a layout issue with empty kitchen tabs (bug 9000)\n" - " tweak max-wheelbarrow [disable]\n" - " Allows assigning more than 3 wheelbarrows to a stockpile\n" - " tweak nestbox-color [disable]\n" - " Makes built nestboxes use the color of their material\n" - " tweak military-color-assigned [disable]\n" - " Color squad candidates already assigned to other squads in brown/green\n" - " to make them stand out more in the list.\n" - " tweak military-stable-assign [disable]\n" - " Preserve list order and cursor position when assigning to squad,\n" - " i.e. stop the rightmost list of the Positions page of the military\n" - " screen from constantly jumping to the top.\n" - " tweak partial-items [disable]\n" - " Displays percentages on partially-consumed items such as hospital cloth\n" - " tweak pausing-fps-counter [disable]\n" - " Replace fortress mode FPS counter with one that stops counting \n" - " when paused.\n" - " tweak reaction-gloves [disable]\n" - " Changes custom reactions to produce gloves in sets with correct handedness\n" - " tweak shift-8-scroll [disable]\n" - " Gives Shift+8 (or *) priority when scrolling menus, instead of \n" - " scrolling the map\n" - " tweak stone-status-all [disable]\n" - " Adds an option to toggle the economic status of all stones\n" - " tweak title-start-rename [disable]\n" - " Adds a safe rename option to the title screen \"Start Playing\" menu\n" - " tweak tradereq-pet-gender [disable]\n" - " Displays the gender of pets in the trade request list\n" - // sort these alphabetically -// " tweak military-training [disable]\n" -// " Speed up melee squad training, removing inverse dependency on unit count.\n" - )); + "tweak", + "Various tweaks for minor bugs.", + tweak)); TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_armor_hook, incWearTimer); TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_helm_hook, incWearTimer); @@ -438,339 +325,6 @@ command_result fix_clothing_ownership(color_ostream &out, df::unit* unit) return CR_OK; } -static void correct_dimension(df::item_actual *self, int32_t &delta, int32_t dim) -{ - // Zero dimension or remainder? - if (dim <= 0 || self->stack_size <= 1) return; - int rem = delta % dim; - if (rem == 0) return; - // If destroys, pass through - int intv = delta / dim; - if (intv >= self->stack_size) return; - // Subtract int part - delta = rem; - self->stack_size -= intv; - if (self->stack_size <= 1) return; - - // If kills the item or cannot split, round up. - if (!self->flags.bits.in_inventory || !Items::getContainer(self)) - { - delta = dim; - return; - } - - // Otherwise split the stack - color_ostream_proxy out(Core::getInstance().getConsole()); - out.print("fix-dimensions: splitting stack #%d for delta %d.\n", self->id, delta); - - auto copy = self->splitStack(self->stack_size-1, true); - if (copy) copy->categorize(true); -} - -struct dimension_liquid_hook : df::item_liquid_miscst { - typedef df::item_liquid_miscst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) - { - correct_dimension(this, delta, dimension); - return INTERPOSE_NEXT(subtractDimension)(delta); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dimension_liquid_hook, subtractDimension); - -struct dimension_powder_hook : df::item_powder_miscst { - typedef df::item_powder_miscst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) - { - correct_dimension(this, delta, dimension); - return INTERPOSE_NEXT(subtractDimension)(delta); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dimension_powder_hook, subtractDimension); - -struct dimension_bar_hook : df::item_barst { - typedef df::item_barst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) - { - correct_dimension(this, delta, dimension); - return INTERPOSE_NEXT(subtractDimension)(delta); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dimension_bar_hook, subtractDimension); - -struct dimension_thread_hook : df::item_threadst { - typedef df::item_threadst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) - { - correct_dimension(this, delta, dimension); - return INTERPOSE_NEXT(subtractDimension)(delta); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dimension_thread_hook, subtractDimension); - -struct dimension_cloth_hook : df::item_clothst { - typedef df::item_clothst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) - { - correct_dimension(this, delta, dimension); - return INTERPOSE_NEXT(subtractDimension)(delta); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension); - -/* -// Unit updates are executed based on an action divisor variable, -// which is computed from the alive unit count and has range 10-100. -static int adjust_unit_divisor(int value) { - return value*10/DF_GLOBAL_FIELD(ui, unit_action_divisor, 10); -} - -static bool can_spar(df::unit *unit) { - return unit->counters2.exhaustion <= 2000 && // actually 4000, but leave a gap - (unit->status2.limbs_grasp_count > 0 || unit->status2.limbs_grasp_max == 0) && - (!unit->health || (unit->health->flags.whole&0x7FF) == 0) && - (!unit->job.current_job || unit->job.current_job->job_type != job_type::Rest); -} - -static bool has_spar_inventory(df::unit *unit, df::job_skill skill) -{ - using namespace df::enums::job_skill; - - auto type = ENUM_ATTR(job_skill, type, skill); - - if (type == job_skill_class::MilitaryWeapon) - { - for (size_t i = 0; i < unit->inventory.size(); i++) - { - auto item = unit->inventory[i]; - if (item->mode == df::unit_inventory_item::Weapon && - item->item->getMeleeSkill() == skill) - return true; - } - - return false; - } - - switch (skill) { - case THROW: - case RANGED_COMBAT: - return false; - - case SHIELD: - for (size_t i = 0; i < unit->inventory.size(); i++) - { - auto item = unit->inventory[i]; - if (item->mode == df::unit_inventory_item::Weapon && - item->item->getType() == item_type::SHIELD) - return true; - } - return false; - - case ARMOR: - for (size_t i = 0; i < unit->inventory.size(); i++) - { - auto item = unit->inventory[i]; - if (item->mode == df::unit_inventory_item::Worn && - item->item->isArmorNotClothing()) - return true; - } - return false; - - default: - return true; - } -} - -struct military_training_ct_hook : df::activity_event_combat_trainingst { - typedef df::activity_event_combat_trainingst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) - { - auto act = df::activity_entry::find(activity_id); - int cur_neid = act ? act->next_event_id : 0; - int cur_oc = organize_counter; - - INTERPOSE_NEXT(process)(unit); - - // Shorten the time it takes to organize stuff, so that in - // reality it remains the same instead of growing proportionally - // to the unit count. - if (organize_counter > cur_oc && organize_counter > 0) - organize_counter = adjust_unit_divisor(organize_counter); - - if (act && act->next_event_id > cur_neid) - { - // New events were added. Check them. - for (size_t i = 0; i < act->events.size(); i++) - { - auto event = act->events[i]; - if (event->flags.bits.dismissed || event->event_id < cur_neid) - continue; - - if (auto sp = strict_virtual_cast(event)) - { - // Sparring has a problem in that all of its participants decrement - // the countdown variable. Fix this by multiplying it by the member count. - sp->countdown = sp->countdown * sp->participants.units.size(); - } - else if (auto sd = strict_virtual_cast(event)) - { - // Adjust initial counter values - sd->train_countdown = adjust_unit_divisor(sd->train_countdown); - sd->wait_countdown = adjust_unit_divisor(sd->wait_countdown); - - // Check if the game selected the most skilled unit as the teacher - auto &units = sd->participants.units; - int maxv = -1, cur_xp = -1, minv = 0; - int best = -1; - size_t spar = 0; - - for (size_t j = 0; j < units.size(); j++) - { - auto unit = df::unit::find(units[j]); - if (!unit) continue; - int xp = Units::getExperience(unit, sd->skill, true); - if (units[j] == sd->unit_id) - cur_xp = xp; - if (j == 0 || xp < minv) - minv = xp; - if (xp > maxv) { - maxv = xp; - best = j; - } - if (can_spar(unit) && has_spar_inventory(unit, sd->skill)) - spar++; - } - -#if 0 - color_ostream_proxy out(Core::getInstance().getConsole()); -#endif - - // If the xp gap is low, sometimes replace with sparring - if ((maxv - minv) < 64*15 && spar == units.size() && - random_int(45) >= 30 + (maxv-minv)/64) - { -#if 0 - out.print("Replacing %s demonstration (xp %d-%d, gap %d) with sparring.\n", - ENUM_KEY_STR(job_skill, sd->skill).c_str(), minv, maxv, maxv-minv); -#endif - - if (auto spar = df::allocate()) - { - spar->event_id = sd->event_id; - spar->activity_id = sd->activity_id; - spar->parent_event_id = sd->parent_event_id; - spar->flags = sd->flags; - spar->participants = sd->participants; - spar->building_id = sd->building_id; - spar->countdown = 300*units.size(); - - delete sd; - act->events[i] = spar; - - continue; - } - } - - // If the teacher has less xp than somebody else, switch - if (best >= 0 && maxv > cur_xp) - { -#if 0 - out.print("Replacing %s teacher %d (%d xp) with %d (%d xp); xp gap %d.\n", - ENUM_KEY_STR(job_skill, sd->skill).c_str(), - sd->unit_id, cur_xp, units[best], maxv, maxv-minv); -#endif - - sd->hist_figure_id = sd->participants.histfigs[best]; - sd->unit_id = units[best]; - } - else - { -#if 0 - out.print("Not changing %s demonstration (xp %d-%d, gap %d).\n", - ENUM_KEY_STR(job_skill, sd->skill).c_str(), - minv, maxv, maxv-minv); -#endif - } - } - } - } - } -}; -*/ - -/* -IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process); - -struct military_training_sd_hook : df::activity_event_skill_demonstrationst { - typedef df::activity_event_skill_demonstrationst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) - { - int cur_oc = organize_counter; - int cur_tc = train_countdown; - - INTERPOSE_NEXT(process)(unit); - - // Shorten the counters if they changed - if (organize_counter > cur_oc && organize_counter > 0) - organize_counter = adjust_unit_divisor(organize_counter); - if (train_countdown > cur_tc) - train_countdown = adjust_unit_divisor(train_countdown); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(military_training_sd_hook, process); - -template -bool is_done(T *event, df::unit *unit) -{ - return event->flags.bits.dismissed || - binsearch_index(event->participants.units, unit->id) < 0; -} - -struct military_training_sp_hook : df::activity_event_sparringst { - typedef df::activity_event_sparringst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) - { - INTERPOSE_NEXT(process)(unit); - - // Since there are no counters to fix, repeat the call - int cnt = (DF_GLOBAL_FIELD(ui, unit_action_divisor, 10)+5) / 10; - for (int i = 1; i < cnt && !is_done(this, unit); i++) - INTERPOSE_NEXT(process)(unit); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(military_training_sp_hook, process); - -struct military_training_id_hook : df::activity_event_individual_skill_drillst { - typedef df::activity_event_individual_skill_drillst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) - { - INTERPOSE_NEXT(process)(unit); - - // Since there are no counters to fix, repeat the call - int cnt = (DF_GLOBAL_FIELD(ui, unit_action_divisor, 10)+5) / 10; - for (int i = 1; i < cnt && !is_done(this, unit); i++) - INTERPOSE_NEXT(process)(unit); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process); -*/ - static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -931,22 +485,6 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_tweak(cmd, out, parameters); return CR_OK; } - /*else if (cmd == "fix-dimensions") - { - enable_hook(out, INTERPOSE_HOOK(dimension_liquid_hook, subtractDimension), parameters); - enable_hook(out, INTERPOSE_HOOK(dimension_powder_hook, subtractDimension), parameters); - enable_hook(out, INTERPOSE_HOOK(dimension_bar_hook, subtractDimension), parameters); - enable_hook(out, INTERPOSE_HOOK(dimension_thread_hook, subtractDimension), parameters); - enable_hook(out, INTERPOSE_HOOK(dimension_cloth_hook, subtractDimension), parameters); - }*/ -/* - else if (cmd == "military-training") - { - enable_hook(out, INTERPOSE_HOOK(military_training_ct_hook, process), parameters); - enable_hook(out, INTERPOSE_HOOK(military_training_sd_hook, process), parameters); - enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters); - enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters); - }*/ else { return enable_tweak(cmd, out, parameters); From 6686f703aeeae31d43c6c548e5860a6bbbf7831c Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 31 Jul 2022 13:32:06 -0700 Subject: [PATCH 207/334] update docs for workNow --- docs/plugins/workNow.rst | 23 +++++++++++++++-------- plugins/workNow.cpp | 14 ++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index bacecf612..dda3fea5c 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -1,12 +1,19 @@ workNow ======= -Don't allow dwarves to idle if any jobs are available. +Tags: +:dfhack-keybind:`workNow` -When workNow is active, every time the game pauses, DF will make dwarves -perform any appropriate available jobs. This includes when you one step -through the game using the pause menu. Usage: +Reduce the time that dwarves idle after completing a job. After finishing a job, +dwarves will wander away for a while before picking up a new job. This plugin +will automatically poke the game to assign dwarves to new tasks. -:workNow: print workNow status -:workNow 0: deactivate workNow -:workNow 1: activate workNow (look for jobs on pause, and only then) -:workNow 2: make dwarves look for jobs whenever a job completes +Usage: + +``workNow`` + Print current plugin status. +``workNow 0`` + Stop monitoring and poking. +``workNow 1`` + Poke the game to assign dwarves to tasks whenever the game is paused. +``workNow 2`` + Poke the game to assign dwarves to tasks whenever a dwarf finishes a job. diff --git a/plugins/workNow.cpp b/plugins/workNow.cpp index ae76b09f3..2286fee7e 100644 --- a/plugins/workNow.cpp +++ b/plugins/workNow.cpp @@ -29,16 +29,10 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector Date: Sun, 31 Jul 2022 13:38:25 -0700 Subject: [PATCH 208/334] add image for stonesense --- docs/images/stonesense-roadtruss.jpg | Bin 0 -> 614174 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/stonesense-roadtruss.jpg diff --git a/docs/images/stonesense-roadtruss.jpg b/docs/images/stonesense-roadtruss.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19e71dd704f0a4edb0dbe6bdaa41d6c5ed282fec GIT binary patch literal 614174 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 z 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 z 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

4uCl70nz7YfsK(p(u%umU`hMx2${ z|B5G9+-@(wEo}r7DSu-~UpB%iCAdu=j&O73GYvI^&Xr427HMp+vUVE>J|%mi>g^~G z**reaOFhOHUqP6dfkr}Oy#-398x9>}x)<5rSwyoCvliS)-|V!TO{L{Czi6mX7Yf4@ zzEpdVGa~ap&R6_6^I878PiQP0sS{=A*I|^NjV{7uV5_VoL{vG^Xj8l>V{VjU!2fz- z^Zd4d4;+OPBNr=aS#==bjOdi2w;-)afGJ~palL3(?-bC@{vP%LFsw`}FjZTrij$B9 z-Nh;K)Or>jQsiba}kZ66->{Zv=48NG+*Bo0O#tOZ@;Lg;KyL1k!5g`5ck- zqdQ5?bL9Y=Y56IU^yxSJZutjf1=7O6Cd&(%E@>jaS!Wb&0on{tm=Kt=L68&qC#@ew ztH@hy+XDPOtxq=^kpr)*_LOGDr)-?gm-$jbR-`J)m62c9O)yQRO;o+M|Kzud|H*IT z>m_c}{%zO2l{VIjEiGGU2_A|M|AEaiF(I*LYV7XaiF&SdRP$XE(^?Ig?+j;9gw}RJ zl1WV|ggM@*PBqx>jYj@)_P^%+f6u-UV-}#D?9%5$Z1n!WB*N(z7GGUFYlH3OxsmLP zn~CE0dEXPgF#YC`{Bj5{<*~)!?Op}4_@_jG!x#bH225nJ%(v+e3w1~MB7aY8$-fhu z3TT}S#s`zRxKS>yC~%|Xw7~C0i(LGH z{HkQtwSB99(OL@SH~k=o$Yu@_$Wv=Ia9hC_mJygn!j-4ZC_{!Pc&V`D_fKcz0N1p+ zqDw*#-oV2wbrpQi8|xaogA<7VkFWReXY23(|Ft@3m1?zCP@7s6GXzCZd)KBFs|XUc zS6fPJBxckuwbdS>R#hcJ?V7P;)Nbtk{-u3i@6UC8uFviJ`v*?WIU?u0UXSPf{=hv; z<}j+dq#S!;=RkIK^51djz29-@aO)+eo71y-xOxeU{%iyn4cN)lD)V{Tjua z6JER9+fDo&*)2CsCf0vUx6`l^efH(X8&;KI6T({mVn?K}6eBl9DULq1O!{!cpCB2K z-l%oBoonNu6huS6%}Za_4&~c?79|Mp>Buz5s(YhX3fn8yc|26ZvGu5GDN}J$nTwM& z39KVjXQ5$YPqGSc{9_gV2N~sfLUpkg6mPOd>Nj=Cfqn9=c))gRZB@x*PvE01V%9b zpg}6g>Y9s#Yg4Mn8W``m6gt0q6i;&Lvi+w;xZaNO1;|cKe4#kKW0vrhL^Bd!5JXdQ zR3D8?7yBiCbD3X%lIAwC-lF49$eeh?a2WYIiRqu?T~K3;Zqg8d;_ZpuJ+LvtN}k|*Vs?Da zjQVX-RnMj_X(17kooi>r%VuXJl4SjJU%@&!ebw&que(!{Q?=uP=`&cl8gkKu_9M0W z!P?VEuoN~%RrKz0+{3xAKRi# zvMNgtDd+Q;DfTdyP{D<2q(R3sG)xzkdOC*bUUo+ICAl>m(uALy=0QEan5ASI^l?m$f`29sTQskn-N6 zt^2+m<|i4tk(kpgMXFjZQ)!om&gSMN@GH_ie>ZJ1WaJ&Nmd2c5F_0y~%GYyN1PcC( z>;xpYIVu27*`~eDnBGs1Tg+*gvzET$Xpi?t%}a0FVvm4tfy&{bKLNS4zctf#f zjN;GR^z`G_JWZD;^rMf++lNMk&C z9_YJ-7hO#HrO?ucCar{^=n(824jpY)??|D3cjThbhZ!l20`SXYH@Snt&1zr|Rjvo_ zCAmP(oIGjkE|b-H?MdR$fSLFg*gBfAE<8Abny$|C`X&7p3k~5mLZ-5Q5As5yqRI~}+P5@ts zy+{$sjpBWFJCp57%>irU=bjqL#*EYLxeiV)7~gA{*HlTlxc9YT*t=Wdq0ND(tyb69 zKw=@kGV;NF^(s8dIb{N0_CHXIgpnqJKBuKkBh2O_E~xC|55yt$OWW5)&ObdL$?qH7 zc9$&M_Jcw29T4V3v)*8iM@tjiFU@Pq+&$~tB7$lsAW4hq^!!v|ZCicV4$ltBL+)!v z1sy&T$S?@kLMdyRuu%g;dgaQlcW@G$(wHsKt~>d#o^92>BUS%0Ge?K?*37}kL7smv z%ZTH1@TQHYApBXTS!i-|+cm)wX~AVIm_Em?S%B47dBpn&Eeqr4`M?s+Z5FiQODuo5 zn~^oQGI!29C~5J$*NpS3IN5Wxw59uI_zsuh4rz&%(OIJBd^e-D_PI8av~Q}$Ltv)a z6H07Y5!wD`LvQkl4Ks)drr!hCzo!D|zZ>MZ9XUu`Mh|AV7j8aFW}U`Zj{)_PwqJSE z?O+;oJEpU1ru~C$9)}+=r^zciXb@Gs`Le%|V@2d@E5G|q+gMC*oJleVm3jMBt4Di4 z-YQeR0G2@)3y>1t*-8OOtsXwvPlsqRbzxARWIU+qB%-}<`X@ouF^uA_-KA?Sr1e;D zV^%v`c(X!4Wmd6&oh1^RH?>7o2lyhkQ&ub|2lHnG!6L;&!&NrD=&EO zsm3Th7j~b47zvk~kSCM(dU+hytOO(d%6cY9Qhe2-;tPGEk8h=)K^za7K`uY8?)><1 zD2Umoa%SE82(}P*43v8wn{csT{&g0q235|t>Z3cc!0IF6{fyM_s@tC~l$s^7~-@f|;0M zn)))NaolUeNgXllwbKd)ikIM^(aJt1a``SX{8v*kJL~|z9Zop0h-0`pO*xD81>0PR z#q{xX&`SMSpSv7tj+>E1&Onhg7t~G?dQOMkqN%TlosuEzlqnZ2cEmL%bgYAnEDpsLW&vqZ=e%7OtO| z?!|AagMZjUtwOLgnRmC`Me2A$z43+JK7!AAZ+mcaxfveYTyDx<_@SY}H+U2NobxtO zq&RK;ecgMT-MW1tB7al)ir2~M+TCx4$sImtHDhDnX)6WYEkfR$0cA-gBjHxxOx(k* z2H)w{E7Z24GyG_}g_VCMhf~>9Ni|;`1g^``gY!~-;a$rd0Py8&I~`4TdVbCrPPj_F zbQouR7Rr3<>B|~&b|!Fgj_qQX=1s+pEWvj6_z7dZ@;nzBqEsQ){%6G)MA#zVFwZ(V zNW(K|dh*O`Rk9KSrpxQ>J|U5*>ZfWyD-|NzR%AV}DPegW?4oo@CKoAPuwKxSI+_mY z>cy!!MDOj_RJVM-ng9$a(p|dh8pr)Z3e|i7rrW;iD3TFfVtLuB!*)+D6a1kgku`iE z=8&XPU}cL@1!Tfz?2n{_f9wrOEFNO-%(rb2(qW3p3poAyH+OvkYGe>6BIU;sc^g&! zTAlHj1r#{Y?rz7LuU~a!NZN-7J(+x6zOTZS#3Gb_6uDjYocK2Ksg?Fvx4=Jm+D}ST zR@ae@|CTleaanWmtk(6Nbj=ilGA}+piT5P^E<_kSBz5Jn9aFNw+cVX;lj-uY+k(YRgjs%@edDb78Nmg_- zs8dxulfA4WeYKAjv{f#!ao9B^yM1QdD}VD%b?a^)#y7ieLh}bKgl1GcTQA4sn~7DRs|S{r)Sr zl8w%tpiSBfWIFeYf0i%74n~dqZroqrhJ#6)spcgz83*%I2PHbpP;_NwdpJ-lA;;;? zOsZkq;ufX?Alkjs>pR~Ej_o!BmVUeaYRBgs&cwj=_8u6%y3FeSw0mP=A8uEY8+Grj z(FUJYR-HcCsvj+;L(?UcPmK-DWF5}68u}_lBc3DWQtA;@qz*9S@Mqbd&vsb!;kg1iz6lQz>EHuMkHLz-uM1^%TqT|-1 z8!QxqK^uLit6Q4q*InvSa^V5N}PCXu{$DCe9TsStk1Y5Ik<1Ky~zC9)98?p`h2v2ut#-+6emGa z4bmsyh7>%M7&E_qax_9;6f8VGkGI2Cg`o`_6f;#;u+%?jfU~XS+p9KAcglJS1W6=i z!pFV~58qCV?F0szKwnIU9;St?1Vrmk+j~OQ@2Q{m$LryDH+-;|%Bn>%9bnjlIa~sF ze3*>wn{77p;vZE-X@II{s}`X{<1Kyi0l!Xvt&aUVY;^6CKDDaz0fjISA_^)c2OOzN zHX5Kxfn#^mh5DVM|00X@*x13sXFFn^4f(i6HC{to7UXWR>*19dR9I8q+`k`l5K`-u z!>|tu`YQ4Ip@=|?aLF4^?LeB2wGTgru!kk`Ztpg{D-Yz*0-yyuRTrZq&9C~#Wfp#` z_!$ESch7}qNX`x1yr|nIqUR{!$g8vwccW71I}j57kf$w{5gB$AK-pV$APYFTC1$^c z|Pc%dCO@QmZyr@%1}`?kg&L0E#Pid*4zd64+H=R)^YuP83|ccgVH@JWZF#`RaiKu8}G2$SiH@TGVBP-Vet`|aq8^kK$<>x`{_XKK(`XEYf z%k_IgxpmB7#0={Lhb7F2B3~4A=t+xngT$+v-PUgw-_O6n+b>mxZt6Zr>de(~M)WvQ zkGi)?$@*MXk2F2FJ;=I9zv zoT8aS^mju{5a9Fja9V^+Pdr1Z$XSK4N^RiqUBkx#lcs3=)Oz|3PWn5R>IeC)ixd~W z=Dj@IQ_md+nmB@1&!cK(;LryS_yz+|cG%i$8cA^7-Pmc>T%xiz` z1EPVw_mIHd&Js}0-=q5efzh4W)`VjQ*M4s0l%+elMB->Fax1@GTL-t+j(VC2KBuo% z^ZLnrV?h8A5;o~qb~jRZbAqO^0z1O1y(l(fuX`^xBE@>cEo$q+l?f|5?l}P|T@~`%HDVAXT9T=a&K+ zz?zFB6(d`x#_i7Gt07z28Fkuq{A%pJ3(ZmdM`aVWN!(Q9rrqxRUW^=XSTrJ#nNy@; zy_T*NO)N0)*sq+1>HF7{fO=diS@Cv^L~rd9V`V_ZwIAomh>fC0IZceMKs#Zvw>=={ zbwtFT)-CVx93W$ukwFKD?FLKl~3P#(S5DV5~g@08eFp0s?{KaldYzqgvE z7Swr4Sp=>|4RGho!rkr>7`wt&9+V%K4(pm{BD@@5K&D0)~dhJ6OhJWyttX zn)_tJ-97=Mw>7KolP1Y#wuc+IsoMW^wFaa7P7eBC(@p~?-MHg{bxPS^?|G9pnN4pe zLMV4JkNI&km=BO{u$ki!-Dl+nHSGRyTITz(O8DCu1Zk1{@G-I))Znc3I&N+&g!vw-gWe12S(^&sI219@2EYEAyZ}9pB=~2*>6BF zUED%%YHarVKQg>evzw{}xn$bRXw!32ygD*=mcuW%#&F#H28rhNyTkfT8CBlhhJOg! zbQ6o_v#44{mdu{GWbdXQwuWNK*>r%HfV>=$VocjnJAD~X8Kw&f4jlKz$+yqXy=qq& z+=o6U+FonZY(3Cym}uyapD}LZg`zmw-z(Yp-HIE;A0L|IF_EF z^*wVf7!Es^K><>{8&3n?s?-zGRj~F&Uk_TIv<6QghFmy&=SAxY2;hSyNkGnaRtTMQ zK*o2fI6NWt#dX@&??{2;&rK}qV&LN00T#^jhIY-@~LTbVL% zlvzzXjdS1a2Q5W{y|+rKSW(YXgz%gU=Q^p6Ho7g@i6?7iestdB%gVR?AAb+Wiy;Av ztd+B}ANHNrgU-E5s&^x(NM&BYKqZaAeYOqajJa=ReZ<5Saxcrekc>SY%SLFe9ha^B zH2-vqz_|n#Wgi`~Vk9awSzY zgJ8ClU!UQgyz4shlEZ$|lsS2D?=CP7n(2a->9T7X(Ha+?Ha6xTpoIhB6z=i( zX+$++M@(BoL7dKoGNnd%jm%aEl@iU|M!43u{&XxCegkv~O7!13Z)yQdZ zh`uEp%V?P!5Vl=K67VGYeLg?F4p}`s@T;r<(iQ+aWmKGIUFQ#%m(|qx6I>VwNK3~= z#wo_jje*G|@t?LnJYzc0Lc2~5v&^LRshM2{5z2xC*l4pt8)bDHx4_dt=Uy(e zX{=;t_h*#giIZK_`9!3L*-1IVi_vZ0^;y7M!Msg7N0}%!?%-(!EBFvLfyUa^;DAwe zNxM-8XXyd6v;nO8?gyVgUO>$IFL=?+f-^VBR z8F=0MNaS36KZF+W{uf0vS=U#kiOauCf+fh5-=YHFFH<~rCOM*huVD+zqyYBtxJ|D` zJFZJ0QvJ=z%6vDg&m^fPX9R9xiv#}-WpzsGNSW-duHWT1q)he$1D2^hK^~;E>>`i# zN6L#6{_CK*yhK_Yk}Rs9D)E2EvEd-Gq;gH(c?4O@@kJ2u8RE9!LLBd=L;B`}n+v)$ z?pwwC_?Y~)iIil(pt5+UX0Cg_+=N11g`CMqdceF`n|%5c0yg!Yb_={@>aYx(`9j|2 z8{0P>Aolg6N;DFNWR?UTv*Q;IhksbnI$BEO9B~Mb*Ak>sAXfRcu^%SP2w=nG0~yjc zodb~Qw{f=j1nVh;;d6uE!v9FAuU6p?B#vz+&w*D@teRk?5c;1BF1{s-kci+R0J4uZ z5>MD@moW(&!t?s+oZ63UIn_viNAdjtV)vg5{wIGna;xsMr(8_9^KCDuS#WWpa8{)E z=Nhq3p)N5y z`L5iz3JnGVqlk++HE6@8Eb)f-zZud${Vp116X|&|Jpeb0Z-%!4{Hrb3WnqK}Gx$u% zH7p&^?i?lUQSwuBLi6XIJ%w-hj5_dxwnz?*;Z=Ipn-(=+4!aBxI!1);4`U@qN;vyKyoF?g$q{mpP*? z(+}P2Dt(xr&vp;+L{+c)mbx&M!%JHs`Mc#95Uk$&q(MDeKYeK`Z;+RLf7G~g&nkb1 zowk5$saC=caCu)xcPPeBvCBMWD1?oP)1`6JwMru(FhkrJoY5lT7$g5WiM9UU&Rnwk zzn!^%D|2za|EbIk+s(oN+oJ9Y9^K9la^42Z2W0Gx9EX(5PR`=hV|$WfZ4QJc zX?=~E_*Y_d6^R^;PO;0g*S%za4rapa(HP?)og2+xN%XPfJ4&GW&q(;y#4=yjWBfl| zrcdK@m5^W)p*69fpUfn@WI{?mjhZ)p zne>w$x27;iM*_86Wwb*(<0Yg9fX=(j;_QXj`ptC@tSiAE5^pABcfq5kg4(%70;I{D zDm{sg&gYRf9ERTYCSa*^swIgj7jJe*6n6g;Yv2BXZjfZA#ek|+EW#KAoTbFKl%H8z zc@I^ocl*_}*YBeliSu^VIL5`I-6rzqq(Y}VB?%X5pNjH=6C(tn>n89P3Eb0DHRKi6 z_s@Vy4ib{MH+< zlJpV&mY%9wRU}RC%jCmf&U*gbQEd9Z6ZcETjR_%48g_8Rf0V~+`WNx=7W$XrS=C(a zMCT$;s=tM$2)$4Nk!>dRkll%U(C)1a8=U6j03+x!n&gp6FOct>`WgC*9`0 zY%E@nE4w!Jm6BK2|YL8bbnDKp6QeFp8Xm5tms?P$Vu)J@M}m32o

# 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 z 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 z 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�WhiH>CK~D=#)3aA81#MfSmoKeP6~iJdW~O-t>|*)gJsDp3=||CfZkP zlrYWyNFk|es}O+m1cG_)%UyQ3NYY6fU)UQ|DbRm;7Htfl-n~2) zkLKU1Zfw`O^&@6i7gr>?#a#@k0O1$AbZQT91Xu`kE*;(d#0{CF50b>_T-JbPXz-&0 z&Cej>D7;Z=Nw+*GlHWJ~o!8q^&kl4$pbCxkV>zhBmp1@%pzHXjszFUJFF8}zBzmJb zjde?bUQJ#(#t6kqk9uC+ANwVNCA>txfA{2K&C_}yn+G%yj~z`c=`yJWle7w7p^1QE z|Jdn2-kQ+?FbFTTCZ3FTo-OZ~T;03nz4BkJl;Bz!L}9dt(9@5?`meM2lP8>PrfUX-*G*ybOwK|K%e ze{l2i>Ffs&<%PQdf&0Y#B!YW?fCc&0y=L8(gTnqmdm(@u8H>$&z;m&h|4lyP9 z-=DJISu@N$=fM!fe3$BO&tVfx5;(y|H3bK%2X|s-{=y;*dDJGEh(zVmE&Tdy3~;5} zoQ_fUFvus?Lxr3xhn(4+;E379VH&<3Umo4@U7&k+bKHD*(nyJ;oc!DD(%0no^-VPh z@F*Oo^^@YW<%+Anu+W$+4bOG|%n8rb1v&~Q;QZl4SWj8g6kg9KrJmedLMUNGJx;kC zkNla*&7y3^d$?7+|Hhbw`Ggi|XN_fZu@Gk5v0k?x34DJR^Z^J~G*q7pIf4qF@^B>= zO2pwK(ADRA`eiZ68}tRvt{B#F_RsvnYxzFy+~Mpq8ZAfM^ZRauT`_cIm4|@1P~T+D zdFH47HP~fKkWHs)4UrXvrJVR$bIUe(R8NJ+q#I`yD#El+dqKZHpNz#eUCPqX?i~Z^ zPFWW73R2Vd&5=Jn{ZYUs24fK)I@T@+DhueH%d`APmUq38<@Gl6oIXDn^UOwr6F#G(()}O*-*Quo-T z1WSfVg5>bB{9C$;)?TUgf0cQ0mZLIy4M5wA7@+&&&`RZhwL)M>jnJuwXkXmvcX$xhYLg)v7hS2yF2#Th$a#!+h zuisR2KO$h-yvy|@8$z$I1;u`)eZtggr!0UHhk0UYe*ccZ{+Nuq+wL-{0qyYgMx@EfhO?k+>Yu!b1F9*wqJ4gffT{r+G51B6X#sB#I-h>xV30jr*jol{aLwg^jA zF9-I>Ea3sLkf6gLWE-r5hY@>&l4ih>cgD1#AP5_oKs@QzG)QoR$s8upSpNLFHp3AU zwKIID){E}FQ_arzRHz|iB={*o_kg9T+G9Oex4y3r2Y6KVE-4`;PBa5>!xLOK!4^tA z@{<5BACa+KQbl=|)Z%JQO_jEtq3}z6qDC~$&+w{P5D%JIL|wB^n2MuPlw{;epAB6^ zBCSLXFCQ0rf(Y`gfMnZG%60~s_0|ltfISIGrSUAn-|p{S9ThYjk*NOib2eRZsymPq z761ONL;hDUr$e8F@U{@8D*`Z8$2-2W3MQlp)&3Y7ce{_tZLw)0o515x<0G&zKlw?8j0S-UMJY9OGpy`RyWRv!E3v zd?iLyC@&V^)Gw6zq&qm{P|QJRtR36WSqVWgD&UpC(9du49T}j_jmn@OeR1*B9x;blc#zk4eYk*N!*I69$w@Pf_{0_zv9C2@-Ize5LN z?xoCR)4s`W;hun~-^=7zaG-HZJlFpa`?$Q_iMMM$BZY{5(d$>FwAG$QeqxgdaH5%- zX#7+y*RV5EW{GyFb@&KX>}60uSY1h?)s(S}jt?&Ef4!C4Xh0>0JNfG3-(4mC^?q_j~qT)KE+8@ScMUAo(d!qd>#TCUgIbKfQOu zPFWyokxO`CYpc2mK9CeAsnT#}&q&miB}cc!x96jLH5j=J11GM?GY%Wwx@aFYXlXb4 zmB0v$l2ZDE3^GsKoWJgssZAWXwWdLuDD`n3hiuY*(FG51^+}T@PQ2v~nm*#&)qVD? zc4&DY-FMh4Ioven*s4M2aJ+wnpS~PR*ojTxNx5p%Z&3MCrD&EiT>1-j=+ING+?b7ZgqlG~2aRjtZD4=(1_8s+{mF{Uvp8&D}d#&K@%6NHFwuc`#NCtg~TWuMdZ}-lk<~_6_)>n&k>no z_=kV8UK+KegSTNfgT*2&C^vr>sG zI5?~wDgiG8^piERHR5G@D(qjD`~c}xBVSV|;cz02-p0c{KMhY6Hq4!KREAVV2rnV} znE~(3Njd&@V5c-$UV1a8GblYwO2{lzwLtUXZ!BtwtU0ZBCq>~8Q_sF^mjeOCfPCN| zkPZ}M1)qUb673X;)dx~&DE3)V(*Ei_X^HPE%O(7G^&+p1$2y&c$Fk z#yW;u&m#aZA>H}u@QkSW6CQ0_a6^nCztmo*NP|3c1+8p_oKMv8!HoW}OB3SulgLbq zsIGJp-nhz)i!~=zw>f2BqgO9k6BAKZ3qca+(=*$d9AMC~d^vrk%Y0kP%nPNbA|kv$ zEtQkDTpRwhv`|BFF6LSWO#@@4lvbohpZA> z(?z=_va?M&{Gei~wi46g5X6M+XOnvVEuzW^4h4^29fe_d^ z5GCG!MHC-ReT(DZ>I-QGDEz{zoc65Y@CVL0;0PIPH(!zDf9ENqv14*Nk!_45f+GU? zv2@QKnveEu(7V8?O&V8d-5r%heIFe;0_(xbaEgQ?^sSuC+l$L}LR_!A}OMhGFDOvttTL24v zHreqA^DyTtgJp>r-_DDby_m$E6&jX?3t5tQyTYKE9;mti=Xcf zuO!tBi9ma)HCFMgPHj6RqUGu23y35QXO8}LeV1P&slbeVJ*ocf*_W%s?=?$`meHh|n3U$`I`O7~*@8H2FJSbR^m%{Vg^tT- zl>i_2VED=Mc$kv7R-!!2p<(^CqL6ZLO>xvN7RkvtxUcV@kj45RtWyk7uzF@kD2#rK z!&`mqYtEBT`i?>-9>DkP1Nfc-{9j0eZcW_0_D7f)PH>n(Pt4my-|Oj!L8!*1-s_!V z@ypkEpfCx&7MYwZ=1L*04}@OkmVl?p2@LucNx+qyQss8AjMn)F(R1tQ$Nk7RSU;mU z1SA61%`@1)jCae2bm*f2`vj9rp%Btc{dP7=jZbAJw1z~%;*=Ly74VW35sc3n?qg#7 zBy+cFoP&wa)j5#;mWXXveZ?A0m*g>jh?gMdeqi16HZ67H0D95G_D(+bQx72XnN`-~ z_@qM3ZO+s>{wFtGpR^4@CmDhRgsh@_F}?L$!jxp&#)K;mCrXyv%!j_!oND@r#&&lm z{E-}5)yk@#g7=q;VQR!;=+ouC7pmb;q;Bg57&x$OR+*tZ5;I^1vj$aT=lV&@)M7N^ zIiiWJI(GHpO)VyTFMO%>=tj(+T-_sNpP%iHQn@1=6?+k<`vd_LmJFkWb8}d%ADnvc z*HGlXd=dm$sU9Qs{8vi@Ppgq(2I7*AucBGqm$0AP-uhjcaU}?IX(02fT}pPYI4-~9 zMaPalUqNtj^CjZ?`N4D$k_QK$+{&3WKN!k4?whNg`0RyhyZ(j6*mxlS=DCV6Il#N` z1z(oQ%k+#(8h`$wz>4RcG=@%hZH&nwu&nT=4{6d^nwmbZ*svRoF|;6)rB75vg=2BPstmLfle!6{f@e5Lhg-X8AN9xJ%0Q+UVFEN?q?7(4@+en1$sHZ##6213&m z)hosmR0jFG*#eZ*UFPqKGJj#QeRCKT|Dw(%Vsb<|a8)Xi+i3iTj(mT<{GUk= z*$b?w*5%<|cp6Q9HP^n?E4*UTyO2GTdI!b9@`rA!A4R_#9HcHczzn->fD}Ry$JWuf zVZ)tKyC_YlCENhR_0m;I*`bd(GE?-pyC{qnRKe)rq_?kaw0iF(58*A`J*2@vde;xV z5Vt3nBV!cUJtFtW<~CPGAEtxzRkW%Nq0qDog5nCH9lZ@6h7lxyy4WNJy)94ZH0ayv z(5sX*nuzUh$cLe<*x4VKOMjy?S_J8@5#Hb#6_FOAWEtjxiF*dEIoMtp!)acr8ApSL z7N|QS4&+{~V!5Qy1!VgmeJXUOUo9>YGCv0Ca+^y^T^m&$jyhU&gz0niPf0m5NJ7p^ z_Zv{UG7zvl7lsL>gv`8vFAH z%fR?760Qegh0xK9PR{vt8@%A_ms8?@Tng+8xUCQ#42coF8A*C%=SkBWhhv2@vIi^H zq&=fVgwto2s#s2`^p;4VBIvS>Z2*#A`RJ&}qOKc z$w`e6SIZMdCdJ3OJ%U~V_Ol6Lui{1H{$F50Kj4Ny0)g@G1VYwjpr9Rzjj#B60^b_2 z!L>cuDt7k!0}Jv-1Y=Nb*~Te_Zb(w;;!RX&z+Ns;6iU80qru|+w727HBKmGKF^X5b zZe}($x#|&|V@yRoIR2zu#R-pQUq;BzvPZ-d=dKxHL*f?tWCBJbk6#Ev%uugY7>l)^ z_zmvU(C4ql5?GtHy<@DKwR#6RQ%`{baQM@8fIv0^I&Q31C~~CsEw2)$Z(0W0Y&>kH zw@5tGKBTybU6o!7rnDZ-rWwtk8E`%8D%a2kfZsw=M-05!h0r69ahxfm4siG8XK3^P z9N<9jxzWVt@9)55{&YDAMPI(b=35}bpP4o=*ibz5^Q=g_x?qaz`+RaVdwhRQW}4D= z`FfHWMJ&A<$52LT1ubM@uoa-)zp_|DCQOQm$HnHHpgCam7d$^36DUrc z`$6uyh)91Xnk25B;6f+4`jbD5>6Q$uO`UgekibZR99|GAt0^@uEe8oM2gvNA zsZGq$AW}qt?31p!{Z;(`ZDudA`~V%lfl`Vk1^N6;+(Qq7s%x0EdcFqr<~?G@55Lf} z7hh{g=?$q~?7TxUCH^aO@*Ynrzq1Ds+2hBadH{pa4upmxXxy#i?bMmvJcG<2_P>57 zaG(?W<>cSGv|LxH0d4&1>W)fTy%R##Z zI2$t!ZU#&3TkeH`p!7(Z>RJj zf-t(bTf+_hXQ+|9UdyIHPM(He32t1 zzfoce5v1h`r&Y|DPWUFlM5*{}k zUJ1U~$fL6(`^c)S{DFAFJ7~IWFh`(D6a98f{iy5LUsx$h#_DOwk}_%)u!O+%4BC3; z2hl)Bu{|!~%C-KQbJJAQGec|PPT|GR<^nXX!2=ie>cTPteg)e332dV<)S(?TH|DLr zZc6#F-oprA^+2uUkSPK^2dF@Zg@RJ$0Zvov95YVNV|6`w@E1*Xy_H4!(66fS`*`l(Y{3wnYDh zCE`hc8+1e{b%WN z)*CVD-*xO#IV~-{_i0bBh+kixsp9m17@@yZrbFaRYR;-qXVBe%-B>C>%Nb2jd=W$M8v(v!7FoFJ|E*%rNp0goK*0dN(}t;YD=l^b*epD9^mwkm(A2% zoFCV)Jll9P4^fgpwuAJWT<6>wG+FgW*G@)z;>s+8xEC-hZRy!WhR;OyEPCaVl}NdB z+kr@urrTELY3(9=95fdv3$!A>xv=EFSK;q8!>?AtrUfJGwd(C<*yjIvYy51?W!UfB zYXXLHqQ7R@Dm_K=1vWT|K8%Seij_Q=qR_E@@2t9?IM*e-7>W%BQJ|94$7jy!XYK^7 zI_>p4syBQZ!=8-YKOO5Y|57tWi>&n?m89~6A;ZMKGk%5IDDOXvQ|C;{7GAlaIh!av z`3vjQ9TXjr^MHL5!38||CDbMT{ujc>@Qt>{S;}gQ49Kt6)3`V(xY6;_LBK?_>RIcP z-Qc3F6aQoCa)c5OwC=*g0ws0kQllBYYri+ZQk<&v<;880^P*B`Op1SqZ+n=cx=&FQ z5u+$psvbk`!LOp)*c9?AYgf!WqPI$1uT`k2gk6qDfI5lQ59<=;kd`vBXOD_38s^sI zJxwHtuPWsB0QKka{@g*M4Fek(^DqDd=ev?xEHjH}FuM@*YWS`;1{f zkNxP#`mRVSU+@+4Wy+Axchwcy$N^3UdgE5honTUQ7f(#}j-uoo>zZUi^U#r6dvU*Z z_KN!e{!ixT!5ZXy7Y-7_7`P2b2a?kZd-6~a74iZ0wQzH5;E2;|g0=G4xOsltyLNrn z+8xibDiAhJTf7emkPs}7-rYX-vF2PtgtMBw5}MpGZO^XuzA#-G0Ns0 z^;;j14~tTYV()QQKzE^xtn8bXQRcI;+n#)L{j91m8JICWO4>R2>_xy70TavfC8-BF z*`})6TzTJLB$uWj#?jK0{FEp7Bjmru>fBK?yxr~^sDBdgpWHNBddDTo`P+AMN0{$Iq-Y&&zY4O_lU z2^?tdy`HYqds%h}ai{@ve{-}-O($|^3%1T(=W#kq%4BdQRxHhAWs5o5^N#i$nH z7Pzr?*s4dPlMRVve39CE3IXJyJ_VLPNS*e=GST||#7O@8C3>tkS%_lmwZ1S>(p&q% zPU`ZG?uj<0w8IB$A327b67yjyaA2Y}s7-cwaZDU=KJZsTtCU zSXm?%{J1A8hA)bhQ0l^|n80{9Nn7va3dj-pH_0J6)r?!HR@xwIg*FIId97x#9L(qq zlxwg55Ay%_)!`-;8S<=p*-A4xQy(z5WLKn7dTNk=;vCcyJTT4nj7`f*eq8Vd&`$y% zgt(2192sC4c43+@=j&7q&|I1exCj_4;j%OKzIMX8muGIsNUQ0$rD?P`==};VBPRTV z25w-TdG5=@!LA&kZ4rK^(2tYPI7BqX)qjt?Wg@d%jG^L7x|0=g=FrPbwUji3JKZLr0xNbV@nhquXI8saTU$73z4 zWmc6AHa5NyIh*-*oTEMIeJp_YJ{!Qj==_BhAi9Y4fgMj06KKfs#-#dR4g7g7jVefm z=G_0Qp!KC_n1#Kp=oEuF!B8pUw$N&~XIC-2;O|MsQb%JCbO55dK>mW_T_B3_q`j>$ z$J$tFl=aKvx0@;4<@R0Y+S@{gdZoIB<>xW%Tc2FFm(=+A{hpKNU#VXK zBZVaRzOG(t*S*3WF21GAyiv;JY0jDThZxNa4)wpoHO9UMSr&MWg$W-DM^Ym> z`Cr=)iIfvO{<|F)@pn5eVBRJmgXJ16TwM!{Sb)x6Y!Sc4C~gcUzLK6x`E?HTAlq%$ zH!@bi{Gc^TN%uy7{scW52Jr3y6Zyu@v|!Y;xNk*1UL# zM=rfl;zDjAkc4`04LHci=_)`Ur6X(9i~}{)ybUVmoaJi1J~ycl8C>v3jco1O>ttH^ zyTvL30(&f`_;L6skRr?a#@9TQvFJgh#qQ|st;p`kq8@6i(lMkcU= ze#dj2w>L~}6>kf*aB`jpjBis)J^cxoSM)ai2b zZcus|Vl&P!oVg3DPHexm#cMp{duq&t;qwe^#YZj(PSoKKVr%ehV#}I@$j-s%d;``# zv|?|vMZR#5Z;+r&*73P~l0AD*J4Uo@V=(UDXFv^~U$IkM@l(uQ62Kj@u#SXi0gSz> zgOiKb=l?VKb8l;T6#-Yd??c=}T*SPQ<-QOMEOtxA`#Z}A$78wp)wT>OuZe*(3s*c$ z`u+1)!&I*fdY70!bJGI^65Jbh6(MuVH}C6nS?lybZN>M~ne-Z^Ma7}axAayVP~mS+ zFV@ZhG8M}Wnd-Kz6&8_7f!O3DbChDkOy>Nf*2(aO-uO7ipAYSiAhaly+6YH4S=O^} z-0glph?fF?H4canOBM}OHdqPS;AEq#S?5@(Gx?x)aIY>by# z^jb|Y(eJ_f>6JNW_XI2R3&u&NMF@A&utT$h$!uy9OF~|4WHKm!cD%4zpxUpg?b{-< z9trY?7H|ot$zi^vnE71T`|Kl%DD(wP+gl{yl`y;ZyZ{GwrwzJ~^qpH?P3&B+K(D~1 zB;+XDSkYuj!|VZX-fPw`nd0;Ak4UN1i&Huyp3P!N_wA(6cM9wzxSB^i_FUo@I zu8zv1M_irx5aixwyJZX!1@t>r6%t9me?HYHl-*M&&W8mDzc-#MwY)ckN@jm%E4a_j z4v-H7nM(`qG`6XquRo)UuJ4GZqwy^gpHRV4Ok)tJ4R~}Cbhp*o<%-&)&N>@ZeW4wf z&+ho};1yz~x~x|E7`9F4Nw1pS`u5J5Z+#A;s;|aOuJ1^PL}n{8H?$2XGhn&q8}6z z(PxFZt-~CSH9J;)5pVoG)l78)Q%qfBSZ%}8?LC|x%U`ks{%k9ly}O|leVjAu!{T4RZ0*I)9kRj47?`BVL~Nabno_m#XhcQ&uD_nOK4 zWPzlVp}?|t|5DMP)krTIR%}>6V>Y{Q1+^AlsFHnHs#jq)cL=uv%M{}=a&)rm=FtIx z-gVy}jov=i$>4r}XSvKUr@ZOM6M$Z2NR;-}ss}Dof&{J`_vX5b z*&aA9DyFd}6iN{i`eJSCd5tY|Ilo3Ho0Z9d0XkOsr$2NoEbg68TMf>tFitaaf(`-> z;{XrT@J^Z9Us!q0RnoKaubG*1>x7rZBKO@YJ`9Fa_@D$gG zO6UE&hCQ<=-*B+(qH*EJ0T4R72{sq``IoZ~5V7-6reFBo3Zc90DuSQWUzY-MD|3NZBHx*RSZ zn(SRT7KDkj{DY(^EwZ{jk_c-UoIL*%hx1_7FqW9_|W`uEjxyRvxVKa!LJe z1sR@=U3hdfC2rd(CO%^fdi??6PcU1#HA{gLraOt}_K|FZ)OM6-HA6bqaMh)gR%!nF zl<@k9IW%4c>B7O{AxKC6_}syIWM~_a6pK5#Wt3$V znS#gJ`svC7D~ojw)c_vF8xQMP9!SD(7UN%%8dvMccqNSox76)RZeUP%woT{p)j~Kq zaD115zg+^u80P;DW6V0?_LEHq{2?LsMxQ=sM6+r9H^E`}wTDa`Y+SV%a&|nra088^ z{JY{|rF72i7DAM&dFhH*P3!I^DcEuV^c6rfn4gWQ0?gmo!aP>_au;}P!%*^5YR3Lr zmETD<7i|EsBw5(RuoM)9Rp>+M1~^0(7MAys{ha@4$S! zr5eS3zIr6U&oL2b{$Ko@5}BZ-Tw!!KsRGl-8#u#IE&%jEjJt(8-j}`xO0h;e{}sZu zhpy&*(Jx4|ZSk7Ac|tk>=C4tH;ivk6USbW!N|Ji80VUb5i145d%vLG*RWu%tl!_E~ z#XKTprzrb1dXO&SZy9_!0I;Ma;F~>%CduVx_@0$}3_SHyuo6Z(8`IN#1es`AGw7!i z)A4Sv$&<}$%x306AC<`Bq1UejNOo;`FA*aE-+>8EgZUA8!T{GTMBJS?MY) z;Lm?yNka|evAHZ?_HEPL3l8Kd;h{;RdbsS+HS;Yv?4!+JSc-4;J_9=8^?4-BX8~r& zx&D~XILP@cP&GX-tUWV@gzP6L$8*{wr1LNhDGIdHwwZFPkR}uP0X2qHaWkmu-`CAX zqeD;R&7C#gI6FP73Jng+WX6}J3VkiIPw%`E3sbC>3`=8fCjW4G=|CA?byK8#R|j4h zj&2W{{zPdF^L3ZuUAY}!)NpVix~^URG_pf^TG0JGXwoCeTlng0^gxV-y?O(S^s!^*1E$iBH zOT+~Bp19No^O!AGMFtNbHY$QWT2D@dYY{|X9+e~kh~&1vp#H(pI2Z|fe%TeD@q7T4 zbJg51>h-Ht#v}aBrs^#gl{+s#wmSrCj2bK<3#TR#y?FwLH%4?kz=$r*J*Q0(Q1|Tf z(*drUH${X>WGT^CVh;X)bnd7BSLZGemO?zVKl_Ldxi+6Mv>aIZ41=D2wO$TP*=#Vl z;`Im@VRQG(V}hkCL{}ISYt-j>ultv;7@&DU(Occi5;};vtCo#t-Y_4p)cser%#YdX zTLG+Q&e;fE{Cj!zG-+QdIEo{8n9i{swN0yu&%^i087FQaRXSDsy@FZdl zqu0fNpu`XTp!XZdQ50p0?^+e_GKG^^Dl!W%h6!Ue05RmI8!C5$6YHr6JTYfE9x9H6 z53}Ib5jwy~M%xOaX$r6PpbTH$d<2pwzztACgcBRco9IRPqN@)I`BkPFf0j>c z9iL4?YI#1r>4IJ)T;oXdfvu5@>w0FonD#6&NSa4ar(z3s^j%CW-uNh}6;255FWLI= zamN1GXRD<12AqU-lWv0_1meD8}Vm*S7eZZ~iZChL!q- z37xuRAP4${O#||S;sa94&HMi<^1yzi~5qqS!y2mI0g4*t@AEAiYB-_lw*> zAL1iXkq5%*EZCOw`?YE^-qn zQ``$`^UTcuR9>}R5fiL>s7e_~Gw+w|7+ORSOwzzWZuG#V_4Hh)-{BFlq7yFC(A^?O z?$(O~+n-Gh4Qu62LzppFr@S>WlSXgA=~ON3YwQ!MT%YuN1vZI#Ld5S>>kr#tOsjY6 zWsNdKu(P{Kq78i|@m!o%mpn4E{2$pQn-=biISpZD%OyMoGiv&%AB#8oZee7thPj8h zd}<_CAXinnlFkkv5s%%HiE+T%I z&x$RDHeus4UDPk1>%_`t{;d;{N+UV3AE~aHTzUsy46>aP?03Vp$4y+Qr@yv*IX3Z?m=r$Nym>6`mpib6X2g$9pFi=$5= zV!E?T)Kcwh!K7qG%}KS8FEhu%P83{NySS?6j3X#K#*UJ071Pt1`4O}E`Xy1$x*P=w zXvrwglQ}lZGik%DycT7Ph@Nn>4b8oM5Wk)Ejf$`~HhSf#sJ4r?Vj5%#>VF3}Eq6Z{ z<^-0Cz~v# zr! z2Jz@E#O;$YGBKT9@BApP4Zio0Ia;cf zT$v4TlntUvekZdNxn3+jR=v!rIVcy2AEdO*eA&bM$@Tzqo<4{?^)vn&A6q2vjub4D z+i^%IKKAfcms@SmW zW(4ZIjpib&eRnD!9L8mcyBa+@cgF31tiQeS!`Wlhwx@p<7Ve6mucS*o_u{nZS7XyW zVtw42ZQsbbHMl_{G((ue8Ov2;NRG7h#;=>Cc^q(gY2U7LbZ>rU;zTT_SM>n=iVNH9 zR{`rJ6U}MC-sgQ6%dd0W4IoP**Q$3g@!QV_eP3*!ov)u-YFBpT2TWv)vNmP)@SA8n z9K0?H;~!r{-KT2F@$=EX$I+D=Y%-+Dnkv*JU<06}?W~26?SZZkY)j)lsb=+_HA@hUdpP6}y+9n@(a|*KG zc>PXK{nj^#^;$jPblkk7i)EiamJ ztrp&V_nW?;cKcI}2GNcq+WKd~&AqnCwp2274lS{Y?~UJ-0MUuU;t=lqW%yIAyQ$jR zH!~JOq5a?<3=$H}iCygwcX}`*7kFos3&o~5VKCaG|7>`uSSZ1;(R%#8@YccA*ViRh zm%sKorH$Ooi()aQh4QjRQCj3HpMf9>WbKU3KP1EYvRjkHf+6R9;o5b zl09j=et)A=q>Gw>^9kNJI@0E4A3?84ryL0-tFTcvgGW|k0<@!Xz`%TSW#~Dae^y!> zkzC1JPxO%{&xQ7#$T@Ls;`E!yJq<_AcLd8}N@Opqt%Q`wb&jsS0N`z*H?+)e4$v4` zCn3tGPES{6Ub2Qf_|C{vL@D#S!SrmaYwt5-NF~iryC=&20>RvNchWZnrIDj>*LBDT zUfzvT&;?hj5$kBb-u2ay*`n;Hl&x_{;(R(;zXfzA8S*~Tt}}LQ>qw%x%893c2v!EC zPB3Y`u-|Mo_|>mr71VxG)WzkF;BvP}{_!=%G)n<2$V96e#~X@~G89o@?Mo3AI6q>-o37QOP}X>67*XGx)~xUr z`e?kS1L8q*Kp;yc<{lUn8ykar@$mVotjIDK#|6D;qB68*AbG~@3N@pZtS}o5t%XA* z2bM>IlIpz0;=Ixt#BZ}Pglg}U!U)lrovBDc@)-iN54FkSiI(g4XDt#xg2AX2tBAeX zQVFMtY+$e9#L7LtF7CBxkc^D(mryAhMo{%b!CMguY@EXG+DofEn%j9UG#@he7DmLx zVXD8}o%vpwwow$&cxBoz3A-w3RpY*GgpWKy0|u z$dpTg1dD*)uYa|dK?`y##BxwlQ zsmL3t&&~+{S)UQJjb_6zC|%P!^C>ZD9#=beWlt~DzyzRnR*z-ETlX*RH>pdgh0*NF z{kim?QKxI|7Wzx8-Z_!`R$%lDX$OHo7lD*VXBJG1s%y^0Isu}Z{5HbP8yKwnQIKUW zz_$5f-qQiAp2N@H#a_f`HaWA@Je5Ec@2^%Vr}snJiK~*2%O3`rj{-Zgc!rwvQB#;$ zpqYUFF0_cN2^Mb@K^H`FryzRfUG#KJ-_HVdTZNtf!A&#Pz+hVeoTR9zWJL=0pN%gVc zf1mT9B4S8_uMs_PK>iEs9>lQhohHe%;Zu7phBJOzB|@v#YvHr;=e*AU>F_P(msi(a z!_vv)Wxlj_N}-%tr0ov&;abqzpy;!77hSR zo=XU^LBcYQ&cNZ>&u09at&(%Y&$wQu@pUx0vGr3GzL-7^>TTrcm6pvbe$fb6vz)|C=&xe^f;MRV zQxfR3*?&DI_`I4Xi7$?r@rI9~f5WVqB$hcZ80(|rKy)wj=encT^%&)6F3 z)ygI+(YP>^zgC|YpkqpYyJ@h${@u-;#SKvmIll9?EvRRMV%iIq148QPThGL5qMW_S zGFSNf_InsIca6W%(^o_zzs#lH8#33KK3pOb?&xQoyBzA+wF>{)FQ&&D_;0CG5wFN;kGF79hx z^@;kC4zwQNcr&({a$i6{mv6U>#inc`@5t4m(zFt zSfvCCPBQ4~s_5mD9=%6%6TFMDU0R39+S5C)648HQB`8A?Ba5Z&5OYL~Q^IXTi%7Kx zW6IA2)#XKl-1)|~Coyl@!@X*{Lg>z-*;!^?c@v8=0;bFzCRrc3HO^kBEl{l79@8I8 zpPg~8bu6I#odAvQHq(I3wYYtrj#k@D>ojB-Go_UW5v%&LC?;Y=`fPGldc3wb(vGeU zpYd>)IrY3VDgO&=d@=^UGXSJI>-I;y9g8&Y$lMH&%Y&g#(kd@5mW zz;~2Wm=c&a;x$RKn+hHlk}2rJ{uPhQb5S3xW_lBri2=eg@BazQtdZ29-K~h&3_}f0 zS2C_P^lWAk#9W%m+LZ2=aCiI?vKM^f>o?|- z;npxH6{uD#q5PqPT8uf5He7$#HmxO0-3AXEKx-y|`c~ zf7xh`Ub4GEm_@Ryb_~|=do9e{oF^=A581jMdqqmO@MftRa_mGy+t3%z|KJ(UKl0Fo z-AYLjO?6Lv1gNi7`RTVMOQtS^Tu4$%)DFROz8EaW1-`C;<)o7xGMm#Pfp2XdhyC3k zEjiht_-ZV!A4$co7FmkliS*v>tMyD)XJxv(oBbz`o=K`5SLHYRMp_Ie{CpCI9196^ zYJJ+nw!IcN5qV0z6We1lQ@vXG?CQVe8TpN`KzYW9KkFcMX&=p7cqKdeEHs~cfbv8r z_Kdv&`6ej0J#=;y(y7DO&K&GTD!#h z>WVvfNtTcby-#?3PKMeo@RR9kSWso6<=Q(qQG&CMoJ;40YGPj`{Wk*pH*0GIvNo}i z5*Mul2bkV#4&{KFrQTRh5=qk^sngIn3O+r|y9RHx1a08DdC0% zHDlj;O65~W%L;;Sz^mtF47D@LC1dw%3d{eED|6Wh950?6$mt7HMb^T!ELAWJWpdA% zlPfN=`WJo%)QW77>u?PEFCCn3T2Eei0ML8#!3dum&pv|3G#u$<&(QfOGs#au(@dH7 zKOYPtE}W5UFC@Gc<1$jhe(*7SraLjtlFlIo=+ecS$svlOxNG(}Oq;K*0=zVlMaj?#X_X3g%zjY-GMvx!>`m75 z0eghpc-rae`ArvatB~mUf>`R|) zbr10P4KB^FyJ)j!Ho|71r38zql$sF!i%ktMlxNi*JpEQ3fxbMQawjPx?o&+~{JF3D z8*h!Tz9)rC%e4H}TwzC;o2EaBPrSk(P4|yilIu8o&z8Hy=K*3Mw{uB6=qT zjUHaF9k})Wmq;S-@GW(d6W1MSWFJEZsp2)#`F=)RGRvB1Jb?OWW*?sYiM{%y3fQUB_&Fm^`h+Q!V_(@o5@g+uLD1=@L!{+S{nhb>HR-2g{OY)#)P zM_)3B@N3ZR15BkIqNJt3POBs2$mpc~>d1mxh6=Pm&G1R_>C)Eq_bV|!5^$Xjf>4gb z4JbzO0^XK5C5J@h!{!{%WPV_q%d;8aZMo1fZ@Woahhi=}vyQ~uq!@H7@pp>DS0rE* zE&BmsSh=uLKnd}!a%SSWpV3}OLnzks(1O=KTFV*9fRTLtVe>3f_7mGLy^HDwhKMVZEFMCM>R?`F&(oc3T6aIx(4S5j~ zm3Lzkuw@O-nZE0@Tlr1*?P7S?OW6_MmoV4yLa9PEL>tLRm5e*rR-(WDlO8n2x_ta^ zdazd;NmG?dOpJ*JK40;IPI4X%kCmoZ^)R>J^Ycf|*_SouWlo!2+uvuO(=?L~&FP zUjhm8M+&(6lb1fD`-Ndx*~a?)5a9*UbMTx!L@ixiq)0)yMRVsAJYM^=&-v%=aDK)g zT~aPz7S7!Vec0I9_#j^!M*V|vfWyQRx^wj`q%Hv)-SXBbkM*J)36}+h_}0oL0ZTP+ zGQ}$tMg*#Lg4%L0L?7pKja2Qd@1eB*g}95G0dsXcfEI}9D~4t~7pe!w?sEQaSC<>| z#yl4R(n+*)svLWx2m9+oH(FMN+KrZV|36yR;D@$6k7JS@-!7n0o%tXBhh^_Gh|N{w zh@FdE{>f)2Q>HT3b?@C-tKK^NqoHAG*6rE-gQM3p|A%F-h-4xw|JHX)Sd5MRe;`)j zH=rDf&wYU44s|s4qi-nPC`%B(%AmL<7~~vcfFLpbaQMGE2%bSEzP)#kSUEt}%>5$| z{x31>m~!N+_l;`hl&Qo;WB-G&mqs;pC5##OKvRF{lGr6fhglv56VGp>jNCXHHDWz9 z29Q2JIj{81>&w1vp%yQk3W?Q}I7SOupI&dVQ77r>8DRB zGj;M{QY*n4$EH6Ij5E2adBT(Q{@2o4=MX0X{~CvRL=e!r4wM4xwl4%8|89!NG0r|jNfQ?LCHEERVGk!D%=t%(5PvJ(Bx-L z&_9I}(AVpbd%w1zj*NimHGk<*w3%|A%#=g$8yW3%%)8nghIEwyr|adQC74C?^mb#j zXLvgP*zg}K(rfTaU~rZ)$1`bu5bphOLnbJf9Kos1d!J@bd4;bn@WL|nRU}Jup8_Th zl%SwniC0yUdh5|=VuN|7o|h}>Ayo%YSANtW6K)Ih@TcYxRmmB&=!iC=thMKt9O9yA zD7aDt2>ONaB+eJUF0QyB{UdrL{E&t;uQ`~x$Ie7JG6b})Oiw6KzZ7oSjJ}ZFs&Ym_ z+AzxJE)n3Ni;nt~N7OLL3MO$qId{Pw738-_ctoZ;+}sl@>P>;lAbWG~IoE3k(GP~& z0rk219dMq-r$6pKzjw#_iz>|O4yFaUHl|>3yvgy4(iSs?D-SHIM>NIbXb43P)HT8zCr;cjbvFfOV$0RN3Y1Az5 zrkK+msCaVR`D43TDNm%5TD%kH7e(lSwUn2V{8}rCWb7wyEqnkGdi%v0pvJpZb7@vIEC}Dfk3c zH3g~ci|bahvNa`h4RxP?@Sa_`1Ocy93VnLI9_{YBymR05$d2iCn zm|_u}dhpoF*2w$!Mo1rc*~hV7ks_-wx&mtd;CYK0EcQtuifUB(xg?-BTv;)SKcrz?&j&FZHV#9_+3i){iHr4 z`yGuGWe#hKa0st}YEZlMPl`T9eU+C4c z>y(4y+vf(G`&KFVH2J`aaj}Ep6LnSgJTyS0bz?1Me-gHYlr1_QcYq1Yz2QxC&h`G9 zT^s}{N`C6W(6lEdvOV{}1tPJlNZf29|r4PET zclKcx=86@}RvI5uq{u%cHA&9iE$h(TJ02uGc$F~vOb-E~(_I#63aqPJ{TO{ad?Ial zvcVrx@BS*5$?dngD!C~JVoG~*4ixsj_|un2nSNMe8=X`1V9Z<4j__j&_=r0HgM+)G4QFiNor83C{rzwn>_Gb!(KaSwvwSITJUc|# zF4MLTFsKBDJJ`PEu6t32Medd^=ENnZa>xSo_^??xq;|dHdWM!m1uaulKoxD~utAqz;~J$!f_mSBNnj9>uTS|A5}S zJj8#8O3g{FBho>J?KeEPONW?Z?ryUh?V0zW-}P0Vupg<&eHr$<(pI#4pMHp--2aut zGx5HcbnZlFHf@>`ypsCjp*)jf9umI!qEYW}6TO1BHRuM4L*L%h5b}(@gmONT_R(z0 zlH^radi^3VE7|Iut&{6nF=aL`fr&eI{pJrf1C8G$U`w~WiZ?y@I{1tuw6EMd#Rz-7 zE5o0`Wn|N{>FA#h7&Ih#WOPYHU={g7(a&r8VnBVT7|hAmUoi>q!D6}ZJUwuUHYzib z8?r8{9-R-&y1TQ;VOoZA#sjsC^7uY^NUcn34Y7swv+MJmI_Qw0?Yr1rT{z+p;Fxjh zN7_oO@wF6RzXoIeA`*w0((s9tno=%KM{fT>NH0-4*#3TpTnP(dB-0jNl8qYblecT+ zSvMm+@*FgPtd<-GUpJlmPfQpIC&vb986dJrTAz`x z>nWGH=}<=3_vntv>w>tXlAyzT$2w$abJ<@P{kHLSAJ~`zOq!CInQJR5*U+vzK41zi zExkwlkE&t1jW8a!`-pi_VxW1#ms*v5c6F$OzG#lYu2*tDF zBF^Vv!5&bgv|42%{|XMO85lqu8Ra$;Q67!X4eKHuY^#8DbOGEHx>7v(4!8K~Rv(>B zF3;Vy1h1rNml|fjXv1_gXdwDLP=*;;df2Y}uUaB7?6;_v|8%{!Fe83IebPBP-b`8G zL)+c??KRb_uWxU+R6YWFdV9;lQW4y5hccp%s$BlIHj=;O1y6QPOy#@rkw+{10B-z;f?)pMHF#<3{oBCTg>+o@q)eeVt)9tSTmt2je~9MH0y2 zOJj5oHve6~6p=(%I3CeHB`XoN9a+k5#+TS{Xall-a~>inZ*M!q!6KOCxQ)ds>&N%6 zNjNe=DD!3u+0}Fmp%x?jzz;0plmFRp|I5rA7B5Mc@&WPC&Ad`Yq@MmE#Y9jruuK%~ zk>ZnBr|I_D`*;z!c!iHouk@Y!fiWL#Q#d!Grt+()XRpu8!=k*+$n`!m7=*8N&USgg z%w3}X$O#6SZexxEBk2tLwq53JIW%Nx|0=67JEHA zG2?Dm8>MCLO&(=^?N74&6mi0I6;GJKNy408jN$VK<9-&QGfma9Y? z*CN@g7EIE1i(760RaUNKEWlbp0h~(HwGAIba(h5qnA;#bz*@Gr+=tH#^%AX)Lkj*b zk8YKVY*~F+ZXSHPUHT9}>`~Z>6}JI3fxhpLRjphL8JJu<0jlII7csL7oyxlIKvyRI za;zIRUQ|5Aj&9}C{nBo0lNX`+Qj#Ixy=_=m*gp_7a8}~FQD=^6t-M^CYEchhA>&>x zUv1z-TEYA#Q~|s5U5y`}F2ebuzy;9KXKphJ*Oz|h##w>%a&g#gOgK&I_j~B`5Y@(r zygfQhcS4m#hm$-2M|@!R^}7-0e50x)7q^BD&{9Srk#0(lRY6{{9K|l^uA2t3CbLQ7 z+Mre?Gm|0E_`YvTNe^RT{r409W|6N1O=d5?L400qaO(BRaobibknZmGrM~=^rC=@x z+`(^V8hr+?rhU9OTTho_pLxAB$a|zJ^yT8RUDnzefcIt#lv_vh^z`aNq6A_pG~i96 zEBJbMuvE_2QV@7w0(htb_D2E=)9;A~_L$K|iLdB1e9?G~Q$dwxV8lR&+=Cfp!fLs= z(63$bouyd%Cr3VxJYiB(Ot0CWY4p48Vfz>8^ga$d5>(N`w!Pdx!X_U%R;jB5Qt!@m z$yz#V3PH5)e2tdxCF4rC$K+y`X+uBC-WoMt4tVbVRQ@4stW1njQ33b;d-w4;8^b&v zN>|RZ#;*+A2kwCmEp*V{hb;ahUQ0V?uT?D28+|;#lnP_V^*PYJE5{wpoeN-LBKKIw z4k95Ew>tL`EX=n@TqxmM*!W{-;4Vf-L5PKpulV~E6oxj5S7((tu%pvEusfEiKA!UW z3t0C49FKY`3UZ#XZ+gv31JMs&qX_P&<=$9QP|XS2$L{r6xgYX4voiulCNU zq?Z2RgGsd4dp3jK#9AGVwl8%T($`tlKITVnE+q)JKRIVvp=7Ock65w zC|(M_S`{3ezF=U$SQ^w>R_baFf$&ksG98Z2EbnP>%D7eW zACH{|c1Nkrz;x}EPp&Sc4uFWGHySoy9}%Y4k99IQCG3u$UOZucEq{+?jSI0KB6=zW zMUc685oWTByyA+Q&|_E%-VdE69(|_2@Y3(1B9k6-nKl{WN6MSWn|Ialh4A86Azz}% zBEhE*G4rR)TnQ)>ef{S!$nZ)^%69C`=D@2t4KwH{_~M=AFUgMlzYL>?ZpCatbbUNY z9C0vpDXQG|Xdn5d%p5qxZH7pzRi2>sba@v>sk|+MK`jJsukOgZ7y6Zm1CFiNWCh>m z2Hy`ha)G=5Hbd`EplN)d==gitBgbbc_#vxuNsmeM2E$~LCaS*VaFU^d`V)5L`#&Vd z=I$v$wc+(i2-)AGm5fqf19n=g{y+s}-p8^7m!i}aKGI2+>rFE162VIZX&6658f;eRyn>zfiDy zeAj_rZc2*kuYZ=?Mx0!1Y6{$<5h34V1lXOQi{5dn`!GeFcDh8sH?A6nY2A_Lf>CsW zTICg8L z_lV@dDSDq3m5`D4Xmlxg@3?5Yn4u~d-Iw3sW2LcOfmD|?)}*0#m{TPW%e~u9mSPz- zCW^azk23l$o|>Sbuj}^A*4d;i}S_hIBR9 zg;j4bfoTc#n{HX-l;08D)DYU*YCh!#UV?ML9wy>^t)>iG~y9>msjBnnNKw#U$`$Q5cB zU1McK@cL-eO|b@mY}y`oQCI~^Jz^K4F)K*YNzvHMr66ng@ABb9@51iqvJ#C>Bu2A_;QHFnb^;AnaF3?n zMZ7GnBLbV0Ft>m38n(;W*j*u#SFa1fs^ZkTziB^9&{%j`m{(H%iA&!m@{*nR5A`ja zxL&N{v6g6}q36?a)+O@a`P=Wh$#++-plR-UHqE0TzNoQV$Zak@TF^tbw!m}SyqAY? zys`b5(GunvCFns{Z#(Nr&@LFS1k-h)I#52|DD3xCOg`(V^5!1v9K`CTqu;RcL;3rO}^Q zw>GFvI-kgopL|J;F2;sGfSuqHsZR&@XC5z~I&E8XoK70E4=uuBEWC{di$HBIm-!Z4 zD#~#*f`HWrarJw47Hlz5n9mu2Nsgs@YKw?wkBdxGA=Ffj{++%6NZXrdz))JJU;9v5cGC(oo z8_`eNM4Pf+$F+q}r*WL_j7^fdLGLF$>v!0Vb@^|aqAke<9osjLQv5V^K8Y`}ul$PS zQ~&Z!?_$NMlj+X#Fjp}IO5^y>c|L5RJaX$7^n(U#MVpK)Rs%eg!j)xkd;6C0#yhzC z`AwSwBJ7YdItm)TPQt4yKbB5X;|115K;jDhs06@ISNH3TrZ>|c9#|XIwlZY0g026O;PWUgf}H0wZxejIT7e!TaERpPqVZ~C z{Bi=pw*eMZo)`W+s5r9^nbsuqqKef-j;;P_Yw#lZGW(4{7TMvbrDFP=-O_HWIVwmTcg*j!@R z({%a%dgTHjQE1Uz#8@(o*)cXwf>Sc3bv1B%)>&Yz#ePbV>kd3#Ac?-tcABq6u{kWq z`7b$)#>Lpv&jsku|Hvd89+yJfk`-r5^=T=uC*1c!p7nkH%aI`QoZZZ#gs>dS!*<^; z)s?5^$6=jIg!o@7C3 z{;EhgM1$~YGWF|$OJ^_qlc&RxDOsy74d@VMD$mnEM=BiBH$~InYv5ei)^oALr=(;&89?rZbqsuBf=(F#2Th!nk1Zb;#8UWHD9K}Q z_yI%uh?ZWMHx#75@_xqR6t{5JbiM%*QDq7^Ov-`2@*rcfJ{^xR=l}itQH-j898&{3 z?L&{+)!nOx>Ik|fA>o&Hu@pWmM!d+k)v=-0Fmus22O1jU%Aur2%nJ=xjFWDi_cerF z-ZQCu1qi4DV!g>s+GCPH|NEmhqxjAvsUyl;to%WhK=Hdiv32_mMQGj<8->?gAIBkq zHaQ;Y)#b#Lo2J|5N(snJZ8;DW9lE6YfI1|08}&keIAIoAAICng%ko1|`Z`e3{CK+} zwS+X@?!K*x*3w6CEfg-|Bk1bz_E*nrs@)l(rFX0cv#g~?P~e+TtTIrlby(P!1rn7^Ou6|3~(6qE@H-mw?)JGTXzJU37;Qq5h} zDT{Bns@+j^f73XTT0#+UpyUx1srf<&UpHLP$o>`F*B3T3~r8uts4&i)E!z-RD) zpVwI|*oF2_lVHVpX+9b@G@HiQN#62-rZT}Jy2iw%DJYBHp8`ftZ%dc-Q7TEPRkMp( zMr_08h&OYLQ0JQ)ywu~F6|Q1Miq0fhsAwb#szK)DSB@uVJqb z34{n;=5O!%-X~~1i<17eCZewK(8+b38?x4Ow2d~GRC@`JflbkWB-X-Xmr-d4#;`9A z&U`e~;inFTz00-;!3k?_z{vCxek+bbG?#dQyKrArbS8K*mSTiezng{mV7qAQ?1y=o za_hB!n41KxVgB4hpj#A}oKu9VUuXIF@OkoO8u(Qrol4rSowd~b<=^4zA~(jumWA^u zDYx3F`_>jk#!u2HW356ZTgfDo$=}!}76lq9^iOk3SA2CzX{M11)YYT6fhRvXf+jwA z?s8BFuI#5U(8=qrs-T|Bs(#nEh7;1T`rv{4Gws$C7JZz3_fdi_JiCC6{};v}dtZ=? zE3(pN@KwXyT8=bZ7zucvddn8^BmHag$AaFf;8VB5+(}uDRJ#R7g*Obr%jrA7TaH4Sn#m7Cp5L_>{&pLMtJpjHCmnhc4PkOY}dubUw+uGOh|=Q zx>lY*NCGVYO$A-ui>KMHBePmNfCecL-J1&xdvor>T+dtVKbS#4HRUmF{p;u`NH0%8 z`keLql{wi+S7U)D6Ndf|-7vZOsM)YuZMmf9-yzR}y|0SN1p4qZ)N==Tw1j#eob?~P zPJR=hu|dW#w#}9t+@dZz>QoiipZV;pPLqvZl>*w<<;}rob!(;VN~U#;v+Vw@m(%&k zb4LqFlFr=k!yiQkGf4L7S>^EMA8iLqagA5G)_bJPznw_t_7_M=kkPF#cHGoegRh?% zL!_*zgX%_0C_eKZ@t%yTE76!(|NFq}qc0=ImVsgG{OhMw&PncFxoDI{Ni!tr`NM+l z+KZFxtVrw~N^y?cm1*4vSKkG~40>XH7+dSJH3*Ep>v^A$CCp5GsxnYprv z)-(81Vw;AU9*KSTdZxY3r6Bw4RaV57J{iQ{WZ`r(*M5r&`L0MvhM+mj~JqO z8omn|9hHW4A6K_k@YQrj%PgYgVB!xGo*S_z-b>vn!d240iNe^yVpRN9rLF{k@BZc( z{3E$~uV28-nVE27_Eh&5ynsl=x#!OuZZfWxkAy zunw{H0mghrT99d-BMPUM_HM16E1~o4{PsT7bnS+`E4y@hE>UM|MAwynM_8{pets%m z!PZ-c>+$C)Vr1WUws)LbW(GEuPc`(UWL{hC-sQ2ulOf_Eh|$nge2z%yIj zwR;S7Rk=0Umt;cYr5l0^vq44leSdet^0gIdq9Y%m%=z@c^Va*C5b-^pXz-Ti!bFv)K0xnSrf{GH)+)Pprb z8|Y-|}}#P9D<#R&mbg;Z=7AnbE? z=WCJv{&7lw*b94oWY0~{l>{UDgzH=Rs7~-4{8ch5f*|MV;Q)WFO1uBH zb@A6Vm|AVB6Y;KHmo*tflqn^YPPbTCUPtHpfvM_D&Rhehyn&?j)^*5tf`H@sfVvyA zoK!qxXI6C(iJ1y!yI-|IP?pJeVaEc(u8?e;6mLz?EI-8xsP z0gN8sW>az>UQ91D8zQ&M8)s@AV8K2r`~2G9YrvM4e4!|wSMd~(cpMzQJ2MiND%}5$ zLYv6*OVJDmD#96hhfVa>7svs|NRrz&PZ=b9C(;pUB+H2T`OK^*&$C(XS#)CL_whd`PAT%F;-MW(9 zlym0A|8Ug1mS?HAjay54MQ9nFCtDQ`&laG!P32I11Cb1NQDnkW{e;N!eGTtOTg1X; z-3SThppS)y2{j-6#1s(_-)pr#95K(g4@wt;r!c7x2+--~56pLsT{kpshURCC;kK8W zb4c99xbCY*-c4nvLV!CJ>lo*n?fbP*Yo;(oPF2PyJo8q1U|I{b_xuH=QNf&5MttNe z$iqbRJ>Da0W<{00yWk5Q?jMpuzz8_s(!34veRlX=OO(~OWNxVNPLv=U+@Z1V;2dUB zOs5p<+g^>bBrL9^{`+Sz>yhWir<=#KUpazszmy;pLki}j8Y4H~nEf{4P(WE(qmAIK zi81<`9<5!NQucawIG;uGbMm07axQ@gF@2-X&6Pq8d^3vvI?YA5(A{?ik*>C-jLb7>OdH$-===xgz)B>`TuEBm6uWHqJzN6^>17F_I^?~ZQRM&!n4_47>gVFT9-_)yR0bPba5_1W8dH7e50~r#;%X>E-PxF zqYG;&C-lGAhB11M?LI~^dUcOhGzWvLy|`kMeMUF0b=xAwAQHfS+WZ&$`I)+T=q``u zW33D!V!#g`n$@VJ|3750jnbrJp@;!pX)d0H?|U&2Z3aBZ(!os>pf0GZO+2?6+L!Ox zz@ulB>4WDgfV-qA(h zEEY0BQpj(c@B+Ak8+GA5AeAAa+4?t?agUA@($fnI!7CWhdp|=r{rKeK`aD=k z>9P|uKsB`i+y+kF_U(>4ezW-k?koUi8qKGs!hQc)4?F{j(j=hs%hmU`D-aK7HmO~l zXzpmH>-aVoYfTkwy;-UN5pKrN&e>MWqq3c1%&tEUiy6w5MZRt`;s3^a_9pST3s(FSn-u6RdqrxRgOuu>RaD_nC--u-x+9ieYS#P0t<9 zDKAR8u629ogtxeJ^ht1M8_4Ow=b`>TJbvIk|DceD%oe27+0vzoeRtb7^XK4=8H=4b1*T2w0W;3;jn8-b*(U?m?vH|a_b$C<7;46Awx2?2=n!A7SDUWC zkD^ri$a7+8XnI|;YA{WquQQT86w~0Tr~4m6;ry*fBn#QC^V@z0QQ(y%P)fXx`tW$( z6}qb7OH&le=|Lx_7OQD{dt>!fuBrfu`A{ayB_~*wL6K|ae*aFplm@&hdb5(hy4XF$ z#D~8T`A8}WKBel-K@@z|ek{`=oHlN<9~ev$<^n1aQwrgd7%-Lokr5!s>aW|CkM2x} zh3K+Kcyl+qH}54N83EYUNEpdE4M{fA=!g#csaF2gozz=QMT-X}A)9(^e`QJ{z~WwB z8eaF;RD2Ry8|T%D>aq;8c)xhF!FZ%|=noVOy+3Xt4u_+pk;_s>ET|o!{-O4;<)SOs zt6^W{(p79NTraEd)%%TAzFTM8VU0Ch(XZFdt14WJ?#n&s1Ai_u{TwZrx9Wp_A!+?f zUqQ0MUH3?0UxRJYc-iZRWj<}06fU*F_cavLvTVjWwMw&dJPPm1Ls3aLPm|;oalZgg zSz7nE4x(QtSsDMu3{Mt<_U5p#kWN!u1W&XwAWr1oJA2Z8>GTRF>kpcSp1AvDQzr2- zH%W{R-A%Qa&e`Xh;z@j|hR0!aG;w;c7G(ij))T@I`V#fFxpl6;sLb)&ZXDH2(Cc6= zi|q}u6bWoHT?;!faEgn3@yL9O_%lcLlphKNT*@QuDIEU9K~iQpIt9=$nOSVpKY-U9 z)$ZLLUE&mz!b9%v?dldB^6?bg`SvJe>lx}YrH3AjYl!GhzFiNcO7%{) zR`mgPV>@T#Y}K4TJ9Pq3S_X{o@tXH>AKyaI{u(`a1ik}=Fj;ZH!~;ksw(Cc)cm=;Xzy;l|K`Ji#6jiaiVcNdavZPz$ z7d7w%nPsKc5AH5sV}Ig);*Kr?CnK%B5RovBL5Py%;ad|_Vs%Mh&4u%JZgAk*uTo9x zyQ0>1aK6m@i^4;!@R|pqb5mo6-E7tPwz*pL9D({*3>{9ZJ$!98K5587y1vU}hfo zHd#feZ!pRrU>k?s{TaOR>z=z-TwxqZ+z3#3#WZF~I<^RZTTYc*SB0GyOXhqPS{;m% zhn|HAomefMTpil(Lx|X#^N>jv zMkG-d3mYDVHNJ;OP;lhwu~5tL1HQ8Pc_1W+6;)Faj#GNH6k46%_*wWBalep}&37N9 ztmwv{z6^Vx-wOpXHeSDeF4LO_WOLm_T|w_sro^5WKFuae9`Gv;OpC>m=e)(rMP*QZwa_>X(?=70qUEPPG zL4fJ0&+99AZ7>)eZFX9+3-&UDY;M{=@FxM?5Ov zUU5`a&8-a*fC%-I0xgKRTAG_@__qc@a8rZG0%{NpP2}mq3333GNoDp_iXo4cuBKi; zh5cR{wrgV28$p+?NQM0eudXI;$Q1YELl>}9gIL-*mC4E`q4WNgPsJMT4?t;NrDc8; z`X)qkQI5LyJsomI*qIgWY0QM6?=gq?4DzO@!Gb_? z2}1v0*49l4qSPk(`G1pv#s4G&xB31}1~e;%5sFOmaYWMHR(Z79|Ei_=d|*;aHZWy5 z$TAvrG_ELs1$VO+lo;tBh04r2DopnYF{J_8iNVfYDcm=9M~y%s%trH1*841BVGZkh z7kV}WoEkK&EH`9Vr&XR6*jhJFY^OTdc&w|*aLTISu$Kugl9TC zoyto2QgOIbRiKn}ozvsWy7?-pfITRjdquj}HvNSs$PcnxR9J&aT_v>rpxjTM;P*BV zkXy%R6yE<`Q9HmIEh?rPc_q0)Cq7-bgwRme`!v1hFC#PC5WTmblQH*^nti2c7l-ch zXnfC}JPclSEs1KWbbVeQm}nCOSO00khD4yv{@W$y>@b@I_Ctzh2zj=||!lB-j79-ekd z6bcv^8p6q?WUZ%+m%W1=8wt!0zPL~bb&rv!=KX`mbuxjcBL@Oe_q=%`5~UzD4wGM= ze`40BB_I#L5I+~RR7qKll$}%^Ve?nZYAg0h0Lxna$@qQ``;3O{rx%!(!)l3ia36Rs zW?Hc~EcZM9TrxbBeLm+h|6;5=Ch+OusLxX1NWC48xH(XhXT~zLvP0izP2@1ueJ zNp5upRzb6*g{tV%yPn}LfvJTiU32MMq~^fPwg1(3cA)WLmp0&;)%YZiC0)N}4Or|^ zWe#^cDRynL`H@c2!hCrLFg?pwwS=53b?MK#L-Tx2_EI)BzUr>=6z^n zK-(j$(2g$ii8>2!7Agd7Uiy@wulUJ>YH!l4@W5@Z6?+xAh|R0) z{RM*c2=uwsF1zls4akf`XDItkp)CRVsJ+gsq zqM6m7Df1VBbjzRe&lJ;%O6+1B+g)UF(KX=JXtxLLdz{?MZL=4-q^cY+W4VS>IKAcf8 zo;Eq?44o(tKWm~p!9PCyrJp@?8;(i77HCN6bzk=5YdvDr(Qd-gwVp`REmPBoO`Xfg z!0+YSW2bHBY|@FIeNq;>$fTp2U8;|6Y_J<5ih57U|Dz4gww zYCfHgK)b2DDL<-HHY{$=j&uZ|YPG45gitaKxMc6!e&UmOksQdff*od7R03!ILL|YS zKH-yH2+3!L#*=SYm8@q6^~nu$cYHMNv>Vj?{*5ti;a6O3bDd&|B(K0{{9bE^x5kN;ZfUbg3CanU&5 z{P8BIpN{qJ7l7I0`zBK(sV%05mz8y*WCiR>(UVJk`)Tt}p?DCX?$LEtkI1jlJ#&du zp*5~6cr~De6iud<-NWXOUN|kJ3Voq$`4J{fmf;`C9RslxO_dGH-Xq=wZ2 zXX8iPv$sJ`_-nnN&-|}sXxCDyCKH53WEL@<8gRuo8P>v`AkP6)2d}i(EXrE%_I=It z%7r{JUFbKup6}8&$_R;eBUEM8-cwBjP7AGwxi@(h(K=$iq(RcaIrONK1!iy+x0)<; zGEh_f-- ztpe2y5*WUa){$zSvCb2(h(d|;>0mrK3x5Au`6@Z(jxNrYfoSMQvP(PiofRV`syq=> z&4`iOWs+VeQdMM1M!Z=ITX@{(SvJ*sVY1A{9a{X&Y(G{wKy{ksTqN#NEz*Av)$zan zy8}O7a~_4cYf*38AFnUt0=u>2Ea138_nYedNqv5Gbi5)BYOXg)`0E7EiX-^kMe&XY z6V@e*51SbISNT5^H7Qgjf%#lTV-cn}xz(I-i0qh$XentiUsvJK}BBXf6(BUN=2BM*HM`xG;z(7NFjR zOSFQ|?=IWHgR7sI99&z6P<~OB=$WwvwwF}d{(I^QDV0HwR6J6c3nb6I}#fs@2>d=+Zo@rSi(UFZBI>H&t5N)YYw}Xcx4$5q zjZU^o7!PZqcg+uM1Il+=xaq`NZQbnP*y9Z!vwuPt() zOA^YZ9s`dT64kb zo6gPA3yC=Q9~bVU7gpk<;;;ZR0x<_w07W`Q^+X+Eg2n)@fd`E!UGW{m-n&YK6rZ>) z^tO?{i$LsDQsTh^g8b^Ap{-+?(*Z7Zh{H)O;X%;;N|f&Kq0{!|_3TLACUN{V7m<2l zne^1=0a6M*+Q3tlsjfP>kZBdBHMcE-q~So`z8y}%ukd>i?%nx2x;vj9)Li^oot;g2 ziVWnUNu!krO{N5T5v&nqL^ zVjuWd6qzB4{#^nJq3pNkk-w9)i@3UUmJIJ1<8JI_0IoFu#wqe~7&Yr4{`1q<1Vgz$ zRQ;QG)LA2{6IM%YH{jd@9=4&apL!AbG)h)&C0#6tvI9y3mT|=GQ=Kq3b&iz(S1bKR zAaO^3p#gluSBki$3p;lWv~c}c<35?!7alzSSd*DM{>Gca#-8`Bp9)fiF8!tzhowx^ za{=fFc0qh;n-7a50ZLX_V|Wzb(>#a~kkHg#WVsEEUmkmNcuQSw+=i)BdPD;UUo)}D zLLM(59vQj!#=qP{Jl7OAG}t7U4*J5ahHbtAciiG1TH;alNMM>avJh{-eDdXx*SJ3} z%5?gpkCE}y+4*g`VSV-KYUNN z_WVwspnfcm!ZTZ_Z*+cVc6KKl)}; ztPgl6-U8lZad6_`q!0hr3gq6I`wE{P4%LWV-#?I<_gm$#75>U$ zT0@LD$7R*hkUvP6d>{3tV|JtBFTq2bD}Cz>AaCiz_8d%$+@Pc9(taFAJAQAy6tI%E zyVfsyOrvZPK`vyaq(2)UUiRV&`(ycScI9{%L$GEtX)PleQ!js_Hm4p89@~wRCG8_g z7G>s>D#I6@l93ZWI`e&AIC}GhOjXi@uX@Pxee>g!@zbV+Zw~ND>e1+1v5FMzRwMPM zVoFmkUzb)d*`SibnCK*e8WgZJ8F-JxNkvyfq`c<7#>ffC9^wsSiY7?OH|sM;V#0m+ z?X>vp^`M8F6))wypk%M1Xpiqx!>1Fs;nYdtd&BPj0o~1u)9k!sba=pU`Hmu_d;Xwq zEg?yGE!udFif13O_k?h;SNKYUuszy&?rd|k5=Fyldg~tzbD2C{|Ij1#rvx{b5D2u+ zvzrn-eGF@_{av>jsdd*KJ=0~Ju^XjsKK29joMc)mmG_H@Z*=i&oqiufjC(zWyANWb4GVg| zTEY9Cqp@bjVyQmkwq%saGQ;nRLPU?IhWeYO2iM7bFXtMktnQB=B+#}vVv@M9HcBjpVVBflCQ6qL-`KmqPj+_mrwVhvbZL2lksW} z%KFIBz-yau_&`=}{_K~t`h)BFZ%%$no%=#?M;|%f>G@&W@xT&4_qCF_AJpMc$jh}k zhpl)0P5GUdH}-PWzqcf1OxBO$;hYkl+_9L|bst8z3@1DI)7HTar9Y$q>GEttruelI z)0zsR1qpvo&7rn_Ce~U02cQ0Z^u%>abd0W(ak_Kij|tVtb%`4!GbFy|d1&+F3Os*7 zf|;itYK)$@&^efjUt0{?|2weU8UINraj>t?A39VSc_|$&fp-W(S7xA1$JEKzhbc|N z<Cr?(JgPSvk-~6g%T^uMs^<(uZA_$h$@U+eKniQH__evK%0*s46Bd zl>4TflsRJzfP9kB({qz48!&X`nwn8}VQINMwxk3K7sph#gq20U_P{Y9LP0q`x97Lp zS|2~KXs2_)m_*uzD9egq}B-HtQzpD!{~^s-%Pt+e_Wq&`#Bwy>5hE4Ar~0Xf+5>(fBc3#q%k)3{=oIDb!kDLxC)_l89+g^~p0b-KZ9EgikB znqQI;6*hR6A?`=-=AxGhDQ}l~t#>pp2>W(N!5j`hZg#SJ=XkXyE6=^vlHlcY z-&NrjyVF5J77{HUoPnc4dD3IE1g$(2vPh;X9ast1>{_=cqKss!toY;02N#NQaQC4XXZ(|@ zqRj=lB`2owdq=-Kl9w?F8GA~dUl$WX*!2XTa+v|RLyu-1P5IxnD=(Y z*GS^9?FXHQ7__^wbss^5(&HnHx9d_Esf^K#cQdlU%ySr z>scDve69CZR?Wc%L2omhIJCuqiX~)RujoeNa4(9?WIWT3n6>{Dcez#A;?B_8*%q@U zB5&W?J{&Y{GP&Q{=lp{u#{IR?$}f){8{dNHWI0!oTVANfPw4YkOF2zF4d=q4Ty9FZ zfM|`N^8`IAg9o5jx%ZzrE#h)|En_}k63G~OLMqy!Jqhg8*^8A8YXK%`i$|IRv$bK2 zdb&}Pm7?J|2~m2xH0Glzay`h)>Y@p1lOpo4-v6Xd(2{ydpjt_o-Phb_E*#sz zHBQw=;%A8{!a5@;kAV$LgT*V<7%r__*HAys?)%K1yQZTrA1-a} zj@%T?s^-!tRBRopZQMQL!(`(lp3u1Yam%_`aWvDB%^lxVK{6Y2+&sGu#AN-!*s=FZ zt2m+u|EX^d|3RObhIv1f!pF+a*rl}{i9v2xMO&sNLRPj!&%neV3SU zq|!!ni}c~0eUdLp!+&6VIY2_ma0L~R3R4+OG$@HLXr5B{P7tAf9!;ejAxHK{4=Su8 zZBXlQ_x{gQd|S3i?Fi&Fk;AC70y%Xs&6#z)%+Hw_=Nt?ur=Ie@AHv;Jy{##(jE+5> zdCOx*Lj`B$iLCn>XtX~G7v&hjCcSCDN>T!$+RR_^S2iPBeD_`og)eZ!Aes>54^5=1c!8CPSG;Q%+B}!pyB;bHC8$ zNFyW_*7WGMd^g`5rYVH+RaD8g%pigjjZ{YSkNnb%dC6 z!1Jy}QJl!&qhC-H#7orZ>3tAK+U8ui4B8F~zg0E{N}xygj;n*ilfejPHsOEUJ~r{V zr{N~-5p@4nZV#WnDuMXz50DvrNb>i)h%q7oJ`aI9S%qiUg|?46Wmr7;4X;oBr{g1! zE$UMk8w(J6KwpI(=m44;1J|e%5OR8NVU-fv9Hh-G@foH8IJgM^*+8&Lgx{wd)4jJI z8gXgyl*{&YUQE`9(s7F6;4V+yaO2bufVak9;@XlBVxx#mbaCw-xn*%W^K@VvH}imk z&1zw&f@_k*8OTekKHK=2d0G=cy-3@;8{sJL)^NI1m>sk50B6Mkxn527{4Z&tonYI$ zzKnm0X)1CD^!lx2h>wHABf>hHb_*%%TRt5ET^Y{q(!Mf6o{M2#3OQzCZS69iIkV>7 zGsUk}$qzF670$a>f`K|0|-Mhnm|k^qKj7NLO->KaM3#ng~?CRk(G`@tTkIdw!mdpN!|> z!-Ga<--(n3Wr+RIERSp&Q8s`3<{q>tl$<}cPR_X3LK6^Z$%fQd&;^hceJ~!B@K>`$ zbIXQwy=xH}VQnQeLN_1JTEjM%6h5rdgE+mgzz#0UF?$^=(VO4w_*{3cx#wsCkP*NI z?f4X{WJkL9#+AU{@{CfPE)ZfL^x4EU%J+rr)D{m#?lRDH`(h2{#dLdwZIld%o*6mL zUi}My$juEqTR%0!ehz9lbMxnxZcpcns($y&EW`*Y8RxmOfOxo)sX*x2S%Es*tx$M* zV5?-L(f}|5K-8@7#7D1xI)R*avZi?miQdlJ^;qy4FB=GThv>ud4<_cMyh<`9 z^XQr)VbJG?m-x!k7j`iX#8EpTV+H@c4$>h}zNT@$C_j4i6!2}Vi`WQh_{qaV#-6+~ z7G)noT-zvt3(!A2Rallu!9G)z00Jl@|0jSl^8s<=DJ~JcdU@{hhWi0IIq7nziqQWK zo)BMXh*)#1x0-JHqMxst`*r3$-?n1*zceks4GASqeDmkxfa10FfSVo1T9ng6(ryf% zK{dXgo?ejsl~}jqnPnMFomnTL$!jkt!~#N>l1QP+Epz{QKAh!-|AmliYg!`RX0xuT zQg%pE^`UZF8Cr&C+0=_JcBB5pKqSswSRmLFbBg<> zlPm=#BFawr#IldvtXzu`x2sHmXO9t0H=l|#LQ+55!fg6`d$}OS6I!Gdxf1u`7EVYP zyM?nfNB&tAWnnK1ah<%+4Ek8atBb@}9<|IpV_SQpa%R%&e-DhmIQaa!l}n*lQQ*``k_ zUf5ty8n0!BKL-ZLCGPg?i`$}D*0}7Y$0J45o7J94*i$EK>(+s{TQo6dio1#@LZ_~u zL#W&@lq=j%B3#eqy*ko(kd&*@+;jY!=BTPnQzOF{*+(#CReI?ebxSRZvl%upD{n~M z$itJBVPI7Ey{!_Ux{gGUynn>&N8d~CS=-P*`oW>7)fD#GUkc`O(S@>)pm`_3{xQxw z@uv0!5Kx$nvF88HQZKo)+x3^A&A@gAhUv&4`+YA*!>&e!nHi&;6wo-6bg2E;zPj_NAMo1QjiK%yL-_IWr)PUMJ zegiC9w=#*Hgk&94`n{^m^Acz$!V36l0)8*vL`9XV*fLMrE`f?zvhtCE; z#mdEh3AC&xV=GqO+Ba4sc1|8L`k7A1AG+%HXGIsVX>eI^GfXV^)0-N8n-z+e@N3rn zOJE&PN%wh^uJ_@Gf#Dk=yW|wzA=Tw!=EfR9bFj|HlbPbpA!24Xs9`d6IlvcS5)@-Q zL=dda@yi!a1TTgaIVb2s%0zp2C=O>PGh#U{-XhOkgm2k6By~m@49~33rVQl9C2|-a zDzYoHxk`oL7>mo5B)Cb?far#gZz}RVr=V3{u7ff(QvT~Z#Pc0Zlw#7nL3T8j8v;8$ zc~$a_L^?~!u7HCaEg1+Sr~O*LUGa&PUd2ry7MZA@sfKH>?@ElZN7#gQv*@RSg$4y2cU30$!>rr z$N1zdIjBJf?HM6P7HyEo!6o+CU$$9tKqrZ%jEQoDEOG@g7`@y2z(7B&uI7B(w9WXU ztIS$b6*Mrvawa2n^h)NKoySl;)~;GPCr2_+H>oIY;8I7*Ct%;IBt~bvxEE}vq|5oG zA$*wXhT=|vS*Q8F+W~}gpP=SGan5p zV261id4^R}71H`sM=!(0o!f@U{K;c&Uy4GraJRVmo+}@TPjzy8H}-SU3^nv8NQ3X4 z@6jaYk7GuhcVOWD_DRk{JMPw2_GB;H(OzPK`$oHnV4y8g0`X>0UR-7D>FRnxV5#n) zZct6OH~PgKrZ1Lz=S!>F5WR5;Q$k|xeB&s-yTBH8=71Jc0nEf_;BYtc!|Tk)2I0*b zRrg*y+&|$j16A{AeH=;B@t3PID>3XNH&3loNiC0Nrj4b@a(k`3%ybFT(r6WN`)nr( zy9VZv{!1V*fk>V_1HsoZJ6*G4%vE8TCR#i3HkqbSbxVnp=^w?=j5Ng$m^_*sZf@d% zLc5_Ep6)dAa}(bXub6u{%TKNI4iUR%zPa(VwzkL=#P4$8WR&SM@O0|m1(B*Aov_VN z6bIe)s?}wQ%MeGMMbS7VU5B!%snI>Z?Y!ya6|Vc)W#{-AvS!hCmJdS$+?ua$|A#?P&F|3>L7 z9DL=BN>bYXeSXOPIJ;C0SnWWsRy*VLW^v-+r8tIFiMGw-&5~iDhuy5uwezwYttTP} z_QKdBEV|U>kAM}B#)p1Jxh2=b>4bmE*b~Twx;GAHp#5M>y2|rnp@z z>z%lnt=C->L^+5;l`Rg~ItbtT@{xo;ISJSg3o)dTnE%snK~eoC%+{NdL09o+2RciA-|lEAr0Eyr zUPu>!ugMpS4OwatbfGmLsiVT`D3onZgf4+5`eZpE?0X|L(1oq`pH6H@$2m?Xs6l0a zn70QRiq(6on`{C@xuV<8oNi6aQN1bO7e_LMVzx_cXrfK@09dbdz{d6g5##es;Gii9 zlZJ67tK1%iUBpgxy%BX9U<53aoE3X~$XPXd`qUKzI#kfiCh&zdF3PC4)VB8|iU>nI z8Mrv=5VVhe4Rp49Y4fd3i;(WdIjFOZ3cWMz_U##HI9keUk(Onq*5Wswds_6lDe5fH zZ#?1XGd7yegi2zo@jT|Sc{I>dR^G_a;J&ttcT!{KX9o$?yBlZbXr^#*W4bef)59vo+HU-6J$U;Ncrhcl z7f+`y-Li+I*#2S7vb4Mqw-K>r3CDr{iUBUAeH?iohwi;Q`0}&4p9w9QK%)KFI{h&X z8>=Ua@0nBJ{;+lQ#|1Ov2(f+q?5x=t%hSQwpfM5|w&oiK;FIU)y{z2VquK2E>p$pr?Rll^K z9|#N#Kx4J!L|+rqM~nj}a(UC>v=eQAIzTd;IB%mCe;3A$`h2k|lV0!go^k}#Dw^7# z-Z<{#hxgqCsP^TBG^o4FF=FaIDl8cL!xlR}&ri~dKmkPRbrC$w}4J=iYN0ez$UK$Wj&=&I~{=r=`& zh0~QJWLLA_Xlnn50_E3DDx(#Sd_VctI`HKz0S|qZSc72hmp) zXoVkyWppwWzE?w*J8`|hm;?(`xD<5{ep%7fa`E-x`F^)2OO6O z^3u}a!j*3BUHAUADWv^lQ!p<&4g>`qN+qi-e^r1>t*}}$Z>OkvAz~pf`c*zE-@k|< ztf)QIF!R*zr=x};Ds2P^xuOEzDrenE3~EE_jfjc(Z^RPppZ&6^l3;7pGYQ^|*qCC2VxN+kkt;goHT#GvFZUx%yi<==bS`nR6&Pj&3c33}2AoH=VF^NNl&o zo!AU{fsoa|%r*K;U;x6M4F8N$6;rQ;Mf_GLm%)ZDGDI!sT>O>i-Pt_CTCUpL$v(vx$O%~%+7 z^aqCcsG(M}4gmkgZ{A@{ZeLz*;9ZtVN^8DxCsdq@acOPxR9#VePr ze_2kRr1|hNMkV;;PY;)hul68|!@3er(8>ew0V zWs5_|-RdH0D!IFkb~r!T-NESYT~bK91=RqnAV+niyN5Ae7W{7PEi|ITbFQU>II&H0*<4Wag&XZM%WX?FfJ z4&}nk#Eu(ILU^yLeX#$$PZtF@xh1<6J&opB<`!y3SU!j>Lsl(W0Ci*Nd$6I%_ETd( zR91Q5M>W~rqd&tE8{i6c|-2wMqul=SVX8F|xt z>N7Ol+p7HX&cc{1!ZS~dNjOJ7EvYb<-7u!v9J*G>zmg?h0C%B1e@%QSi;%k5`pw0n z5?yB-m;DEaOZZ;8+_sDE`qzS(Oc>M+QW7b#TVRGY72+Hm}HE9*O2p|25v zGjm!+)eooybv(My3A)!Gdk5{|J?F6CzPyp<^#?XAEjmmGy>9LUd2sg=wiXd%Mk}E{ zUvZK0^6R9o&jnJnpT8?3En?SX*F!`&Dbs|yRW)0E%j)s|@zkR2ynIL-aMfps&2nj# zYKyvX2TP$xhM)f`m3NMq1f9MXXRO*!CI^4n=p$mMjeV$q?LXQJ1va%RZ)x6;5^O4m z8m|S* zWj^YjtE6mlV`Ia)v@s^pJ1)r$c;u#8I04j&O-(24P4C@nzx2}%-Vn&a2F!r#-FRV!=yOOV~9vR;#D=KZ_Xf$r$&Mzf63CII|? zf>b|GS{>64FnYe)s$j`>IUgpX_vDrSZdv{!Nm6ohEQ_WS-X~BCtn_O5jg(agy6epd z92j!~CjY(!ogDLLEAg9UcT7tDeNZE${c?S^3yo6us}@e8EhVV$K0d9|_6((UZz%Lu za>*BiFd;XKmmH|~7PKhAEYsViqJ;fhn55TABB_&NGe;0WcgzSfHYuAP;$8Fi$G1g6 zTD|)F=xHAqu>v#snQ2>sII0wLz{;+$eKpv2xUv)LCkdi2QS>yVPDrHonKAB z9z}=jJ}*5k$+lupV2zX*)PxDBZV~`p5j#HHrPs*XGyASbMgr3MnUgbTIW{Unw){v^ zsP7^+`*B7K=6CHq2Ex-C>2qA^x^&mOIliGUu-%>D^mzLLx5Vs4jyKct?;z6MT@Bw_ z4Ru3Ah#&v+sABi0cFg~1%&D3)w~hHPK}|%K`=d*yjL08@49;rZ3)t4N@GM3=HV0kSLZQ^mU(PX3h(^|Xx5__tdlXaM!?r=bXE32bNrCG@~1LZkNGOi4f&_M*P$B;Cy1tkUFIAMXDJRHsPJ z11YaSGPUm)Xse+zGBYd&tbW$zk-5{*>z|q${_>Z=yjlG&b~au&Ku^-QH;PkDxowJ# zV%AIYm-$2;Wtw8e3Ei~@PB*W+=u;YjPd9Yn>xeQ>_O@(|PQTS}NxU@s2w+}Yq$g== zrrk$%$z~!Jcf2QmD|PPRy^)#gYJ*H?L!(cP&zMOMx24t3xzPBe0(y}TOf%SQB>av# z(Z_HN`dePydn2tcUY_HIFh_IhWGV@OF`)nG1W*R3GiTN)y#1;FzFO0lk=7#(1D5W; z&Pm!ey>T zK-5`a%a24?GaekF$2yqy5qXo!eK|3}CE%Ixax`&)?$ukpc5r~~a=BM_5534|rB;ue z9F{7a*GR0eUfr|)pGkad(t>HN?fxl#mE&I!-3RorU zpWdY#&0)Y3HywZUa$M{ao;`~DMU(Z>spg^G_s@Qrhv4iWLImf5xKlf*eO?+nLtpV%pv3C%+R)Z;I(|3c9@jIQQKsps|bU zrQ22(a&pXjDy^1obuGH;#xtYy5P4M*i2`=cubzeO!N{Xd-5!f7k%>Zq6{yzMh>^jKX2{54z~Dq+?CU`6hcKy57QzGtAWf7@-3=c z++8p#Gd<;$qi4yRI&r)mE2d!rNQ?U%jvY^*a}_#(rWPtTe@uKnH1gFsq}O`>L9uo0pf1A!xut%q+t+8(AGOXmaW-uqp&f_QJA)i2ys2qwRO~GtO@!n zselcxpL6zIWQg}@tkTaq#yRA{SM!&`vAs11lW&I2KLrQgtV|ZnQ~J~pAd3R}6||{4 zgxnom9+ynWFcam(HkE*qy>HVK$LK_MG%t2bHfs1)UDHOoj^9vzG9Wxet-E*5kZLOn zafB9mwjPXAWrm0M^^M|w*&Xds%kr6XyUQySKW`mgO*l$wRo~q$FOZCjned$5sB#OK zYVsq^Tn$sd(0tx{sI5Jh1E>|M9(R363a&sMbSe!s*lj~2?PBz?1EH@UKu@!ecW@Wm zK266Y6FWa2&?Di~7 zf8rE;q27HseN!Gmg3^0O0f{}F{AD;0%OXj*O5!Mv#%d@A?Dip&rUye26t4p|R131z zcF|8+8qv0MZqQ{!O!iZEzuQKK+j+iH9qpgI?Cgxrw1^IN%T@WTw4RVah04%RT>&nL z*L~eRNKOwr(Ahe3!F$Sw4C!h!&(Q388Fxl+Ka_I&YwZ2yi;y7y%RmneO`R|GxIovE zgF6e$8&ZD5SUM8J-4VoP<5a9<4Sj_&xg}pWL@U+OJRsWN7OcD&%^|2E_eOfE6>nK0 z7#*5ImTNM?JnS{<<9~@4zv<@I2l@tc4`V2S2jDu#P@dUA(9np-*^bwVx37MrrMc6P z|FfhB5kEBi`FWake18geL%b=Du^e)F>Fu!!za|pu6a!*92hJ?ra|>?P-29-_)f!7y zmy^P6On>a&_}r~kYY_Mv&6QaPJPTUpX-kkM^S6EiwXb$sjiT?3>pBAJqIXMe0Ia2s z%iuqf9bn50ZY|dcEmn5y%t6+dvnL92^A|k0?sR=~7wn}g(x>}qC`i##}Ws7r=(g5p+v-9uO*|P3QpR+jNw(iiKtJ@{RBeeT5cs!+% zXS{CNszkDj5;~v#M<`8tvO8z*?05QSxCvE~@>?xt@RK4Todvhr37)ZE>*y!wv!%RT z5BKK1d3F%o+~qn)Gs8)z6JlBn)%k_5InFmV@&b&-Upzs&A4`a{(nX7Wr7}}Lss2k) zFX zi{a|j11-D(`v>2vXQ6jK{_zxh3wP8grq!tVj%>V)ketX_CG(f(5|4KZz@?^%+GGm9 znr`_uY{jB~u=QK<;goQaL417cK~Qzjk@h7^I^9p3=+KUPcG4MAAW#sK!RuwrEMGWOSQU)A={)HTkgq@%gKe$AGSBNj;a9X|?*rGh(S7~hs;LoVZ+;HTA2 zwbWzE&6kY=#UCOQUM=Rj0V@6Y+c!vzZX*#fCKG=|6At@H6MQOwSn6i^F|WiD16{t5 z*O^OYz-~q?$GhipMM7}_vK}nOW~Jg8;HEK~Hy77PQoYwKE`qlRGt5P-- zP_z3^jd1-Gd$d_3>39>$qeVU3z28-tBq?Y-Op|aaBO5$lW+RdMD4F-**e@U%1kX!N zk0&pB+C`IPSY+pXBTtL@iHGg{KMtpD0CXT!x(_~#)*%h_9p4Xoy~ayQL1e`ss2?tu z0RXHaQyaN;bq>^VDT56@`}?`9^!H+OO+Y0tz?XOh`}81vm3Q-3s-BWtVYf(YM2)hg zh!JS*DQYPF)sQ@wLD*iG@I!9NYoJVcat%5LTMnq1*xW6&D}D9ev{Rcn1Urx#R&XV_ zzix=`bRzy={n69%sq=@V`4)jdl6iUd`UcgfNwJIA80XC(KE{pkV{Rm(ay;MlwZ}%Iy>c zFk8SH!~L}b(|U+{4_TURth-eFc$P|eN-%AQ&t}>4Ej>fL(7fFbyvGz3neI>umqZz} z!(3pEhvh;Lv94H}uSm8*_>!n3`watrk8XL)WCZvh@GI{O7s^c7p@!mjp^Mz+ef&&znHg1DYCw!LConG z)T{^3cR#d^ZBFHmSv6qBQ!c3`x-Yu!AJ^=U)g?`CrVo%?u>B0bo6MhbFQ0Vo=wve6 zX+%Ox@a`rriRaYiDc*(7%jR3$Mlz&GpKEH<0+1|B(5Nsk=?uj*P19u2Xqu7>;y%bR z5zWvD%JL0=0z_#kXr06;w}ALzy6ua4*Wxarrgw)F#?n&G{4JaOylXdZU%o~xI3@?~ zcsZ;xHUquEA4|rQ%mWNuw}M=mRpBTWgQ)Y(_t_yKO|^9nqgj24&`*l~rc%-!45e2y za=EJ+xp#L;j4<49IntjOxAMr$wR0AZMjEO`#`5nS=PnCE;+#V~Q*_uhZm5zLzAw%w z*aq?@3zl18a_4;KAM8o^%i^f#v32=VDp9*F;Otm=#9lBAb zCINN$D9EEIRnxOyz6=b=Ic;s-kByqoH+EC!;3lH#KR?xBM}7kB)l^sIit(jt>aQw% zuEYY-mP7D$uo=kus&*XW^M9)Lob!8c-kcM6({d_KIUc7DhlJ841-vWtvx0Q~K6;!d zf%!_M%+5iS4RA98(?ncXQh}!bNCkA>Pul|A7Ef+b)>${igXt%$EV20y(pqklpYr&8y9W8WwLLKcvFnbN7qZ8+)!5 z-Ain2aq&3FVt?$z!JxJnneh(XWcA(~i-%=KfflZ=_9v!{3nnFrGh*!;Ed#rwKZ~mZ zj9~qww6dg?@_VAU*Hpe2}I>`p)6va(5*RAMtmAzn1yU%L^=&EN$O?m($IKP2l?4@U2o zCkvvmBHF08_n~C(`4*~;@WJ!kk9QG|?NgJK!?vu#sxSS)-yd@)NEL92Ob8)AWMw&UIlXliBeS&=kE_t?(F^Z|f zY~rHXh05D`n3f{gZNYC5fIaDmGM-vc!Q~C#HsWCRIx3@~^l+C46RG(_T`qw{2oZwH zSu3 zH8W%hI596Hn-|r*(g&ZV(-J_R`$R;w&y?JARt@=sO){NQ>LR1-ghA2EfLm!zfhac* z)B6F$?IHEOwr(9Rq=$^Uok;maWU{}Zy`i98JFIqwIqzh4x>MO7!W;$n&*PJx+neDQ z(CtGZ7RR$=HyArBz3dj(VU8oLd6s>{B>}+;E)O#A4bAo*KSA`5O0+5oJ*x|u9*rtr zpm!voBFuOoqvol}zD&7w;?3j!Hu>pIl~tt~f>Z3931I}q=tQ(aMHc{z#W*JD0_Ehy zhx!4QaatP)`-_HmC+*DJt}}ZmrR{qtK*tFu$Rpd{5VM1#Z`3e&NDbg$J}?73c43o7;a1Va?1mYf2gAq_Vd0cMQ=UwL~jp zX5Ap)8o_2nxE{I|>jl>PngpHltS1s2@s-|gaCaCsKX}u)ckZ)qIyryI1Y_@5pd4(l zjqt4NJoAwaabXZo~bD$=!q0}w5$ySUrN1pHndZzEoq+8G%X0@ z*a{5U&2wfIJ;}nQY@H%P53tSq-xau7IY3$4V~rwPK?}M=5_N6mtL@VwRRQn%{uQ_A z{U7MN9~2!&?$-?H&Iv|Z#~!JamdzLh0ZQ#8R&Tnm&Wd;MUZJruq%kCR*;nYaQqTVb zoi4i!?COzWf2G_b|6|`uFx?+Rwwo`?cJ&Fne>DA0c;gpLjeVFvN9;&O244&&p11aj zckv+t1_^0KMGkD`0w5G}Eb{>!CfY(>H!7f!XObuS%yAf{CaAj%Mk9x9cNz4eQ|-aOe+>B6TUz1IcD}c7c6Sx_ zQjXjjVS6y&Y;|0k7D`3%U(4^AH(0&pp#%fI;aTg=pR#?Lz&-unutA9JNU^XsNLXD! zT2bk?yYE%;z{ai}bS?Tp+)$i9qgFtWYz;$&GoO9IeQs3~eu<6fwdvXbnR}}>prz_R z%;2_)?(M>_>+N^$BPHc$*a*r-K{J8pu0uoOU@>HV-$$E){PsHsXfqU^`OetcW9yV9 z#2>mwfmJVWW*f zLQmYgi)t9inX@)dj*C?{F?1adcvyW6jqcB*Bv_;{NzCcMT-lr#H6)hy{k3udzAtfU zA;XbYgAXnmRz#M%^enA!k7}M=2mp2ux@^DLe7`hMdvqZN3V2z@Dyu3VvJ4|xgPDQE`eh9h^oHJEU>?n zB|w9_jOq&8^%RFex5Sw|R05pI6Aaid$6v=D>yO5|3)rf!;=*=@ELF_T@7hH1Y9NG$ zi9L|PuE0Z;v1sp2bN#25YAyK;b#{*a!jJB*#pq=MC~`nLrHI7d>QRCalf1Wb4tlIp zgk%d}+i%VKxvSb0zy^_US(x4aD53kc`70KqifM@_6>rUTB9Y%6j4pl~A4O|Sy;7;) zHtSja0q{9z(>{7PbH!l*iKcChudI~BB{e)_s@DdTxj?`3A z7^3c|s$ie-!*{geYEzKZ{-lGW0+Q$Zz_Zl-nN44k_e89c8G2>yi*y1vDS11xEfK8u6c0 zhX@B6Z67>q-`~58m1JT5F%{p;!GiYB;)TsY>6Ri(y95DaAahOlOPeYa}Lb0@A?gnxNxrZw2n`Lu_S=9o;zF3ZsxN_O54)& z%qMIGJ>g0Ty0sC0Qw8Q5=)s^Eeo+VfmmyTX@uExq}n(a=_!&k=ChvB%dJ;E+PQ zs?75ot6A-vY^!}!sz|8WIM{0_vw&`df61QAzE-A-V)hjGvLCX8OMWTQA+a^J>=)v4 z*5PXI=O~j5H(#hUPANm3kdAb4=FCIuQz?`lS_T`10>ECDK0wIu``4*^-U#91`lT&e z5!}xbtf|ox&KjHBBeH?$!D24G4Xt#08bSVcb_wH(jd&oy*0veJK)C?eLWRV+lywZE ztl?&X4qU=VTHi>!J45ulN=Ld8QC|FXmPrB(H%HDWj=o!Br-2$)GCwO1YTAgDSfSX- z2Cnb)(p3YN?_({zGv8TU1ZEw&#WdXY;AdJznoRF`lwHL1lBST!_%4F1@2X!njnLkzOYcqFl7s%w<=`js7N>XYq1uj)2ZuYj|0<%VqZs1xVpnKS-rgGOm zf|#aSUxz>cZTKXRg}n0jM%s*d)^9B%?KR2#$Rp3E&*6lp)uE?2(IHpC*`A;Z!(pLk zL{XSuz+=DRv)^U6I#?J8=hT^LjZ zXa%y<$NSg_y_jKXA(AuP=ok%Xe${_QmwEf%SzDYUVGRWZ+A!FekgWol1!nN53d}l4 zBMyOUOA=CjM74SCm(+CuC;fsaS`tz&RB>XL#czU(e>a0 z&r5`yf~Hmj6qL=y>BNI^>j{W*)P@qW`DPnabLn9<=qJI$dsrmGlV!yGlAkI(s1=fv zuD4;${1Y{@jA?453n3r1g7Fw6Ce(BzLYmrkYwoERAite2or&2d@c$(^eLTc8UMPgc z$ZOk2i9H|k^RlQP&<*O}x$N#9Q257Yf=Jm9jFIlnnfu;Ep@YNq0cFa)lQ%*wgJrFz zOkG^kAMGZKL+!!4TRyANKcZ77qf`57ezUN^kWCRi&3zd+l^&`l&@eKPV~?<=wd@H* zvAi6fxosE0vdMWF&Y^6JoAotkyE~9wdwqOPb?Y8fe}RdqrFGNA2`X3g(Zao8n%N<-9iTnD$=j6<3&l;5Ai&t~GNj0(D8w1sF4H|m8l;(tC;9CWEgMQI@Hl1buQJzn7GI?1um1RNqiuCKq z?IvwL#FgrYKRmwJ&6i$y!0ZiNcul@k7MZ2re*UC+QOTsqgfkYBk?H7^dtaNI5EUCE z?~Ws%pj(AubcBaIp6tG!h%I$*HtDhW|M+_Ea5fva|6eC5XRuM^w(b}~~QF~K0BGlf3Ac(#9y1$p+_x*W3&vX2a-*Na`T!~!ceZJ4vt0AbN zO>d=7d~7AV48<6F5U4c0!8YZ%_);Rev$`cvaIPO+BHyGsl9S-CbG8^Et2MCL}Vj}t{I`^mUEjV2^_vx&=Dyde8UVC{?y z@Ff_u-_@=T0DMwtoqLpv$!{j%_uXF^7ChcmcNcl;XRPZ6S()Xr>c7+w>kHh-7SK$# z5@Q$Mc$NG=nuLCm_IP@Tn^ILT?gdOArO+Wg9MUkhRT~ zy_&*BFwF(V$BY2!&N0D)Kgl zqDv|A*ZaZ%BHH_8r*9w{NztrA9V-tWT)WJ=TAvQY8?kg-*d zY25v>^^HZu&#D&?Cl<%d(9Ik;n8jPM80mHnp$U=;-}#)gR75=*MQ!dT}~U z%7)k|1^N)?NQQQskB4V|w(5LV$!n1zB1*ER=&Pum1`10!4h+J9p*MB&@GK z32%j&WJwWq0dR2#NIGQR%~~NBeYCFTQ_FryD$mc4c{Ca%y~U7G*5Xh16R|suAG@be ztXvRi_7dq*6NWCAN~LrN43`P3Zx>hP!;wE*0`|9>_k6{j&jz6{$cTJm*4gM9`FyHX;=l^Q)*ob7@JaOw#&=-Z&Y8il_U+xgH?d}zUzlEG`qn)>Bo7}swMq7+B!Sne zydI!hyeEbP4SmPOKQPI0jJ&NaaSvl$J{8Dl?Vwg>9 zX6|q=BU3h-kxGb`ZtukQ_)0aD#BnrsTg@mnqr#U$EQw%?v3lF_9q~Nf<(*6TcFnF? zOt}qA(a?Em*ECGWu`1@^{tQlF%%l1jmKl)SO2o@LTx*JW7G1dES;q>5$?3BPDy6Aw z!;BPg<)DYPz9U1}RpP@@t_HyRv35Ud4{*_4#-nRYl3VZfiF@2%OxzpU4wtO-nIZ9Nq{r z9{V2fc>g!rcC(c3XU*1Ba&(yHev|Mn#Lv(Clyz>3HoL|mVdOa~A{6wr%gC!j=yV8u zFe;pxsI6Fe{KC6dsK0IT(ZNKQ8bO%Lu3Nwm|?{M=8_bQcDJf@S+t_y zZ6EV^>Ma~S8O~6RT7U+I9@2gC7ddYHu`cM!!Dn%^{SLrB1e>U_m*e`V>gQEC)Fu=z zq)8E~S|>tz?@7q&1M1wHZ{7Rpg?&X^v@DjnT;4cC5;BlLrF3t{+Io5D`_lWetyJHc z+E+1eM|+o*c=X6^6g(h@#r91>UB#Yg@}PDbRA8w?`nTp6_qhA4lnmLM;nh?XcNdoZ zz^MgMz`ADODAdFecY0Yz`Zz)WV>O>PCg)xJD<{JkK@%r18sXev2lXEXlwqlvGOJ zyZ5;U_RdxOg;#xQoW@VLX?f6FGF9kYeV8sKL{ISE2v0*iZF=|{_5(&&PO`w$=9P=Zy^<}3jV?~?-AL* zXg>Xt2^MB0`t&QeN1#c~=J;au`H|ZVut$rt6^EJ`T#lSr&7s!OEOY{!6Vi0=%K+PgZ-v~+SUZXrB{&u!)v+n)-!6mIeeJEoqQ8%!m@)9^tDvROBevAGt zb+iJ4NRQV@D2P{~NMb7T2W} zTae^@vicy2HCsXr07M6G^4Qev%~$fv#$@<#Y0?e^I!aNpQ`OBK50?%;Pt-No$@`J; z>ed)>kk%b5q_cMC-ad_huz~@8M8JKu=Qd0S*G~0@v^0b{B5+mj!(kjF94EP@`H%EI z)+f{DM05Y(yjurcG+yN>iGQ8LZlr^g5f=@W?T15BdQVJfC#5$n($A5em0H&^Y0ttW zl*}RC>*tB_d|W&Z%KRK|_>azWsgeao^nOMtnQ+YZKRR=>vc9&so>=!FRmg>(@78RS zv1terPF@3;w@=aI7yL4AG0%V0b_rzngLcmMpH41EN3Dhmcrs-E6lXs@X_TWhH(w4Z zx7-Dcw5AsOLl5HT1C|u!DS)!%mGx%m7m*Yx#C^BDk7myL;f+YKo) z_}f*R+gli_%>8AaP*$%Td-=9Sg-cxnu-^3S9AUMgjGx=#AcDxtt#iPT6}0kAGxG*h z_)^TR$?GLf#5DD*-mZNN3?1AWue55O&7Ii=%n%Bjt4ENiKO4fS;|oZWVII8}F4Li` zcv^IE7ndUa_Ip{RBZJWPVtbf#LvrRojEyC9Q|hk4%yJVL+~hrq%MM_gc69$GEKr;5 z<9c9`Aj@;JGHTq-&Fk1Bse1qXGram-4pMLZxXX(vO4+XYs zM1e`Cs|_ub+nR6RYXG|6`&L{^P8o0|5Lw)U$1%s0mDz{56u-L-=taNS!vV3V(w-W5 zGG^50%u@O8xA;kKO><6f|AjX~zbQ{HH^@V|lq|(Oa_h)6`@F*5cLbp2^{HJF+kUc& z&Sji=Uv+*?d!wq{PAf9@Yl9V`yGBRR7b1{b5;{*Y9Q4{B+wb>N{3h+*C`Slnb(qu3 zvQGi>MI?`JO^2bML8-5sHSBf%uaQFs>SMa%vc_kGZi_Q-5IjQUto0F4WgWf&*=O+) zoCRnQdDKiF=_;si6Jq?#^DXd+s22b7^%LUh@*!lAbE7~p70GPlxuMgmzLvHQTQv4m zw#Uc0EWMYAN&c`gt z{)z9!vq)wBfu;Sl(zyi>CPd|~`VJ9=*@6FB$TCZZm*XYCJ1D%U9aBCI9A+#XcP!t? zjDjgbi^#}+@k<+T=pkV=?jjq#j)$uTWe*5rbP6Q!#PUshPVG4afDgf+fj6`jYzAuE ziZA%}smRdGn&z_FD0(j~8tnVSP*i_-A??cEMbF;KqSaEDiGwej#OG~`1MZcx^Vl=h&EE1ygkRmbZSZo#fK}q^ z)VIfiw}q{)H8qOP?F7)?Jex?{d^Z|3G^Z3%Hz-l3c-yZOgQ{k@1IN0rw1h zf5;eX;O+Mv7H^cF_K<(nWA_F&B8oVtxO*R8lPjv@*xyQL1$&qOB6Rq)?dliEsC5fQ z#{2RC(|b;upFll;n*n*#ygUXYVQ4gVFeKaci<`Kc*^b`^jX|#D&v9?yKFVZurY)J% z(0A)C&MpAC^alis98dan;9L=yI^#1mJ0(vY&(8E%V>>L$Fvg{qhb89jtDFXdh0~+| z^Cy=?NiZkg97lqH`1{eq;;}Sl(@oN$oA1+K>LT01d-!<(~~A*#3sw z0$zdTyB3fyi*X;Xsp6$HiK|nk%Ea!Je~@yC_W~Zt`Ln|uBe}q_S#fuw;P~!XVDH3I z|D|NqAYc{fOHm;B)5dEHXZ(TznL5KRQf`*6bnPq9-V%DXFfyLeWkBe5p6{2z#SQY$teote&g4RB_8tLV@3W!p6R|{qQd|0myIfnW{vz3R z->TCZt^`=U4X?r#Jn~c6`ilvE-mJY7I!p>lkZUUSEw_76CLTvgE;^uEQ;E%;7XfMtEY>hSWZ~I$6Z1vCBk1 zL~q$0UR2t^XrYc>E3~wJ{y>o{YBV^H&saB`@H&tHoF=ujM|fA$@CY?@M|7iwcj=nR?m z(=PhKgsZe>Jo_r5^bjCgz_fbrNX3$9;x)=tYy`w}&KcdgX{UW;k&_`%5b zpq)Vlts`uoYKk#_>n589#B*O6>mG%ipW8q#ld7f`tWvo^-M@F@Oal>HXRBqAzA9Ca zh^L9dLsL^r6gn^0R|`bk&iyM)S{bW&U!(32TG`!a0PRi9ALNHw)2YEvzkJ=z@pf~Y z&UN2337$D{@}h>;WE1vZ=O9Dh(_ZP@-6-&`uwBZ|(!1gSU;&oP$K;fAs^n5R!ruox zl0)>$ix;WBb|UdiidT?lZ0*^qoUN!vvMiAeHto)-*Fe@jN=+#bM4yS=G@3x7?G3W1%7y=%^Z94~4ezcjq%ZGD||M_HRlXN26{* zaGN!lLeD#cJ1AWllNp=g+1mnovcL}Vf_kDnYOzaK* zM4`wk)YTxsqWSIRRrao*2NE|1l*s4eWO8#c&X1%%@Y7vV59=nRLU3AeSQ z=%?)WM?Ft{+(_b8%055VYdP#Eu1_7yLPofwc$&qpT}XAKVfZHOIaC*eO)0|2{ee9hd=mlQY*$>LpC%*U?op)Xt z^|r-0$hT;UQCd8o#X_g1u)WbN?r!HBq!eee+Sg0AOm{GbQk+k%1r$p-)P5ho(}pos zYw%d;e&-Z_)WD96xbVmgdgS`+a1O?3iC2sn+c;ZI={R$)h?9O6;@Z}HVNWhtfSW4i z4yEYHf_sJ4pL+6hb}%suZ|vJ|9-TSX@nAXZq~eHtjxRitl18ej^YYI0+oRy(&3$Nt z6COFrc{OmJQ+-h&qef(Ou=GViCGE!5RqFc`S^&mW1!3SpM;pah`Ll9r_ie7}Jt9PO zw*P&ZN*hbLw|sVI^Fq(Ia(*k3Le&T3KteeC)A?2q)zT0EBf;~LV4qjn$r?!WO zAp?5DW)`gOqFfi07JN=bR(7Pc;-#riBsM0tu=|@fcO4kxWvoeNwZmW@K%cy?3@MW- zl9?2c5a^uLKhA3r?DjX$=ARit$3Hw9$nt$C^?$2@FXJ-aoSDI&sNK&uEH%BSs=Q%yM>=p)p%uC?EnjkzPf;+DIE}JL2bUh8HdpCShdvSmapr2fv zat@5ZbWfINXmO8oUe9&GMbLqcSGb)#CqlKp5=Jp^V*s1#hqYrbYRLHENqIB^*4vQ2 zzYQ%~f=Nei3brUJRlNG%j>e+rrut~dn6hunxc;)@Ev*fR4cNE2SOOUkeW$<8-iH+L zmXc>j2nbmtDXiQcS_^qFfzva7-jWdax8D+Ib2qmJ`;>MFwY8Wq+om9^SabEv9htIt2=dQJBZ2V*loYZ-++0}%gjZ+e!u!(AZ= zGR-=rbR`Gh?c8&li&0yVI~L3*JUZY7&U=f^{iZ`r<%B-WaHc%vERk40JKqrQQFczo zZX_|Czmnr)TQL-VwXiU|<)f6;^T5$i)KFAxGh4|u92}S7j}q04m4n$=uiAivjhf?! zebh`a-5UBF1G$L-*&l@nD4(dlXxN4$el_P>8yY4!UpMVoj8~bsjlDT5d?B8I)~7c5 zN_b3Yk7q(eE3>$}oWB6&ZTr#{x>g5t0W+O5DvG;k$`Rn*dxTOStIoYdR`pBg+svcg z;|f6E)L+CJK`ltHzLWcCOh*kTb z+;q6>4--pd(EcT--2<#&yh~*-p<7&~E<NdU-{*e_G)5t9uy_Ny^-GI#f|j6)3&9^sJnB_sPpEWaw{QaGPYD zt2)Xu$s=d=BynN$)#=?6G7_l{Rk04D5p+;(T~=!J!Ts=aEg$ zk&_kJPWB$^>1HGR@M-s(^~u4A62DBYjDN#71hxO_*)skRV~osUpOk<_8Y2b?U)diB z6*ffNmQDE5mw4tQuEx)zpC2!uAw4~xxRVb}_WY{%n3K zN^eWimY@{D#%_i2!Wn)A&>WNu&)3H}{q&CdhxR*v7-wBn|9;pDNX}1Pi@#W_YYF2% z9FdCeEj}lmXO!`Kbk*7a)4f|^6l#++5d&@|sch%^U@q;o!S81aR+<479aliIO=Iy8 z`d!%_p(4p!oZJ2lYl>FEx>ee=gkNc7ul-Dwrcf@)H;K$lvOOuHgb(e=J!gIz{X4bT zG~JUJm+{Ghi+;$l@MR%zPcAhny@v^Sc9Q&J7S)Xye*2%Ko6pr(n&L5e``+2b$#D3k z3)yP)(v@vqp}>M(`!77A!0f}JMgqyal-?UBYf-3bXNLpE$Bz}Y9?Gq{pM9h|%0k?H zOh?|gl^1DC+~;%Vk{yD_gTN4=R#Yc1C=b?GWb$i(4<*r6CGx9SJ`J4f+i#lm`bNM{T04IEE{tWm zx@?sMYRl^yijrq!@`Ca)x?_KPRSTX`As^!Nc`2`%jsm|3c_r7RmnE23+)s!ypXt~t~ z!(<*c@RCkTKawOlThXgR?^Aq>rpWG zrWYkE#pCO4KhE5d2mSx^T2)T!Q#x^vu`XiWyRjwP_Y%ky9>Ue{NKf@gw<3IMV4}tB zxXy1BQg<^Kh-^x3SHco4==A|f($&#V>_2P6&-fkeS@TJe%q5&Ke`jPuXnhT&rKj~4 z2Hj4Jba{Ds_d*2NC>_gNYEN)&>ZU1?v?Mqy7`e3bF3HaAC~s5;Mj831{K`I6(V?ar z-4AEa-RJgH#j6_0t&b6d)%v;O*o!YpZtlKi4AJQY8_xD(bT1bD?RRqg7vSpqHV=4t zBhw(MRs3zQ-Ss3Liw=d=Rj@|ENEF2q3J&!)Y$}-Z z!y6ktw>_FEs>fuxS(;s|!G_^Aj&)T}U)+$uS{5@BPKD{WQV#16<|6~2(HSc}7lfgsJG5m)d4!HAflJ{DwY6w}peg-hmJXv*Fjc zV^7@Q0uIQe$#zQ1&auUC`#>?`G5NXu9MZLe+Zr8*&#T1cjFG@S9bt+`4r&g_!R-CpY;6-qVE|aTT8*@|P z-WU#d?29{1qw6!!?picZQ&~8wIxta=1pTz z+)MI-Q36Ju2N1Zpv?@%COmaRgNQ>Y)zvB@{X`w?W%@fhWjx=<{f%2BRg%(>$#HI^C zXdr2q=NzzZDHI`N)=QCcQ$O4tBS)*=Ie5|_T5;V?yTv8|7O&oOUHO+C=|dwR$zIj} z@=;L}-2VVNo)v^>JmH7@5TEu6csn!oRo}(Om!-Jmmjj z{g&izFu(KpQwR_zpmqO=t_%JXT^p%rJ<|mn+`4rv7El~w=oF zbB+Gnj#>oRQOSIUXKym6`k`VtW=3!T?x^NpR`~ic_GtKldd7q9>josB{w)Px=i~W@ zi1m+I!#Mrvw2Su!Mhi))6r*Bs>t6QLUzb$JXRDGrJLcd?J0&{7`|Qqq+k^=p($QMerBp<-_I?Cu?S zP*}Y&57!@$^m_^{Yp1aNWV&fJc16n)*&7F%AFmi6ZGqUr3HB&-_lz+KXKTVek^()? z=Y8H}grXfmC`nH1~wiFpKY{^q$mWwrF-*+zkIMN(1nc+;1n z4(*x*3iC^IW377KAAs3KQNtkLieTqZ#12~D;Ef~Gs)f)9I`3f*Mu?6Wc{JK&$WChk z1UCSiFVI4Z`gw29nmkwF4@c1S+~@}W`Cm2KUs9ogpEuCzV^D2fgnQNx{4Scb0_MN) zz#zjfmX$T#J-`v_9ZV`G5-q0fwd4n<3R;z6Lv4mUPDEKp8V!d^-+9_tPC4K0X|{s)Ct!iS-iW0F)-|uqt3VLqf{>0lgmmcw40l76loZm zOHXM6eev8mGK}$HAYpu+?RlfBp4V#cBczFit~y?^u*9ublMa%Hp(?)(N-F9QZDIg4 zzy#1vG}1$YUy@znodn$1I{=_(KikMBxi)J6DS3Cg7N_Ma%7T6+Sp64XST$YPkJ%|_ zFNz!|MR^t>+1R^TWVH^$Kv3jf0y$8jF8586YVSsf z8Gemw~WfZ8aBxIh##k%L5*cjdrk&{dt1ez;;DKrnOiJ^TQ0lpu}U&K>UmY2hah>s^f2(Q4Hu=<+AoUSC#L+ zf%3{{u~Ljb)kY><^0N`cL6*wkmOBu<8_pKkt-N_c7@>El!Q=U5B5&MQPB>B!^5&*8G*k)@<@H@B0 zhxi}V$ZGlVs{e1v+~`lq+&8%~ZF@wx?=>``AiCHv7l&MXmt2PZt@Axr?OCHZam=}m zlH*&_NMAx|=I8Uz3#*%P7XgzD6q%#XW8myuxFLP=c*7rSmczY>zJ);lKhuHRYLc$O zp+f&6g&b>XH<$qkY zX|iVJoQd7NMNR$8pOwukd#eKO(CoC1*s$A3V(%r^=q)tFrL zFzWy)+lDlfzc@9?ISqC?ArO}tm(8j&878M7r@ss6;_<__E~#LSsTigrnkL`B@NnGQ z;%@mD>SG4JMhKpTc;V7?_O0o;lMA2@p?k#B*+&iI4Ha-T%DN&NT)_Nm<(Y)NJBe7$8C1DEcmeoN9eSq>!A_+&f9`Ue%H+ zr_j=xs><8({^DG`7$&wHhnV>K{O_X+1da}Ua*;w_KP`!l>4xXh#}FGJZOH@0d}lHd zL%6C|Mgc|^er*AgawKaYE%?mFvF&1+j8aignMfXSFhY}Et}|qt{`IQQtMxvYe?rZt zxbMXyV9CVlFg0mj{z+J1Zr}@G0*L7B(81Ggo!;8Jc>BYN&`BZdv!nDcTK@a26YMqh zGQ1Y?cfK>JfB z9W&bCE6OaUWaMA5GFXzMMZa_#3#&xwVJ@bV3e zhEZ>KRPcC%pY6A&f9mAozEvm?i(fSTgQ|XMoQ@f~_C}VJ>LY;sVr;;&|H+ZLN_zwC zH?mE$r6+BhYL5M{l6i)!!yMchbVBA%6jygwBeOEEu74^fl#K*Ij(!h^3}9bjkqgsj zpz?)+{jb}tkvFlR7#&@yBBC2wJj#<#1@xH>)bNhHFkf_?}`OAH?wa(>+_Vv&2!WdieDrkLxr5gYN5Gn~(5^VvN z08{_MqbtqFwt__be0Zd^V+f9NE+K)*b;aC$^}W6ni|kLI4A6lZgY;DS5|4P_If|R@ zbW!ZQ1r`P;iM*@+!b7Tl6p6y9L4>fJ-3(PB)0V~nt1Sp9<~A)RNd+s)Yl zbz?1|LbHK{au484csJmC)muem0%zRuI(6JtsUe{*(DFr?j`KuFm>7Be4))a&=H_ej zCyopHLR}nKK%yhkc!|FL-?@3(|IW?zYLIfe!bbzSNy9FV^0YcA*7uQiV_w*5F9VS@ z&8auZ-1Gi54E?)1(|!SQ>9^^_S1l(?g{6UmFU;~yO2*!enEso7XD=wwlNl~=fn>6{ z$?QYP2WkX*vFJQG?PTr)BeTahAs3fpIr#V~Z z1(bfp5};qq1kDMqodqkdFKP< zf;Rl4ldSntCzH%J@%fxdmb=K7QbN9CweWP)OO2U1-Q@fmd{N-tTaV~AGD{rLw2Xsi3~)aOx%%?)Kn{Y@JhbaIcRYn7$d2!iyQ+B>^gT;lp3R>$dU zC1rbJuDU`~fJmaB29G);+55ZRzKxfG#p#c4E1GvFOez$4+#jJ0+a!lX>$z^2&% zAa5k_Lln~-<>!=v-`Aw3h(NWxZi&VjoshLyLseuQ~i*EFSaO=gKXE}8)xwr znds8wled;!#b=`JB~;W5yd4W4H2ei;x~c;lFj>Mh*ygpGu0ht$v9MvXaizzHYL4<3Mn;2bF<+hWHlN2|F)wRbJ%@JFt zFT+zLxEBe(EOu7BNMeBLxu~kVwX}Q>VHNYU%e4Zo7nz}&M5G_L&W}eA)s>NT(O~fv zg}9)lCUrXh8};agjvN~Wb4|ueI(TLwi>zjZ$Lt#=a+3PyoNqkBS2qGz!rE_ylsx7r z2Fp0PJ6-r;y@k`W?VneFJ&;-0xu~?qTUjr2{w@`}foTSGRkhX+Rh5<+@VO$B-7Nq} z*G~W#t9nC-r+bLvskj!f2!0J1bmS6cL(TbuFaS8^aU?ndtP*GQH(;W$HH;CWg=%vo zIJ!$mRp*mQIFKGqeb0LXcVep8(Q<&(eEanGcW<}Ny~n?b`g7Z9^}T7Cjq?Xp03#94T>hiSJ-E`w3L#jV718EODFU}?)N z?I7DQ$D5%9%2#dsdVh7aIxE@u5o0Vj=IqS?UFG4Zr>QZF57z(2(#C3hZr#h*zw&Es z59DO-T<%Rvy*HeeIE4*i=)R`EtKd0)nq`V}0Q+8^dyfjJOqAESIq~F`aL8L%2kBD& zyTj~p)?f?OMiOeHRDm#}S>_1Mw9#Xe{YfZ_?t$4VzzcYLyl4dM* zZ}_>l5J%a{?t$UcIU6;J(^Y*e1x&i7JhCvz(gs-2xoX%*2vIE$vKBc#D_e>jsUdNn+TRxeH*XRy=#=uYfdz(EFm zP4_h@D_LtS?z~8|z4(*sk?b6yBG|q3-<1K2Vy;J5D+9pk3I_h%b&ZTv_;EE*@xE^; z-bJx{TLBWOhtXXti|IqYy>mIpKKRhV#KrUs2X_&5RnzjG~HmEHz2q5hpz(qtj>lC-;F5W1yhp( z5O7t~7sSp?iof(`N~Dku&F5`jWW14r@XbK=oU(YPL)f}sM=^RU47yB_1 zig9B!0wtH1r$yatipbCL{1W8N{R!6VLxnF^Un^2AAkZh6jqjjW{xqei+YKes2W1*k zk+<6}1BcY%plTi%LRpadj#Snc_LE2Bo+Mg47EYp5y+E;vi zNjjt=exhA*K_O0;#Hl)C3(9?PA8C~TA;Zm@7;ZyO?_9G>MuN- zLJm84P*S67eV^XBaPOwDBxtMwo8XFb0(}O=;y1uYXcgs9~|~f)YREm|HAv| z>nt&G$*_-gt!mrw3Zb;1U}6*x-gH$>E3XW~&Mn&wbkdId{5uVK`jA>FUrrg(9cYSJd0z`|dbay!gc&ollyS z#&P%`G>J8UCh4tK_*GJ_Enc+XM6F*F?Bf#%ph>L%ph=Q}o_c`+hq8RcPJTFo0{NBZ zlAm4EGEWonr<=}i^mjGAqD9qIv}@gNghyuOsc}={X8}IBhf0NF{A3T;B<~0Al%T1TE<%8O`edmeSJ~azFOt<_6QAGc~Zh|H}Q~zZ&V-Y9PJuRK#cX z-OFG2V65kblMp)l{9lc9?f;d@pLgEyD@yjGx}M}F1H9+hm(q@kegs+B^G){+Zv#m@ zLzxc({b6URlCU0uiEQiq#L<4~QY6;&f@ZZ(78@ zGw;x*lAAmRc;ON{a>-t-dAcOFIX};?+$(7d(f>L^y?vXAbBZTs<1LY&BI6zUlRy3% zsjf$wkF^xhRH5x7os({2tEa8Zh1`%&Z}w6&la)MsU3YWTEfH=7idpZ94kNRPnZ2nE zG#z@=EfXpM1IQgkxgw`Qcy({ZVQNCfv^3d6Luk?oVYGzEqxTgh1c%Rpf+1)F$MSA6 zgSY!->DnKpSCd(H-)gI_gfQ(#5SpOidqjGn*Zx6!&0QHq|7Qs-uRWNMpj=Hqc|esb zR;B(s78X+fqMlCg%x)27Zf-$;hZf6pBaySYx~Hy=$y`TCD7P7-(GEwy@6S}a&o^?} zcrtu;W9Edq$fp6Cs+Bz`+IyRs!pFCi`(3L1()ltZVw1`Eu8Y@O%kPo9G?%6){la!_ zj_l>9FH1j+Ar#^%!?a(1(A`1H;R)a!tM5hK`5-5;yGxJh4s4429F6E3@N3@7s%N3g zB4(%$2VFlDsngG)QGJE)1Psq$ugautRnrD5A$NK36JlV1vixkdFO&cD<1eP-pg90e zF#qw8mVJotv4@=@MkC&EjT>2@d57aQu`_Y&B%p@imnCP_F=f%W&|{Us5mJY8VQFgm zx+1)`&*2@hMY1*`@-4s)qVb)|z9a}1rkP`a2bC?aGnlxCgUvWcc%kMtJxib*itBa<9Nli;pooh%)KA_%MVdO8( zUbXlk?7Vs!YiJJ5Nuk9Ti6Yd>6Kxv!{TdBGBsv~`!Upl77&vcz3$WezcIrLbJcKYN z!Vdat6Bl56QmOs-%c5~yQY1POvg{eljSi>Qv#M2W@~j0;jZ=-5fnjam?#Wg+)EsB7 z9t~f7RUCj*3z&%P7lCrKPt}~YyGqY%u3olwoz^sWD|@vVEFAbA{lIn3B)@G*%NM;= z@8{1y8o=CLy&vdKON5|)(i--NK#tS6a{i=5%;OhlA!}R}hILFcp5x(;VF0cXnY>pm z?6Dqd8ALKm!dLORegUF)(i>lU{*`yub!nq*O;mhGKwWizDoLKaIfetInH=8n+)z=2 zRM$kPU~yEy=yJ*L>kDpdyljlzO$+CrE}pAE1!rl+pA@fgyaHNAx_{we zFnXvYPV^f;wjKADymrh&rVsHuGWp^>Y#gp^^`CMETLDnc*ran7<1^#Gx(z~ZVNUHh z$XFLSZFKIP0l)>ir;b`L828sTze6?Wu4N6ZJ>0L+T&W=Smh(G34g5IE}MU?^**L{SRsq( zlmYBI?$U5+=Xi!0l9qnT#PZPBk1@cMD01?_l*$pWuV&pHQ*kHT;gb5rl{Zti7orw+ z)m#3tZ=h~{)NBVblDW-d#;>r4ltLlUppbvyC~Jmf5O2$$P!`>)6vaOJ1z_H!vnlW; zi>EaqH@`Tc^>8eg_^F9;vM>aahF<{<3ZZ( zX%whR-X)S&b1cg96#y3fgoP}gX_&srnm%8-k*WeYNg zFJ-@>mVYc|0Zn`YoNXa*NUs;45NBrmaLSBIxk0_Un0t=Z4b|_~D|cwH#csShe>hdj zpiIsdg`w%gX!CZdd=rWQ^^b{>?r-J2fkDbY>9Z--7zT)W*b~o9bX|0Q(^6AvqJv$W z`B#4H)eQm~`=B_>|7q+m=)Y9{)7VF0bP#o?M9*SLpOrY#Y+UhB_t&Z-{uXW_g}}Y&0#eNHp;sl5s=0jB~An0 zg5%k0L^WE^`ME>6IZOe?4E_wV+huz)JtqEa1*2n*rsTF#$>&${p&}AXIiT6kM3COz zsp8j}PQy`|mfmL_m&SMM(yxn=7>v+5JP|&-*canO!+f$S3+Kw{wHT6R%;4L`G7{B|!CAgrM+1S>h>FDAiE>ahfRsYzC~St%2Z~dX zv$WoJ_`VAR9R)0j-RNury^)cfUMA%L^~@})~`iTlOwYmtO$NI*hvILNY0PR_yYZi?sCVFDl~(L=jb zgt`Sagz$(2$*eeR{a9w@fpUOJ;6cHWJ2Tw@qtdkr5+9syy9Gi_!OBrSw6@4JI$pSy z>6j7t{+tGGX(|>JJxo#+8SjsO75$0FB1H6=oiiY8L$8;kHeR<3NOXf&vbX72*D~M8 z85;m-tfhj)KS7_t#4aj9URBmdO}7Mp1*flk)2a81+nW)daBT3d#gr5Crjio4NE^Fh zmS5iePdlFMsvU1EgZz`QTi{ht`I1{>XOVjfIh3BA@`8RmMB9?pJf^3)9MtB~0 zf}jz&A-0uE3vx$oOrBX=uO9MSWh-PFRw0v1u{{*rWel3_JLgf-7AxgbxFa*bS`I!g zzE>Vuu-8N9Z?_?Ok8qCCx*L>-DZfYL_sk31QQ6Z((AFk_?wmDiF$D$oX6TE}o2Y$; zGg~;f8{)ODJeQ{3V*1GjrM;1=SARHrwwF0~g*qa8eyFsv%ywaa$^X(>6TKl%EA!uR zI~<7Hfj`^G<#Pq3S$F63Wwpt39hhfsG+IEAdYxK+*RuX`Fu;i)Ep>la?B7aCVFsO4 zI8yU%&b5$4GMWAF)cp@_P*xJZSu9`-=)d!aGSeO!#JGlfcUU~7F5aT+9A;QBqe$C` z`KKeF*Wv9F?M4#qtndkP;&)Ly*iLC(dyT|0-V@g{ei7)X)nga(%-NaHBl=B6V+L_h z*S6=d={tXJt_P0qf)Y~g{~up(;n!sU_WxspA_z)4K^mk4sgWw((jg&?kY*swK&5M= zB&3uMY3Z1VY>Ww$juFDZ(MUJf@5Jl+#QnV=_wOGNfpMPi_whQO&)1nHUau<>Wu5W( zGsZ9AWKXVBH^qJ`B(!ltv4FU=wM>S5^P-E~c(TOfjuA3=9(I#q@pDh%a)V9yrwcw?^e zXz^2&L9=kpQP75)1ny4k5BB?}5nUcxt(HHe7@4w?AT3de9-v{}*u_#)U|yZPtsuGG z3$d}u^r#EA-6j}6&-CnS>JhTybh_iZrb8E6goHh>X}f2*LfikXC5;e!VIeopGu=lj z@a&hic2bJO)&=o5kAp3C-B8tS$^-QQS%uqT=0lV^-A(le#CIJ1B)1C_XZlhQ3*2so zav@@kc`czZli1(Z!pfk{{Q2Cni({Ea(s)>jE$dw|p$(1i)hd#NyAb`5Ex*H>UcS4| zChj(6>=?3}01bHTbo<(eHSg)By6_o4e$*q+oXeB(OG+fWB=xL%Tars2{;~ve5T5^g z?bNtfg6!>RLhbP~W9}32CWfaqW`5L7Oxj#9S^6F=c@UEOUW?3OfivJIjZOv@2 zXErH(O|bmv(Q{kh*)Gs%z?1^)MZhV}g5)$+xJUsD;XXY5{_Dt;t>9qB6v}6lFEA+{ zCRC*yF3>MBy^6h{1c-z#-843+ok^7Zxtk}20*2Kwc}v9m$niqM&Aw3)6C4ZKaLiXZ z?W3WsfErXl0zKseI!j_-m5(2pA}?OSfyYn8aL^Ahf-0wfWl@|G+dEh*{y>*BusI*X zU)C>bNQPb`G{iwR#L`((nD#`Y?mu@RV#^x=QPZbY)c{?@4FQ@Bv}7ZiTk#|wd-s># zNunoU^UMA7`GycqKYyfbty%4Td6%q#3&DYG^YoK75MPfa`%YVE$ zwGFa$G3;m;M5*GL_g9=8l1Vf!Nz8d2)Dj!b+tWY3_H($jTeKm&{+>j&LeIc%cOHV- za^VwZ-Q!nChOE-757O5}kC;THYt^Sit5TU%M34>GVyZ$q_V`j2Q@>`}li7qi_ zKi7j@lwoY_@;sVRW(YaX5J2Y+r|z+_cl5}EbOmPmo$hFekJV~3$=?@>Z}{p7tV(y~ zn9EzT7moZm*qpcLx;;!9=DQJ$Z~tHefAX!3@0ni1*=Of&Y#ApfYfeT{tr=kW415WO zq-dCUNa*sK+lwl10hk#oyMK^E-ZbGhBLFdGNa+-P-wM$|_#X~uWP0HB;O4L1dJ_zS zixQLwY$bSZTek>&EMwdkA!J-t zsZH+gE}XuNVDxJ;Chv&cD&jia{lWLu3qUi6(`vBygjy3Sz|F^jj&OE~lgnuft{M!q z77$sd^N;UPI;5a`MSJ z`)ZC0cuh%zy&{KG++dv9EfgF))(Y?uGsG;O`IzKokm@hM8}&3%O|@w7N(u}G%!(O4 zavC2&t;6objH%U`{$7FjEPC#&rtP1VM2!=$qTzVAhw&c6+u9@~W$1gKsYs}Ggdtn4 zC6+kt9M3kf#;Ucpr%KYgX1-hZ6!Za=EU_9NEm|mkcNe5Po4+Rab4fhFO>KAFWiP-S zbqxBhQQb6MtpDWYbBSL)(iBx~>isbJfpn=@@=ckVFUI13kxbESj}uL0V`_F}P}`1Z zzYMD!X6?U7xOzz|Owcv9zJ=vc>X?pP;sG)sLM-t4r@Icb7@k|PzbAbiNYTk_PornJ z=<2nkh1X=VB*0A%^-cRxZYP{+uID3t~>-l%d;SN;CeHbh`2!(qVb{lH2LR( z!?r&mG3CzO>5&Y#uSXFmVQ-BZu2Qt{Zr4}Maul~x@*=iDc<*;dcY=b~mVWeG(^2RulT^E~@jy`SWD_yj z>C#N|z9gmmiOP|{0JV^^w0%;MfW+*56b)pAwp?0{SxXrY5kJoAbE#jsdJSZoNR4@z zV&EJp{v&K}?%2GdmTjXdWe`*#?8(JPU#|aP=Guyd?0a6y0o#p7Loc%1Ja@pyx~t`BRKZp@ba z&#x{&Gkg_L;y*z3KEdm|TF!X$kL&+cplKNVyn9kCFVZbF6(|ff3>>toz+=QZvpvKnrmglmGZ0qy&(yz8L|ZZ4RfqY+62DO_&%tJ-D94!0 zL$79Lki!^5=5^oP{Xq17%HIq8PX-~$x}SBw5_C4W3$@nCx5BBw;9e*4azC~N3} z7V+9rzJ|h$rxlIBA-^PX=9ljpF5RmoYC2i)bhi#27V4+|$0WgdnbD{*6@KefWVbO3?D_1>gE{gsZFIT~{; zRFwd?IKUt*pNjkrNy`7*NdpC(H0)yI)L@xUdcAZL08eN6V|~*Oq ziEl4}3%_GeLtUp+GYC65cew2k(C;5E8L|xz0B7kOLB`n3#?7<^lCT=RZ6oPmS1-fi z;gn}CrlwSb%=VnBlzL7*tuiOu#mb(b@dbgBuN+}_PJ_C#c}KhYR}#{s73hvO5#*Nco!7#(k`6A&WI|}R>Sr&i&`teiHI5&QKh#^)`%b3X+LFE6>0=1y_Bap>F$}($;=?Z3crdECj0_iA zyT1-}TeaM!C4mbCq?GztF2L%NTE~fF_U`MyA9kl$zN(lr&e1bt_$c8}Ilp_Bjy9Sn zB16RaIOu!$EQU-_A>UDYHY!i9Fu$+d9O)S34)Wu6&kmZZ)Vx_w-&6f9woy5QvS1}k zt3|nqPjlzX!`0e8b7x(dLx&*LCoGvlvhhJ~p9tiKVXbc==* zsG7KSFW|UMdPkYOS{W1bea+Uj0;z`_0^h9zNIU77YENguvjVEx_-C>q{;<7$uLvf- z7PG`qh?bJXm&ey~n2DXm5*Tf%xa69|WZQMK&Sf{fI&5h<1>WsY=mb*6( z4)P`9Wa{!wO_eE(?fX)Un5}UH*i)Am7+r^FpJjQE-{G^pvND{=@!=lHHFlheY|Pwe z#M8auabY`@P1l>uhqm#r>0bSA>zm;KmEcW|5xPCNJSMP=Ejfkb!8|_^0e8%DpqGQh zbQKz{FujX$I{e-qj33kw9PQkkF$x0qz)U#OzNc-T8Pa_Fq?bMEQ_k~q%D>*hj!g%3 zQR7N|Mmyh)(?+7XcD_F+*0Dz;6N+WWGZ$saOz1VPiUWXY*7P149EH_B@?kD0YEJSkhUjE3nA z7(=?!dmBPA^d25iST->j1QU_oGv2?e;#Qw^4}51&2v;7h_-sMHst6bcvV34{ayK(o zZj2N2t&A|+PA15)<3147ubQddcva85Vo@e%G}Ds)1oiCVjO_CYm(!M)G#@Ag(WrFp zz=obBQ0m;V98t1I6N6t}57noRHDFOWiJ2=VWbt#=c-QWh^FQI^rBj8XyY^`dj+guL z)2@rEY+LqitbJsq8A6u?&`t#|+u5KLuZsA5JccKt@SlBKwab*q-0-v6oHQ%i;UPyr7`;^|D+LCfl^TMIS zT=e1Ao)`VkjX8voxYT9np`W)&J(?V!1G$uKJg;S3Z4K`Aj8j-2ABq!W!<}z+B1txg zbt}{M-s3_~#p$~;th@(1_l&Y9zKtCfQa;_~Z{Mf5M%NH+vPlTW(|QfZ>Zo65zA}Xe z^5_9MT7S0^#9t&lDKi49fYLcq|8etX;FZkLQS&hf8L#uE9|+MxLtAllC;{95xB;WS zom1>=mR1AN0Dow^nESx{un<`hf=R+SKD=P^d?}zHRP$jP5E*4DJFrZb?M9W zwMXHw)@>WxRRz@L%P%-m%@4MNT`W2!-FTZIytZ-M=&=6<_aT^~Do-`0qPE&!+E zl1~uIK5x|eHFVp!Wzl7u+&w9}!RiIGq;7^7`xfvgvz$t~U9>v{ZpdGr`=ljndFli_ z(iM#wR()&q@Tp*$MBYxK&=BMAJHzG(6UFdbp6QT8Vz}D+rj4?%2)vD zhV?#n9LNsHmctd+!c95`#d4TwZ5Qq_rahd)#ISS3atytNvb}Bpi^TrfRPED8e{cZ5 zS~TnmXvsN7w-Pt`$`Z-&YM#A~J6Eq0=m$$_o=p+nA15vlw3+KFleGa;mN0w#-=?fx zqIQTo1RzU79GPxsh`OqFO8m(n-f!CHAYtUzy#O+k5E}z16nrX2QJ=w4cA<7(C5VFz z_lp2mRyoV+d`3Fg&}AEMKj-7^@!^1f>2{FYL*KXj&x5Rfv>aFV)Xo*5Yz-}wC;y8r zsi{2KFm*m3G|iW>7i&2CO64%Sh;83TpE^Z-1+-I`8QqKje`tx!r^z45AB1AQ$V^!1 zts60e-nU$RNq}pZFwzbrD~Me7xmKm5(fo#SS z{!vc_c}Wl!vGF*jcb6#?8j_9=hDi8yaR{*`VPiHkS-BNdIp+Ij(|wXmj(5Q`zv}F6 z)=Q&iRpr?dF6-xVkRA}xk}j)9vnJPp797AMvQot4L!I87{1= z2L!@78dEa(ZYf?p25702ZDwO)P%|OdZG5vAa4s@`(M!qz)p~5nVWJW=u@XxVNGD=J zn`ilytzXxwsXIj!ihZOGwt_NYqTCzo>Ul!_?6dDYgDHXawwkvbl-lLTAq`T?F+tU4JOy*}pe&z?~mo57_q&`UfMK zR3gOA#%21|0ue}3xYfV&&>e7$$fj*NeZj!J9;wk4f?<6#_gq2!EBZGw--Su$qg3%H zYe2iIGsC=1EC@WGC;gIgrt@e;PRzmj?HZAFxxY;CaIqIzyen|;%ej|o_h5~1=s2yq zl|C3s*-=fpc^(_Ht%n>&>R5XHB${9X?kXNnM_Ydt>Qd)ii|PRl zEGdEa{csRa0fz!38zHsINzZoiR>l-5(R z(sCO2R6vO;>TH}>r%Y-3Of)0IuK!yiAS!`#l_WWSTcGd@Hq!VXWP_vd-rjGt39S=A zk<46s_j9e-%N8Rs7ncGoOdXBFPp5QGHPb=iwC(X`UHXdnl}t?Qoy}1Fz<@psZcKXUX_~x?_SV&vtL-*PC#0soRiy zgbLwU*=(%qM*}La?GLBLRwo-P#uj<8G_3l{WfZZ{)7RL<*BDsn7p3APgIb$ByS}Ql zT#TWV_LZ$@T1e+tSc+h1!~8{Zi4ZGfZI{QXLdlE84G(8$T2Hy|9SF~ct9(lyQ(=Z) zi}u|PRvV^zt#Z*~-l&{MIfGc;7d_q*3rLRm%bzS8qgvX8*AFd%r1qsfdo68KM8+*RC44qaDjJ1U%UF!p$fixBYFQ-}|^-*vkILJDljnRO3_Uh3x1$$>R9_to= z#h|bZ*X8HS8wT#bMc!XxORe)Pti0V9sAGA$E3es=X0^tUci*X~X>w!Yc=~PcQhL=l z_gA=k&m#nXUCStI4@Sa3)`_Ey9?3KJ4|uzRW%w%y4$bs6k{_zA#7f`Gp0+MN!QlqQ zrLtC5*B&~v77)|=Je*qpU=-Ws{1FAt^`AB`8u7+?h78G!yF3EqJzP)3x?Vo31f7BhR_U~9RR#xCv_{5ZC;LB71AK-XM;UMX z-ze@^+ds>6q_C1yxyWSOOC!I9+$^nib)S3dDt-ZK5gEhV+^`fZ0(*5O32H|lVE3|{ zacSL0YOj6&Q}6oB0e#k^(7DtTS+!K@86O1bv*#qZ-?!>0d{rQ-`i28w5OqcjD=vt{YQmfAfy5Rdt6z$2P#`yzY?mJ0a>KslE z=7pNnb0|9$`aH#H5CR3A6L$vk(N9;0JRfI}{@cnI6(jO{p<78f0?VLg1HE=ywvqadqfU;8iQ{CM+Cg)40Xq zA$@Wc2qHbKj99{FiQFu7S;}GO%Oo>v3j5e$ zzT6tO=Ao( zcB2_UpK=SlN@IcrW!k+o%kxSG_51977F*T|IVCowVx3bbBNHi8I7;@AIfXTOm(elo zW8JA#aro_5!2Vd9KR5hI;$wx^xgV!4%#Q>Cjca1hSNklT!)=y5uS;9fjGqSJiCIpd z_U~VPZ%=B;o#PXWF$@|GcLVmMnR^FNXj)E3FX6|}Ei8g65WCFEyEmgSQx$2=}3*)1)N-{vFDD_pV!W8NP zER6j~|5GvX0q|;a2sYBB<0btDEIPa^9&T_GL=38+$=y*;HEQ^I6mHN~Vmt89E;IOm zQDihNlBI~U&Aow8fjs)eB^|{pms{3>)0229{rFgP+=X53(cY!J6i$K2=VGe%B+t9{ zDC_enycQl(|DP_TSRud(b32jx#rUN7)N@lwW`+3dd3lWvmNqK$-#m={-#pB1lZEEV=|apJVXbD+=Yi8 zJv`x$TQ47vo=>mLGJ&P`o%So+DBKwjzLX@ZSx?Tu))+#b)^ITr-{r~VP`yAIr-0`5uM$~7=i-9)@tDUnR|Q+628}!qx&#6cF?@|wOCukGXD z_LgT?yeW=`I>M!Vh45d`twIa6Cma4nVlEXw_Wj#Z%;&OJvU$%$he1N-Wzs}U58h{7 zI>5{rCFORencx@o_{0*s3-p$GHFUtWmKBU*){C8Z16G=Z>p~d*ODhEA5MU#*neCA! z27|Tt_)2rkLv({KM}w@<+UCjbJLw98UnYK8u!67jx808n}1zX^svUZKhJR6;oPpaMuGKJdaHfL&u`ZyH1IQkV68 zOjJFaHk~_m_$Zst;J$Y@aRJQp^Rt|?Iu}4NaBU57vNvvyABys+nAwb_yd+g})_sDR zK6#&Q%)H)G+n$c*G;?RNz?42r>u zF7}UGNYTXiXgTj8VUZ?-D+$p4{X7+6`5g5%${Iprv(43TiO}~k)q~;OEcCY3a`f1- z=AZmw?e0(H$EmqpKJ}>RC(8vf_y0)of0VgI{6QL0{x{OVzejXQ4(G3Y{_?g{yaWN; zm2?pFsn&)%{>H>;PLV)K_!A-eIqJwV0bH~k?1_FNCs06o(U0m{$@9wNAz(S@ z4I>AKJturaPLS~>0XVq(b>H}W#joOAly5wTp$23_W&e^5sj@;cSnk3hAoR>*AY=A5DH+1> zEcp2yHRn%Wl@NQ361G_01D2=N9+JIb@apnUOx8Nt0|S9ligiFN{qHYqcWXoyLwXLq zjDDz1U7EwBGGa2D3-NA(11J5c(rGsz3cag>JKyB+Un{py>zwBZ84PDeZzR+YPc?N3 zaLvgY73vGr8_k}3vm;Dwo!bNeqW67Z-KnF5ozz=s0HnjUxX- zUl$+xJcJL6k}yy1M4)J|TwkTxv_?8hL(f-di|l>v;3AmbQX{m9tq~&~pUL#eSzjRl znC`&T-0F99b+nQV9{B_uIC<8u;2Jdg-|6VTc83L>!0Hf99EhvNecTZ?pXW~lPF8ef zYmUsTCOJ~qax&d|1et?GyzzcZvMtrn@CtvM(wpg@P z2(u?@3&sTJZ5D5cw?y!S++K?euJ5uW9y_@O9Qepyz|<#X+-Z97?&w51(23f&G5|c! zgQCiJ*2}5SZE&H})zr0X$a4kaW~jBhYqt&R6IZm@U@!lc$0=_da2i5+uJ0GlK$|&I zA7D6ZXciJP$9w}p?oQX(x8z;k#g4TJifad*_z?7uyg_=Io(tJ3J^UTfQMllojne$# z&)E6`okXeLKK_(Jsz$`8bm!{uE9j?26mtp|_M}YLK!5oASU8A^Z8A8b}*-RfG0XJ(+cp8(qcFBEnH9=m;USlY*u8P`{a9ggryqBYYC zoh#t)7JO4di&Wkf1qC>#*DBMJV%bN!A1|x@+F0u=HW4&cH82qr@2$jfoUG)bgw2_` z1dO;4?~W?ZZk&#N9yRGE3EE)n+hvuKZZTni3iZ-Gg7b1x>I9BOKYt3cr19r)yz<$b z=7X%Ley5}tikm$QTsw!AJ*t-j1qe!K<`k=Um9u)_=f^GZe3!(eIBwl=3vY!k_-zC( zT^c^i>n*)k@}LZt4ZuSxsT?jYhQk463)?uD|GCyR!JZ#sZ zgS(PVC{bvB%B$-FKQ^1sd=+^~ErLth66S1sV!n8m-VA4DNo8Vo6A9PxP{ZnwZ20sb zg21TWx0`6tULRlDmSqdeEw$^ux7)(@oEM(-9{3Dy14-rlO2NMMK;;xM%brNORR3yj z&VK$02ct@n;9P8WHGki7069zoo^CxC>FLmgxL7b?&L|EzqiOubOn&}FGC!UU=_1v7 z^eojz!Jw?#b@hj@3?0*;5B%<{JIOvglyOoTb< zI9xftus4F4bHcoeE&7IGIA~m5L{glw?VKE|WH?~m=v8dAsA8{_KJ!b=lv#M;iLKkWO-Don~HuIP@OxFyYbUYFFIkraOWF&J}hNr9bIc}%FPue;$`8iLF z!O2eKL+blvV-A#WDkh|Ry97OEc2+f!97Sutb?%iaJgd6C@XQyymP?WFGII2CbTFd<6`9oHcx=@B?s(}(bMo2Gh|?u@ z?3JZJOS$&fHvjo|v`0!wbzY2}0*g4BLjU8xp%tuu3%ClB(yO8(_aA(#oY7X5RWdZX zLK3*0pTzgi7q`s3(wy&CN%YwpHI&tuE&A+ke-y;mVrm^My!F}?WI02WUzm96z8q?! z-7?sP6wTEYKb*JpXFT>J?hg2Ov#D!eP-@9brM1 zm8de4OT5%eS~J*&8PonHd!52iCt~lZml+H^wZ8fDD+*l84jm;KC_QoC8y1GHK<&m3 zmsVIWbu}e$#H*CH%u^x&R-^v7B7)Txwa9P-D|e!O+v_p-pDR={RS=wz^7e`oi+RnkuB$vfNde=#`P z%#wA069m7{8m0>7@df5~YkPxM_~1=$d9ETGP8-mh1lF%b`l3&IjeZ<0-|7|fjL=DV zqh^3eLNv^nQzfPiUGzH6KAjnS>5z%-OuvdpTL*BUN>K z@4+j4ve@0QI5b`lGuga7wYuDvjX!Xvip6JquLz%h8Ng+ob2vm2C}X_6p2YsiI&4(* zb`_J}mk7d?TV+F${CT|?l#{J*Jxasm_5-bf zY^4xHVaf&zw~!Wm^iJ9QX2D>RTM`dK-0X4s7k30uBO&J^t7RXDA-O!ucLVrC6|ESv zi5)G-90<)FbW+smVD;F0#(G7pYXgrtg6<4jO{WRlpi}OV&Ip66Rx^V#z5A2UYrE>? zD!{;M6O9IR^lw#m7GR&b6pOAi>I>-2eh+5hN|ykT+&G({zVNOU(kn zHIDaw;InEQBImG!sye|BJ!jX0GaYrNzWYB4G61S$4rW*PRtIq|0@>XTbroUcHE8K3 zp@owfHf=ZJo>v*O9-L3L5d{eFEC69Uy!N+MR}!s?+67*RzdwG(uIb%mUGGP=wPm&M z?b=Kj8WeE4$mg-M@%gC^!Y*|JVBbwO<4p{k#4-3riToSld?oP{tU@*2Lmf2s`p*9P z@9bc6{?U?kPi`DO)Ymw&H8`uzjpO+vprJrxpm$k9EDymRo9Ihg-|7=2<>Qsqw0jTb zAkV~w4-Naca8wDDSYI|y_=e{k2BMU$vm@oEroM;8<>SNpFiS*zt*4vP=C{0RLgK@> z)S%e+6c&J~Vr__pr;q-q;$ie#%Amm0N=@kNoA-00sh>z}oowdg#60hqhAQRW9}PmE zH1w6&edfkdFs0kDu}&x4@>_44srEOH`kUqedH!4tLe>{rp%Ehf`HA_-`4dq5S)5R^ z&<<6uE}%&DLZ-&ac?7@ET=j6-6T(D9)m$_zo7t1wFIY6T<;M>*^s;bR=*VaMUXi!0 z3Ma}=P9MXW3~xOz)p`7~UE1eh)F8y^X8nZ3UVpf>JnU9ku%+NeMPRx#`)4mm7x7Z! zccuejeP1g1i0XW*OsG82Dh3Nal$H%-*7UlYqc*WV-z^t0Deho)+J|GVs1~=#X_j{QkWCTvu{bcs^f^Bhzf9Z3V_)}$H?rJ{(#UIlX&6DrKaJAMBWv8pflJmx z!W;?qhM~59cVcXQMLB-G_@~iG4sQ$=+k1t&&a3h!X8isx+z@r)rPx+mwv?kgIvzsb zzkWSoHlXczbk&i{ihlObiXUU63=t zABRSMNkiPcpn{x|HXisiAes7 z1T6a5gKNB3n%~9w=cziNg_ul8F^g6Gtoq>@P^=p?fj^PSU@kg>B7A%&57`M_Oq^L9 zb(6G${DF%NzkvsdCsHGTPd0;w4l0BJvtaLo zss0n0lv7d37sXoL$>a0Ecx}JEP|5grhQ@tP)zTtCW|?UV_cMC`DibfTtP%%Q&CFF)IA|Isn57V zx}s^~N2)7z8(yd8R0v28CwCs7@r2~qRN(LZgZ=}Ulv5Z6G$x5*N#enWAciVwPwN86 z(-b{{7s8vDcl0*4(S3~zITI|npR}?${Pkescqaw1^x&~F7Jc*7?`S2wV=233Nh%ZB z;UDyfil;la;n5;%@*NHszVcq&E7_KVM8 z&sNa?scr8y_|Z}u9k>Orig}RDonTN0CYpTv9PBK%?vy?bOc$DuJwk5!)-)F3VA@_1 zJ(r@xd2mVDXf({PNC(0^n~s6~jsE`q;Qy8gs7IqClYtlUZlH(Fai z_pUHlhQ0Bx)_aUU>%G*ihR1UUHW0)QOfPN>zUI&>KuT-@UN56RfE&ST=`$flYGPW>#=nL}5QkclZlHRgnKSifkV$>?N{se z*s(?3+iAFS#X!xi6xB{8QG*A zN5-(QHU+mQpYM}Yr-fg|Wo2H&tEb{FOBpJViM8t4aE$~ua2Ys)%P18`cf?pB%eN(! z;5>O^Yy=mz&vZLw9%>V#!nEM4V>)z~DRZMKnO-sfxg&iImRqVc?adZ~G-ouR;SjiP zLMN4>;SUhh$EQ-}&>ZujA$#)1*TG_Wc6ZH`73MZ->I?5LW{U6l5w&j)J@U7&axD^LXs(8Y z2{|j)Hc0EB|>4`!yL45WhwAJ5x$OWKLTHjegQ`3^2FMzEBtfAHC|3aos!S^C&Q+E2PU%LdJt z7_BDce622LP>)^-!d9?tM!Q0r86cS_(d!WR-vrPyy)=ks{MDg<`8(s!Tr4G!SO^_g zN0(!J7p9hMs56ORXpQB2g(5N%GAaTAhDptji}hjh{BQz))b7D?^AO%&x~-Ozz};23 zQ?2^>U|Ei^Kb#DTC8kiVLclq4O425vH(vCmz<$P2VFh}r>CqE(BW{gXfy8LBa);+= zaq{_jS^+t5PW`TRZB|=wv^~XqWX3I8Yk{@Z(o53;Rnm z36s#dgz4IUBuw*Z5PS(W{=X$mw&xP2`aF@pB}|6r5~lU7(LsWVNv&oQU!sR9KxY){ zCJyw^wK`gB(|>FF+rrd-g{;P3|1yEFR;+~)G56an4igPL??%wa+|DoYu4Me9Ve zVOsuM!}RUipK=5WHfsYvpz?t6%JQY1%EJ5)9!4~7E>AGpS3Vv|ntXq9NhXfM!+^>NnN(g4%7=wH?~Ga_XcKwqAUn`fNT8^&QTUT zLXhU?5c{Z|1E#_GT*S05tR~UnpZqw$X!yus_8ON873A(!W%rtu<7EvN@~YS^R!ZFl z%l+bXz&rRcu{KHBCnq&dmF;b#$NWp9cNLv~i*uX) z7eq^pC;o}iYlzsNgpCsGu-N(P*B@>uy8|m%uNAsh)BYj>Jf<&bBZ7=-(Jor_TjVcv z&X4nd1B$W6PbH}AQmVnZ@68|D0CkP*`ufWsWGnZE-n##XT|AqqC<{qq7b0Wj2%%aC zwCu5n+rg=&7JrZRfP@N_@OZI)NVzv~h$L3TTq{_6_ywJMt%)8wvZb(TNr}&rXQ@q_ zScP>9XVdWs#l}a!@d7FioX=9pjfL#G8bc>@CBj;*zDWCRmY?GC<~zw+K!1@e&C^%g z8j1{@WqW1LV`Qou{vy#pBiMN3gzsFgP0l;XZQSb9@nIGEw3YQl{4bJga*d&}enLHb z>=822wK?z!e^#pNBo({nEe9i;A$LBY@xsc>6KC(xD!+oh%^R~m(oD7v zdo(n=w(&3{FgbKc?Klf*A&PY&TYGW&Puc>o>VM}5X9 zeC?LlNI)S{_6OUHq{T8~bCYb$_lHCW zZ1Xtk)A<`pT!q~}%za67;gp}9JKXY_Yu%q6fy4S9=aQ3H)ECVBN&s%{ZFVk_dyU-0LPkX*Z-t@^gfWYj$rb&oB++E=st?(^Y z+9{4mjzjLl)(xFqQs)|Fa$#E!1zjONrk|BQh|2L_-4lRbcY?m4CC$96>*sGvG#<@( z8(NRP_gx4hOia`pxq#-~+_nrlGM6b+PM4;RuK#R6`7Htdi+)=nTco1ux)IVpZ)c<( z?-Sw|Q+f3X^-8T-7E;smtfgvVEkBQoqm&0vM966`Wqt9p?-b2{ytB2A#q(Re9WJuN2# zdky;qDM2RR?S;yU&wr87o^jx+CS1HNBHla@LX$W4JV;z^@mnv!R=i}N*mqtbZWX-! z$TU7cm~fTh9YAOUwcSG4`=6?<@4S?Jij`=YmD9Ndv@0Jw#6UA-d0g4(qq4yyPaCc8 zl?Qs)9sRByKdKmY${9M9_ev4~I$V{R>)A*jg!nw^9MhU{d3knk#=j5t$+i(rIV*Oe znRDZ7PD;~t3yBM>P9Nv7cMIRk`pu`lYfA<|fIbtVX!5X$xZ}Q;#JZO6{X@k{ywMp) zPJzESp2-<3_8XT*xMO-@t~;4B!*?#BAo zhO|u2-RwwZNh5ok#To}==7J#OF0{aXA!zvRjCEH-ivcf|tHr)m())*k%GdYGlm+`qKs7yiz$SdnxCv)1bR7WZu^$3IgOj)lHA@eR4l$@KDln=g=1^{sD47ad2cy zd8acDED4lo(og4v(5j|ytFv%7T;*q#ISX{smfg5IA(wH##74U}*zg#3aJ*LoGx{1b-LeDR46 z)n|%;6DnXs$!YLrA1L0&?6SXIAYgsa94m55GIpP`?u=HabbeiOv7%ks$X|KRW%}m4 zkd)_g^+KM#zrh9hS?i(tDJ|LfQf5>;pCA zFP~E{CG#<&J-NjQM=hwZn-VE{H2yn`u^Ie5(fWQ^d@AJ(a$C?$I3(u(W9vN}sr>&p zekq|uC@YaoHieT_WN)%V9D8PODx!>Y>^-vyAv+{F$01JkI5=`}jO;zm>36H%@6Y@D z`~Cg^_qiR%eO}M!bzP53+Q=qmw3vHunEp!cktcE`xSs30c&+0ZKESF3pvms@9uBDx zRF>eysyA`_@{cHZD3C!?7Q2%87W@DFkic)&Y2*o@sEAX))xLR>3^erxTo2QcJijd_ zU0TT>K&5OtdjEeH+rv5Ly{4g{*vDr8v+ke9x!j>sM=iPQa$KG-@Byw|e_tPbhrJ>9 zXsV;*@H>gWGCuI1OT`i?Cjd8%6Ku$>d~*F==a17ve8xX_JrehzJKjy zANOWpd=lFqR#{;=aRfR$o)(qCVR#Cz(vt{vzv@X8_$vJAh0oHJ4kbCJOM4Boxox?{ z61U%X$;k0=a62YuaLe3k4RUG_jy}u1&!;JAy9;cH&0jXS4=6ed6Vi#0@~FIJk=*;p zWqVu~Yq-)pv2hs3-YmjtdyQMgRL0+_Eg5BuZ>#A8bxt}jg1|A+$oJ>V~_3vj}2wawo5+U@P~>pzS>*y`oX96F*8O+T5Mj?yImOU zvE#H(WL+YY=39kCgIFT~0;kg~!V^Ol^Brh;#@#Qg6+}qb(Nn7s@#~rM?H+!I@eOyU zSc6&rBD*SBVmuZOSv%hlWLKSrCYR1n2Z|sVt_L(fs@@tgd0dE=-#B?{u`(Tfv;8i7 ze*`0?!6q;9e037=Esq0r${1#iL zmD75{r7ESRp-~t^?CO^<{v4CE*hn~XXV}HVs>fAmA&I?NAcv|ee!F-&{RZr^qCKsg z=c(hPkiV$0z;kHbp76OlH=VAhMZQ@n02k!g17>WN?rEBMse*7qomCoMoAw#bvbhmE zQEA~SSP#$<2!9?IMKk;fXz-o$Bq22@!o4*nxL*uH$zx(HtHM4qB{=WMkqixJx zbqN4vtS~D%7=JFA4LapLoSa*m?qVD((Mj9NW+ioV@(0i+n|9+@dh|zsr+)shw-y63 z{G2j20n)009~R1~Nz!9j?+Fk~+f-eouMat944O-cB-PRtxnKNwxYVJ=U?eJc8KBrT z^wuUG+o#8;pQB{7`iGy4OO4y*GW7Qui|+OPD>jY%JRDb)TECYwAeE)uJ8ed57oM(o z-r)NiYiBEyz5wxi^_qws;kzH_5Aby!*wI~($ElExJ3Y~39%Qn=2u zV86aMH1jQsj^$!>l>TnTC@<^!umD2B7WiBd2->aK<+j&(_VFebt$W4u__gCD9go(r z^nFKOr7wqhH8(F#<8w8xr7}6EF9-{dqS%w&=Ol1u@-B%t5`R{i&!U+g^wgP{4_-c! zswQ?(@YE{i_(D~`N^a432Dq@J$=uUwrA%VR#ygPUv->=@scpoh?l+)UPlH!2^~m_72*KUE3Cu)Kz{ZmeOf+*fPWwGIcyyR zCI%OdT3weRgC5vrm4tqFqK`yQQ53`NMbV7Klbk}YMu%QnhF%K|)__Nn8z}~9NBKL| z?7s7%vul3z;#t&y8aDIdCTgi3$=6q!ZFJP%p@+Z5tlY(4%*Of1!&X3TwmSOsa4bTM zW1(wU1^L>GZ&JjW=4t-)Nhqx9jS}_!3JFPB`IRbLln+uw@M7W0Y`{3rdylny&*KSNQE+_$J&rN|5Q+XvFmVhJpL$kr~&n<%~_+t zTL;=N4RFK96c2}d{XF+=OKw*ru)f>@ZW6OrR?AI8Zw0ozYK!2TyXx28c`ye3k=+oC zeDA^vvriNzBz$$ep~JRm*19Hk{qTYEUN5D#UXw8(omOW%=&(9wAap0j1F}iqc{$?#_EXtvSu;P4hK+$tw`bIQ$RNA=th7pZC2c<}UN)Fm zlv6?&0Y$~Jnw3EI2O782c7U{L&g{b$267px!`%~xiU%7k%$}EY91zNeDCA0Ies-nw zyIvO`%P*Xu_;9D!Avbp`wttG7ru7Jc z*B@1nN;`mag(HdYM^aFhVEf=mW_hES=DZTD>eH`*Ybe0@5$RxhmgU(Gaw=4I3)A)k z_PSr7UwlRiNk=?`_ou8`7nyPprA4yA#!x1eZ!xU3egyTn`_=V(WVK|ONiUZum$cR< z#z3B!uT{=yidzD9+KTGJ3qaX2m`7!faC~p9YIy5xp>`SpH{1T&+_4oZ?c&lfAyxx! zgp&pN19H=$;fZ^NX8K>}iC;6m1mwg}v=ew1TKGk>cm0gA6G}5q8A&-z9hd2QGFy;A z*_V1wV2!)?X?Jn-?VXz~m||dWfktbMHJm$4fTjFO6`e;3ZKIvJTgiOJUNsFTb62gq zDg8w)9>dct+}_Pjj{=YR>JAqMl`OyZkRoQ&p>C2iyJ$YE?u69&&ox_X9ZY^&XuH_l z$W&U#@!q@rqY>IUimP&Ghugbn^}my<*~%Aw(=u`UiVHn8|9-z|!$2aZ@z1_qQS56+ zFFJX~y$4{L9t$fm(T~q_tn2UIfjzT@4_TuH{8UeTPnUyCBd^A-G{sI0PvGu@b$j}l z7nLit)VybohNp=)+)0GYGnz`S1z8|)GE>z~pT63q@TiDfkK|DuMKhY^txkg|@Q!ZE zyEm7LaJks`LCxDrzBwV&@LM-x-n}Zh8N7yRFV@k0qRu(Lew)+fn=QRV)YW*|E!E4d zyL+R}@-kdJU+N#s=Te|x&19z2N4|Dg8+xl$?QcwrgVUgjp9T#F=FP7>X*E;0m)wh|o}o$&<@>VQ-AxlVpKbfbp$uJ9@AMu( zKquP^o3O|kS1i=O3BDN!Q3k7|eTQ+ewvD&>O0`@z#z-~QTH3Kk3AYf!52-uGbqicX zYBN;wVw$m;_ZJ9_1-j>j$nB($nafX!P=%CO< z58H+w-95!-`RF4FY_@s{nUiy|l(DC*Dp7>gr0~2QsgUyv9k(7|KPE5RafkIb<4Z{f z`En^oGP7P!0@UvORR;{Lx}`@j(QdDdJyhm}_(OmX*5#&Jk41h3rgr8MxvPC_9Y?&v z7=io4BjxpujX+b%5Y3Qm&gpjOw;wn!*TX@a*03dGW?PlA!X;Bxt;Fcl<^MGjJoV7+ z&<8T$C9z*pUx!}2eGevA^EVMuS^fA-@kEr-7DWx|&SI74uy8F1KY7qSZ>fEA*Cymd zg*C;{;%KTK2Xs=q3XB8xO~!clRHfn*!>XUsCYsU>OV&FBn#wAqUgMp1QZzyQYx1ZV zF=re?iB0AWBGYgEis_aUf-U9oNxr~arL?IxXEWP}_uE!NqYqo{=$l}!H8AV+-j*X? z+&Gfaa}R3{k+BNMs7B0VPr#;MH@CdnJC2+v$`>KxJ}wiVQ)IvOQb=7gj9is1Zh~s> z1^*%_s>r35a+i;>^(v*n)j+h) zx5b%_THAzj4M(wd$qQ$#j?H5{8`5W4aoENA4pLqVqFdiQ`x_4Kyn7dw6)ZU}6I$Xa z38og9r|M=*`*9tS2r2q-A+xW0d#`?au z=dmuQQBt}>ZKC6M6_Zl6AEO5mi%k_LW|O+DcONV~e^sEM*5emJ=(ujI9{m+KP~Ghh zj5cwv#+4&4y$`M48~Uf-W4|Yu>h`kSwZ|LJnUOl@GvDt&`fy>C!7wD#5V%C}A4}Z1 z+*8VKzqeNtR|e1^Wr68Rv^$Lbsb}~jY~1EvkZePt*T5gc+}b$9VkBDqvd-_yXq8YmOtG}2hw@hid(`9nqK269F zTD{o4-8ZdzbhHm<{1DrazB`=C%$E`6E^&@s6gtN)mi@*qI!dn46ZA1St1kG1>J-$5 zrX_Y4PysoN$FPF>Fp#EJe;zR!TJ6+GTWpP#XpM!}#rcIxPVRJk$L+tjE=&a3(Lywg z(_=ET;V(TtQif~#x8o^VO=M|{l_@YAbWH_U!R9v>u$aTK6e;&EiXV%O5p+;8Nq$^t zMLQzeT%4BnhUT@I`!9k@Js!{F zlpGN7=g%E}X#8(jLo!Z~!fm~XcKS8(l4N~(+_`LFPpYosj;Y&m*skGAv@$N^P2jq5 zR>Dww%TdifuCQqD=pK;(NCfi&;CT4(Q7qQ8eiF261t9+|=Y0{sb&3zy{?RFbP~VN> z#Fv0hA?L8CuOT*czyl_+fzuVR>!(7i%0(eEFO6&cB?xC_F0PkePni$)Omcs<&;4?~ zy?R)SM0DkiLJS}M?Js@Rn`JBwJ@JTjs~r3GaeFioj(n50%nE3SrZ{hhW;}0)&RR{3 zE)IqNfKt-^AuOJ&6ydd+fG11>^XwJv;i>tH zT2H{#^$gbYR*4oD`_Q8o2)U1%KqY9f1sZcU zZGC(tc1z>nq=fIbL`vINQmzap;J?D@<6fxxUY5~Y=Sk~nutJV~814EK&+X8v+HEZw z`#u4wLTk^iNASec<=f-cV3alV?2E$Qs-7X&omkUz&#Y<8t}Loc<2k2W)|BOqHufY?O2AfK*83wdz?ClVn}tP_?1Mf zah8P>voj~d%MGyw$ufIjAY0+o^q>_f1!)Fud^nRe2;kL-2v!6P&9uZKBtJ{q|R={k+ zASgF!u5llUolPSv1P|*qA;r_fDukC_m790Xw+(PSOk%l3vEppAfUUJV@={?}-b)8U zz|+p9wh(y<{W;`tYXd{DfZ}x4_4%5p+*0g+eQu>7lRZ-1|g5%xikFy z{o9&2FmM*a_8DbzzIsU0U)*WRblCKT%K}Tc)9iv*B9n97(E|RQ%CGiH2sNq>~UEK^^z^o9PV0;-%`G!Kb;oy-Vz|a+3HLo}mP_`7O zUzD&~X1S#tY1x=FivDl|C?Wn7D0(6Dr-7q3^U4Ksfr)hkW zCHMQxmIa9oG<3JU)}b<+5`!725-7r_=SOARXEZO%-3yNyC>w{Ic;6gp{kydzaoEh9D_lnHH10*H=uooo0aNCBNtUT8qL1cii2A z)x?g@0)7C^9*`3hs4+$-?=4vjr1@q8p|o1NmBejl_nT04$j0~#5rVsMlFSwz3|}T) zlwGC!`%-D1dVb&o0)21u(T%cazqTwm|f`#MTnm z4dn*blFflkdo7n3QuZ7RfEfZ*>aOKaZMN}_HwYuzm%9`6tiC$D^6>D~qPXW6yZK}K z$c=N%_Wa>txVDAQkZ5`LWY}lVa#I`y^^kB6EwK!i0uTzaC?#i>+{-XYz>hdU*n9tK z{!;b)I;PW zJ)id{wbm_>W|g$^n4gDRhl4ubKqN5*>_8sBH&oLf2kcuy$c#Dd`uW1&P1AjRb9lV* z|D!go0S9G^!p*L44gvjM0wuCdEP!)SMWi#pf)=^?9tSN`a<3PqXK{oX6_?A3`BE}z zjVpo?kmsyZS#d$Q=)#leDwRjw!%F#N@fjL2h{Up&?2hrS9ocA<=Iasc;U(Ekm1Q}S z$D0$fIccW<6IVvB5v#nqIt19-f`w%V2>+&S914cjt?!2ZK*|;!jO2vh3D?A4INKxq zOMUw{B4c?T&yS48+l7v+6RXij{ZW=aUfQM?!9w_ivv#;n3DHxVKyO_a&|3#jN3LdS zyx+cp$tva9IjA~|MhxVB2OP69v4Rd$^Eve5cS$s49zd{zqmscDtETG%X2ol6FQMkj zS^^JkD_@8Y8tmK;N)6b+V7mDcn{zwO&CS>AiPRP^SntKmLIO`?3&n4#P{$`;>KFpj zVE}(7clEj06g9FjWtO@>RjP!-?J}J=weq`3q+O${Me+j*%`=U}VQG;-anjs7HE`0N zEM@+}QquCuEM1RPndGAk^US7%p9g~w6{^H&MtAk4Wliet2Sa*rdcP91U0i!+PlgBo z-sWd~LGGm;w1T3Q?~u6pdEjAx6MLrPYRP7aM^hphG%VSLo^5diBj?)~9R zWuqk)pm*K^Bf=~prZK(YSZ|U~1pGY(%T5&=m~g$? zw4-$EvnOU*>JHh889e2iOZXNC3ge(5E|s}stfAdEyMcwcNB*Ukzy}3T^d?&0)a_wk zRE@-O=zb|Yhk7VbFf%fheBq-O8)Mw6(#w+mV3}McV^B-miYZ53VlKn*gQqsCy*1Hh z?Iq(+mZspMj&fRJX6`#dxQ~6r+{^AFa7I+B`i)8 zvkpiQsrqbY8@aj6m8-cLGxAeND0|h;)9U!^priE)&o63pa`I!Z`Z5-Fs}|Nd{i&m4 zvMg$>Ycb!Ry#E6=%hez}O_xwD>J@^mX`- zD$v4oE@O=**4rYc)Sb0Otb4lug>&(jHHg#OG{r2W>OyaD@QVlWVAm^3eoJwGB$Gej zI`aR3>)ym+z)hdwGDC{$?h9|wq>})E<@EL*&5}f&t8h=uyQWp{;b09IeDygl(E!`e zr~Q;V-rIgO^=wbw7ZA6i|08Y+H{O!^3fsxed8&p0I@% zgwIomfZ1-%5N;P%?Mdcog3w8u_`e#Lp<|U&A_8qDvHP*y6omb%VRFuhL-AOW{07&Y z=YZ-wmx#i_LxLiHPBIjCaC#*%fs#ntGy}MU5 zSJ{fW%+*~b#x+UHhUnzp+*D#pNvcHD&)fJwCr$by+WfOYa<}$!H=+%M5}~!Mxb|*v zhLk};4w${M_}oT|1*U5H_`1Oj2J{77HLJtX{kF9FxeK%F?%cFw__4@t$F2 zt|WQ%4V+k=yi6LewOLMpxI<2Ue0c5 z@$T~nuMOLR`~ms*y%Q+LMDt?Z;dg0(_*Ij(xB)YGmG6WPfhAbGD=MnJK`T$;a@PZj z5Pbg12y@V}>|se5)sE##A@)mS`JfWTt4d^%i#T{lGH89uJ=C)f=Ovr>^T&#LM^s1| zo$X{?7UR3(`+UhnNy&DW{`q6!yHDZw0>Dp&`9s_=falkiTcW#LojkyNITss>($Cn@ z6r3))ci{b9k1~~MNwTs^byc~8siEO;58 znk?p-v*vN5>#FvxpS=|aZT{%@c)oo%)259^rdgs6yK68(bl(Yn3U+I5go!ECEO0`? z==rcJx(~#a;d#cYrnHEO8d16E)KwDOD=nQk09d9dgr$|=Mw1oCYJr}^E{>GsNZ{Z4 zc-iEl!m^Fu$b>5G^RaUzf}7q7CDGpM3Bys#BWW}~^(?_9MBWOeMR7o7s0SJw+dEd7 zYBKo_Cb3Yr7tJ=23!LL(j6^<6_9L}6GXd!4Fo88Db-I%P&L51mG6}r(`abKhPow*! z3Mca&oM|;kz7f`jfdIOaR2D#k5hO6dXN*stTpCH)u;tF$BSD-V-LDICYCy?Pb;rP@ zDh%))C!?>Xly=b`)yGM)Rn4L}O^^Cm-D4RjbT3UY$IxxA`tmT%5ShTZD^yC=l z?Ph_!;QBX49JK;w(fhkL&HiUUYW}2%M{Hp|=Nr)f`>u@iYMIssN$6~X`t>J3EZAuX zPQTD&^#x?Z`Q~+b2UqC0p%S^2X9^$ASj5W0lk5qLp-(lG#e0#Vwd=-CNRl3>v)|aH z(tlp_Z*+N2dtG#Wwu!EQaJtDVTx|oU@u`Hv@l&gMQw@E-@ds<8^k>{9?tUdXNU7D~ zPKk;G9%v%OwXIk8*}1X()I68b!xqUX0p=NadY!!Jl-BoP#7Z%(=gB`id8p!Jfl-H^MxiTCW1^DhFTO<$duL;CIYg#r>T-4e@^Nccb< z&UEmwJ*Q`8ed+oK1e4Y^V^rI46?*s=!Sw)FJp0y0L4*<5Q>5)KGXf}+ZqwS1n5D|$ zPm!IkeQc#^qm-Bu)zJ!TgG%|$wHZJ&{OW9baQA)@%>Bp@6Ho`(4eaGYs@hUAUYa1e zz&A~p8*jFDZG5mN4kgVTsccYszUXo8FL$o)K4q?S{3hYP6i^$+E|VpyXBVdWIapqDVq9fbJ^JLXr8=zZIsD3xw7;Gq8*d7lUe}9Ph zY{#rgIV8>NQpNi;Y^7ZDb29dm7QL*cj%c2>>rvyg{_*AE#}>|N_X-&ae+}Dp(S#`P zs;5*d%7V`~^iY{6PSw;Y+{Z(XNM25LsZUTcU?!eh@yV%M&9u;+Vk)#_OCZ4XgV^X_ zYnH#_=O5l#g<2q%jXgWgWJDnHxPf~ST&twM2L+q&X9iB)Kg9^U`hvJfJgGcBG{U1# z4!@5ZP-t%f=M3rGarNokmNGVT@vA>&dDPCRR8p=$qp^qh6I*Y#q=Ez>j2qfeo}=7% z>B|#EvSj;0Akm>6^nh8kj7`A6B1XEV5ZdP}IGo>2Vs2hhlDlJKXS-J>1V1Mm0?L!}ST-D?1p z^~Tilp~qZ=-`JCvCDEKhO|hPRGbP6O?2kDd9&>LWyd{19iDc9VASyu_rg%$=1p1={OKOcfWTxv7Dk8=eXg+LzUOz#-}@Q9&ioqu^KH z0Tmq2)@uq@8hzk#0y)rw`!dpDJA=MQ?qOF~r|C34QM-|(!A>rw3~b_N%aEB)AEjrj zZ4$rfwRv`j!rHjOg-MxqFH_?eIe*-w@;hj%>4pIIxVUH;eKUS|`jCKcl&2*EB?k`i;5zqnzYX zC*)j}-}5Rt&eKrUS}grXIT4lsl#@I*`kM2a?j1+}JBDU6*?R+jWD^au=!?fL%GFmV zH-He^i{jg%8+><@cMk^X8U~cg2(Za@&@h9&FZAy5ipmv&%w|vQ7=sDxtaqLheG0l9 zE*3ALJ)fjtkS^WC%;f!VX*Yc4_z&dD6DZkOzm@lYAy;`F#K_UVkSl>dz1{FegvSqo zWT%e4r{k`(UX88U4MtrwZZ!lb=V`*wVWRX4IA>HZS?!ggMTH z&c3rHdrbIN#kle|dK9BUN6)DxwLmyU_-T|FGPBR;o)pgx6E?Jq(TLcsQ5L{0O?vt?cP;l=^ zp?wr0Q)W2uA_$dc*+)~rol2>joL7%&J#lQ(ng&B>Vp5bl?P;c*I^4!#F{l7rGZfvA zMo!_LYhEv%z|-Y^*m}cY(ht(OHl<7Il<_=rBDx}0LXSYI75e#eRqiRO8t{pJYYTUU z7W0Ly%%`_v0W77TjG(wFC^lsf=hi z{G>%es5;~tSdJX_Zs$yeRFJ<>rf|CP%EKwspGfnHzEkAW0o}fFJ0DE}9?t2qzz}su z6Tddf`bCP%0#((C`y&mLT#?SIm#9!LOiWC;z_|}P zm^3649yAZ?M#a#(F6kf`;14;@B9Ae~&Ek|1etR&{n8!CU=W$dn@7&8+GLK1R?T;Oy zxep3lHA%W|Ju57}EhO0H=hSDiN3CHZrI_J0*42R19KOI;lXT0dQW52FT(aN6kn+@s zOh^dknRvtFUK#RKsm8@~w|r~i;Q6&#?WAHAqIyiubUi%txJ@To^4du7ams7(&-t2$ z)t4W!DcwJvxD0I(tFg*xiYp<+P)u9Rw){vPwl-wEEv`Xt#JvKQT3%> zNRSrFrnmIHgF%A351l%C0;gMzQ3)Jc_wGW zO5N^?z6keR8>lsI)#!@>@O3v!Wh+BwnTH2TBUX)lj6J}am*W>H>ySCj*!PnrZ@)&& zcW_C!16ZtqOxMZ!(xQ0>x5i_{>2q}*xrwa`fY;`7t)bua=@EMc-M(B`eM&GuOo7JI z*6;OWtCmDugz<`Z1~e$063}dOV;jR;n}q=(%1WWxDf34L@7%Y1nX4IEOn7)U7qDV7 z(6)UZrm?%Qt%Xb-Y-DaDtkti%jd!?4rXcm`jpf@I#0A<;tEmHlTzEp>3 zK6Q!NL{6b*Lq@-=cae+l+cvP5p3~JXWmD)tsC1lK`27bFT*`c|s;EL2wJ}k86_)}= zzlfee$c$+K{kwN}+jXezhz50d^@MeVl@6A|>$NYcajB<^c+C#eG zsC$hlT&JunY1BPart@e|e#5boQ|_fv<$XCMjqzRyUgo~^*ln0f;09S7^fA9xHeWPR zpfO^%Knxw>+uByw(v^5MiVo%=ltphwvQ;XWQL|a5qJJYt!DoWD1j#A=875rj&Z)zt zjJBJ9T}@l5fpO1@V zBTh6ZPIWz+mGauA5C#`)F5}vLXh4AI_Td4`TITJlDOC{czy04&+WTYi3bb7)^jrtrKp9 ziNN)B`HLLt_U3;MO+`ipJ`mEROs%%ZN{bF=7h||u>FwfVW|iXZPC>a}I!)D=F#2J5 z282j^tD;O!{MIXPZ*EC~`n*%T728|zva&-|uzATi(X7P#5{49#VDv#)-K`&g1_JTwzyG6)zg z@w_WXhbVRD+GZ-FZLlR%tyuO8{fltoWG^yLS23A`nV1^T9ZZjK5g#VE(bL$^uf1Q? zGshlaGK4nOuGh&sgbDaRf>^1``8*%C*+y=bUu?T^)c&JF)J>?%iIJ0gALiWBx%!yE z{~d4yhJEqX%huDOS(}F(o;q^enqt=KOAdH?SC+-lJLuKqe1)1kuymJrZI!f;cYVRN z)y#xud|2(twq1pcCijCl8sn>ve^uLCn}}=CDC& zm07#~%$>A6tFX9|J5o}9<%_%JJ1(_zz0K}5Npoxlcb-$^W(Pc(_cJo<x)z=(7PV}!uW&vZ*S z0PPlo$t|uq%v^^zS$5;IU++!)ei%8OD6XAn1OxrB;RW;}Z+$54~l_|dnhTF7cq>I#M7VQbjr2IhmrvrJq zEEPDLH8@qbijeufR{w4}C^!etbaEFI$wQfBox7f{wStr_Znhy+SZ4|sQFI*U`b}Be z`a@aM`;!R*uxwfQ(%%Y}(4*f9Ryl+7f5~j)j?Z;01(-WRSBh6vUKBim%I^9e4Ci}; zaN59pgMTBQ#*4rDSvOjshL9U603p1Q~J z-jhP}I-z6zjS~P=GL;ImAlu#&e7&*_I*-(w7?k>siGhf82*`VjEx7 zk=VE7?rAe^<1~lfcl*+wp6it82@x#mqf0Lt@e;WZ%0C{;8r&`cfIYKzBAXdIJEg$G zO6c68@tsj%4%9`T#B6#ur6@^@W>cg(kam?*#>;GK(VUIFRR*%9!OKqT*xYfIwL7u=QbY{P5l@Yi0dbv+NmG_B zX5IUUpes^9V#69?Z2PDol3C;jr7JbbU{IhZG2S&TQLBy&z-C9F8M-hV7 zJoyk=m*8rhs!bU`xoL+6a&j>{Rvx_~|5V|q=mvfY&+C@{s6dsPP2DhK9a9wbDDyg@ zR5m&XT@~4zkR#51g*v>h<9N2-Yx~r>D4bP!No<^%`S$4pLSaN>U#53-v2b)kkH$jN z>*)R4dgmb}(z=K3ly!UGO=Dgrm``Amnrsi&7j}OUFhBS(GeEbkB_osk`0`Jwv)4>+ zn`IZC!$2N^=Z0xbHFNZ$W0PzDM=br@TQw%_6w=izuxd}M>CY-+im+p)ACi9*Ca-@K zreFovuq*u}Hi(O934ilGF|v_%9KR8HcTdlJH&N?(2Z2(}LX=%Og|sn=;HQHN)g|i{ zv17{W@TLdsu=z41_t5hlABPjinuGuz#xw?N z-Uuas%u@bP-2rqCHP|b<+xGrN%q{jUDFz!03*9)Ixv$~b@8iGy{@bxFvY@1xx7Vc7 zPsVSz!2KaY>dp5HS`@w=NH?iv^`=d}5}r0T1LOWs>5JULf$U5*y%V4LFVFsyjko%p zjSo89w5TcV4rh5yqftSfbhQGv2le&LdKPb5S6;gAGza9WxHToVwIPZCl-U$-S~>@D z>5IIs%zv3R#P+nq3HNdEb)gm2icVbK_f#N(XG}J!@~nqXx%Y+r-Jdev7j8Z8g1W?X zUfjWOGoK|d;>=9L7^KzQL2mV{5{<&WO+}OhrkgGYxKExHkDs6Vp^}HBqz+}EX!(4@ z8uO=x#U)c*k!vTb)0$c?G@mQHRcQ29;Uc^5A*n9e#AlVv&H$f zvGPhFUiU~I^^!+x|K?+qwCB zLy${7A+GsLr}u(o2TmaVF$uG2^-92YC<}PUT&eO!dKV=sP3{Us7%O&V2+IF})i!(h zhJ;KRdywmgnMvK)={OxBKfMxOB_NgdgGm53e)uU}-b5&AOKpV-qVgh7@=Z`9n5X>g5B7 zTYDSvi1fu_oVqdiK%5b8zts^+u~y|{5h3E@OVN>>GX0AHN$2WJR^mUF4CW*mML8pQ!*I8>c7~3g@YIni}y=i8k&42DqKk7;ylFJ zPmf2J6zNG7o%MQmLK?vb6vUje63ZhYZlrFZ|Kf= zqWU~3ndQFqp#Sub-7e0!v)Ch?6xAC0y*hrr)hRu)%1vyx zpke>5O$tT~CH{!7A@T{=^`K9EtynexRTT0`&o2V%=A4I5VD-a69~l|(pM1xRJs))= zD(h0AQOv;{_c9Le_Ct(3SW-aBABCQT^ckMyqIK5vFL8hH)2ar(*x2`9G)^)(0pQbm zMVA8RQ#)b5&U`nb*UG(-?+^5GIF$*+)X>2YR!$v zL)KuKqWVEMutDwK4jMm|t&5{XXU^IgGOjJWsadGxjMw%Y0LgqL#mkpwfW#KHP!&hnm&Y5A#Q0ixCs z_tQvrQVk6%`Au3NIdll-SLIx{(S|Az_pXM(n%DaC73BNyInd0#VPqPMMPg$ zQ|IH{ymIMy^uV-bu;%k=HMdB+*vpSe6-7fFT#nyCE56wOeD3o6E7@Z(?<4Nhai^2R z(aDBESJf#~q1F?pTK6aw1ums*h(9|jE29IPog?eQf z_WKqWJE%6>jXx3h%|tyQ2h&6^m|cynfPK<}j^p=CCi!~K8-@L_UFG7>jnZr0O8g>d zmI#4BX4Y|8r43Th;U`~{-c5edZPzOPsNP@TJ1jPY4$wqz70~STTSiU=R4Uh3os92%3YxoM%X3Ye4(SBRKI@W*fw6egp`+cqyMJN zGlY{`t%WlJ!0<$M|D9*Q2##ej6Y*01EcB0~_^%~((C!{!br2wo8f|lbJ|L%A(kDulv%FOg7*z|d7!xJxjuaz zi1+$oQAMg%J&zSdJCCi+%!C)Pd)q}RAQ8S=uPZ4Ji{5==9@kTO^;br*3e6&Adm80_ zwt=5&$x0|3;Mw{hzA!2n`Z7P8 z1FpRVHb}*3D_N>9sxx^HzsJqMZ*jZYCUOluRPa}8=Z*jI(=ga^e(z`P%S174Siw3t zxomqB*AeVOMx9t#zvHp6H2oGbgG@_MA; zYKJWGW`Xd!J2?7w&6$|O64#}6+QpN@p}rQ>`Zi)lIcht2^qLO_BPSVXmzbHG%2B#< zl}YrH^xJ3W*%5N4kdDzdH5n|;H|cNj?nU9rp;`}Mzx4^}mj54pVy%+x)&H1Q5y$?a zTCp}u{bJ6wDhT$av(*UI-$*N;e~?x*YI8c7TtyExWn`wpza}b-1M3AgL!?J5L%n+Z z1JzU)Wrmw9;S2i4+oc~;&4|V+!u0-6Eys)SXWs?2Lq_*TlE_u(E7wc^Y2{b~h*q*G zFVclS@Rzp$m_(CeE!p%Rm|OK?=W*XPOv6?_gWKMxYu%xr%*;jW})>@rJ)XQzG zc3<=RZNGF%B0pSc1E6478(Dp}e#1MeHc=#a?07aFNiIUk@jOaTIS**I%C%fM_H?dU zoUsWPI~EAy7^?I=cFH*QADZ1bM_QTcy6Ij*-?GFqmf?(?Ef;YGP`<@WHJDaVq5h8XcTXaCZdB!0SwRsLIj<=O^i)9Ns0H- z$?9d5KpmK3J`eY3Wx3A5@lZFTGs4EWXiIg{bMcQv0ds`?LDfn*UZAF}1)pJ(YhIY# zJ~l_*Za>KqUZDG2FnyEl%kko$gF6{Xou*ONV87t%%4LlK%U22tn&lT)fr^YfNh0!0 zci01?UU(`r9-Oum1dF?^pq@@N-gb5CqxBd&yBbEx4=EnGn9&)i>a9)UIo*9hWJ97_L zd?cD|ZZ1jutYciGWlE&$=a+mYeuJTP!RCJ1(>6;1b?QQ-67xmA#6**$V_wu8JVp3v zT>8yYeQtMe%>HgcFCR(LbB_D?Aous5ZmFj=YO_K7@-LPZ#*4jpkTA=qmA|v5%N_+$ z*b;c)enhQic?;}BRmq?qfRLDgfY3R4@OJmEdlNyarHioF~a!t(1 zs(nxBV|3FKx#KyObgnXorIv!0Fq1IjMYRzY>fGeUj-302wKl!WFbTgi8wc8ja4XPu zP50emUAcR=Q-Bx`r~RU3;{?X@{~_!>yy09Mw{3|ONhE?q9VNPG(TPDw^iHBhi7rt` z?-GRQy)&YBqKJ&ARUEJdfj4!1I)E z9Y(`|)=++ZfA95k=UJ=#sGD}TC-$7K?k}H>@Xwlc!XRIlCC4&88hkn{p^FkbK_j)n zd`ja;;n==Ff0T@vT}yt;CNdG31$qy^f#-b#iw@fXACx+aRGUgM2a(Q1J`h`tyKsYj z-kzP%BXVLg;m&yF@|i=9EXynV`}TKil83MpGjLsBlJ~d?XY4@qt7X$=^lg;fJ%wT= zDe4GRJm?c!&+9t`hd17~&Wiy5)8xJsW>Dpzpr;kP^M`jX{4=s(x4NHjMc)VfW5X!h ziCScUVpa58PMk%SqCXm~XlGlE3v^KvE~-INrYuE29;G8nKNG_p?e=81k_F^qA<$b<^d-uPGH{E`?}mOjm4-c1wm;izuYAx z9XfqfK{R&a8rnFwA}+_GoceubkVWB6lo*|&81VTwDwI&wZ-F+tc5LN_MCC*^Ama9L zC!=vxe0Cx!9aopMn+x*fGiQ4C$qBLRv-S{}3361M2`~*KgNFl?H`aD-`ZL%LK}~V> zn(luC92rp#i!lwmH&6!+D=0s2C+)L5i8cq!E_||`D)>AS3A)z6Lg76{^nHqL>|!N2 zeGa>7WCK*hyhvMwM~5Wu6B6{)!K*#eP;PM$`TkC@f*BvEK-nkIE>y*H*T5%j{*r%9 zRQLkMU(XM}@oO6+9LHQa!5wxR8>nzN;2~cXCkm;`D@eGUoLy7|zhosZw>MJRCq6q7>B^RD+o^Bw)W{TOKe&?vVua%_7&w%8os^y@>}?lVba26ZK6FKn^evvZ z3zl=f{~J&1aI2B;+;T&z`7>XRxh0H^OMOzv=@{F?O={y{#VowzB9VEiYIlvgRb`H- zW{`3ge?V4_(Zl1#Py4Z3>Z>+lRX@{wspp6wDZQbc_~z4vhWtD6D8XLaD>K}kF=udG zcsMEs0jknrV4WO&<~UpzMZF++*BqMi|<}NZ)qya6;rA%mk4uz zuV)31cZkyrDtHl-5V_A_cnTcmT-xGKc>wJhA-{R_o(EHGpvb{9Yl2gWL&(vjC~z@~ zo;5jgP=#mN%ib!B=1i>R2$;F;aiB)ip6kayXC6!_QCQ25?MsN?E>i#xccalzQSRFC z$fCZ?*d>hPzK#6QrOv6l#aeuKxI+ViGkz&(szrz(;r;{>k3ef(dh?}!_4H0|0EG#w zUC1=cUyV22Q%ZCMbcFplvgf>3iL`?5dQYYl41?*V+1)QT^{IpKVn^>fzoSB04R5UGgX!%lQ!>?rLM9(4;5m7HNUKQ}& z&0}}^bz9_-WRGsOn!W#C3j_)>XJlegUfPy%+@mQ!$Q2hGJ|+z&q!8alr}h3-^d|46 zZeXXY+n%vK@kv{1U1uvI{M5%%qTUfcW`0KvA7|m0C|k;Nt0Ho5DN3)XP_{k$`o+vj z2f6dTt(B;3r`~eGGwE>7_WYC8TDs!hdhfaUT~2r24K**qpVeCQ3}5TB_W~+!S~#@w z({A4SrVWmlNp(5s4e{SC5vLojHd|045#8@&3~-hPpDgKDa#yL6z`Rdi0`H<{yylv! z$-Vmn2vJJ176sAD{B}D%3M98?!<*VII&AdI@#t)m!7oS{;+{pXv__9I)!nibC(WnY zt23t2I4bT{%r|n^ zk4RLA_K@eZKn}_(FIxMo*8sH*Sz zXjo19cE?Z+#HJ>PU*%&Uu)?K@0AgB9L^gxE=_um@w(;tyj!rkZfb)N1ODVT*KGV1% z!2$E9qJ~7z&i6dClCM9NM81sn>-(r9r&YJ$rBi8Z7VkR}&Q#F-ZR{2XvkIlPo}gan zY}|`dDI1u|(|cR_ds9q;y3E6|&tyC1|2Hv9xP*?#-F;f<|Jfi#$D7?eOk`PxiGjWE zFnb~%D*vIs$JQt$z3@crZ@l>?VDv$FJa>E7(VGv$Cy;ogI(LaX`UOy8d)Lm7d z*x%7ESxFMnfp93rQ)b2534WrnJ<~pZiI3*YWQ%`z(8!P2xkZ%BM(=Yy`~%~Owgo?l z^QVDdkEedDl@N2lXNS`x{8o>7AH0 zR=xC>5R7o-u#ZaJ$1cWO)*G(U!}Pc#8t zL!$q{?)myHFWvt(d<*&X$8(;308l#P_5Yghe%)2ym45t*)wv3&^sW4tsZyUiH=1;B zJUoEZMO+QGt$rfa8+tnOl=#z5kmoPX*88GPcGadc13w+G;jT)gwEQ!rOl2MkXYbB& z6;lwOoXlg-Y0+y^2ySgIT21MHc>jnoTmIq}*h8euw1a!ph27R-XV@c=V2wnK>GwhH zW??xO7cw4&!?Fu&C9)0wF3gk#BHDqY-clh(Z|>pUr^wAxf4MWD%b_{BE(}iy!M0ug zF$H=IyZrf}S%6+Jw(&6fb(dA&!&yE%+;)a7&N8l3G>&*P?}M>hqh{8I*|FA{QplHYC!+X(wrYbWKMZ5gJY3({3E%osHL^BliEYX9z0!ial<1 zMQ7Ob97pp@uotYN6}CxFFx7u{mM0}hk@n}o59>J_oZiK~Wf20?AfxNc*RE+YDrX}C z@-h-y|5)6H^Wr>#1%iaVNx^beP4C&D@2N@n@)^Z5#ETn&>A= zncr-KqXltiK!kZLc-~)N-eT{I;l*_Jm7HCe_x8Bsa%bJ~7-jS$iIiu=B73uOmzPvM zqPe0TaElCm?LH;DTI6*-=XH-(OuTUoa78rM7CdJ*WD!(jDi!*4{lLZ*<4|=$Rp}m+ zpz)HJO3}zg73CVom&E>N@Y@YqHA(SyLpz1?6a-alrnkSd*k=a%5COJ1A168$(yUU# zY;atY%-Pa!XvQY_O*5(B1!^Nt*je~uNED6 z-V~=N6-AL95%kAZhHv+et861!$W3wF<8EsqFqa#I)@`jy$SEY zdqV>szHudRD?5or=yAR6=+D69@U5q~LX=sb==SW0fpQVqR~Rgd=w$>wrl^aeLn?Nb zbK8GXfhomFWyRWI(VLcNZ*cuy0Pasibf0X`jxS;? zM0%8S?z(znYtVt3e=R40Q}|q*S@-+Peqj7un1z~%k~Tzo5e$OeJVLnhtHb@Cm3k5~ zl`4Ik^}~zn^ZvP-9w+Z`1s_&P*mlwO$ypVWu#loRL5tZik%{zJ?D-) zxNDHz}$vqEPxu}4k5p`i z0=Vv6dLFia*rkX@1r@bgHKNI*iG~d54A&QJ;Kf*;luYF12UkVI|H4Z|K-;e;>^~76 zOfd-({~*Tw;9~9mJ~!BCxa6cV8vHjN*Skoo7}HTlnyj|MWLkyl1pgOxZ~`BltTD|V zS&@w5sa!Z6mg=Uxf?2PuA`aOH+_!=$lH-X~SS83a9|$R@=O?qdOJw0nbBMbJmxmr9 zhtI8P4-xy88@-~rV^m9_HV6K6$lX$2sUGgI!bSweW za`NcRCgpNOJ7G0~neLt)2)U=Vz=C?hXT`@r5v&;b#qf+LI@!>;zz=^G+j*&0VtGx) z`7Nc(@;P1VHUK;6Y?omz2vfaqgELm|C4se#TgsUFqzt%2<0_R~+ zJG_{mbTI+1dRi8wgQ#-Lj7xnt=XU1WkldcVv-Y%*Ko%p7sj2C*&nlB(B0F4DqGZFW>H*oIuH6!DzOy=f2zXnn2;E`(|i!5HC z%C+pibwQ`U0&=JXk)307TdiAl)Ez#P;oc=ro6U+-91gdRm|z_28$b^DB`y&iYt)o zB~bk3Pxghs{Ie!?)L3DOYaXG4#}|IVCCgdn1tX#9BTj~ms$n@fr3WP@Yt!a=`+;vE z{s%oxuLYc>GD4*od!2iJh^q-LJwS{_QU@vC-8?wo6poxv%}Fc&E`E$4rs$r9v*=F5 za9-_7r{I=e#!vy8KSY5O>=l7yn+kYCw{PwWd{tKxCId=m21Qgb&$%C*Opth4F1QEn ztji>5h^S7^O~gm2rw(QSp#xOV^t<<#CgOA7&B6#QRLf8<*Gqy(>r${Lt396VLtLER zPc=<_!$=>iq?gUgx?|bf68agEMteafO?~AYpN5>~OqbY>b_a zok#Q5k$6^-mk~;`L5ZS+a9Eq9cAK%CPT=}{#bUfSra)>V;Dj{2vhnv9qRabTtyzb> z=9Yo3f*VrDj~-gu8Wv1?vpKw&hD#@b7g*!ZZCvA#V~mMQ7e)>4(}i`jrgPi8n2?nS zL_wKD-1Qr^F`Z?2vXHUOEoP|mVuWewc_t8|J&pee8`P8>?%y}>N@VOlRXme4yx&~j zlB#21(w9t73)S4ZNUmg!J)BKhNO)J-0qDcZ7l#s9E-lUk4$hfcIUGVH%cM1eWg{M^ z@H|lJ1*>b88d%TH$F3QbraxL3GyFBl5v3KLlwo&s4>p5h^QIces<1ynafeLpD+NUiw`h^)ofo|lGgx>AIRc0b^h)37yx=uR=p3(SF zSRB5mG~vrJ(JK1W!I8h?a|G|o7mrx`vb<)oG4!jHmG*fR)Q4bRX30+vi63cBF1DY7 zg?7hb-j@|BL(85|^^kpK*pP9I3}F%Y7yHG-Y^O5V`r5s4@^vZ(8)51 z=?uEdT1QdSi)R+($$73$rxQs@6VzGg(NN{TY`S{Azr#9qGICm#!V6%Ii@_9$%3_j6 z|GT2BOk^)=etIl9GtMh2Hagxxth;`&rq>qI?L^TX&UEvWQ|pYUM6N!gMWYbvHOq=4 zviOd)D8oC!g=@oyS-yG z273S2e80Ls6prtID4ZaczIAod=;-=vF{Tk$Y`2YKPM^lV?y{oS%nalPl2l1AWl8=S zTG{;Gw6yo>)foikz=)&?Q39oJ5Iwo1$^eonjmy&YVm44>_j}YUIn+PgjpiTj<|8Tf zBmL3}$)ghUgEJ_t=-%1`R^S%XfeYeWJDjEYpcAFY(-`$Tn3|A(;OEmubAvsU8|>z% zBEiB+Vyh&`;q>NtRsvf4wVi?w!GEP?HUCJ<9`=lf?X^O8FF&pQ=>FVC5q9(z`WL%D zLT9x_1QHX<5u}->boaa|XzY1Gx{GGdV?@-8|DD9Si&B5a)uY-H7xls@|1sc%{5_=d zj3P+)pHtt}5tGm~2{1bUKX@EX2(P^O+bZ)X;8w+~!x=Kk^W2yqmTU}<~6dB5zB{HR4ksHy+=ZU2!8-BW41a{j6_JQ(3S$O|7693kfMS=9p zC?V49bACjbuYjRMk=}7;W~#b6MTv!%H-=I!MET^s(v3Dd7mzQN_g82TF?v}Hr2x(8)g1T*(mmmAG>>K^EyuV934Pf>nP5d>6N z!5h&UQ9c#;laTYK+j|=GMMVUet&c@CyG40wH=5OEbNESeoi@Z+c}}ieuqwR& z@j-sggkYXMsETfr*^cskxUh07*zC1WL;~713_dY+<{ov9N zWcT>{ifA3XH&vl?x|3O?49Wj3LpmBQVQ0(za)z-RjK3Bcz;ug7$b^c_RBaoy&~mQq zUq&}4A)gQoz5N>7Gl!cKvyuEiB}whM&Yk}MBuQEh=Fh`M@BP2X$qI3M7FZIgS6#;h zdDc)l23=Utr_Ef4X1DN(`L@ZC$&VN8J)FAoSPq|*sy|c%5);T^!ywHs=eT=-!6bg+ z$mVc7U&f#!xiRGb&f%N`9L_j@^(@c*akX-q?lZKuS9Ix-ZJ=F!5>0oA0m(OCWox;0vU#NbD8|BmH0Mmrew6K`v)78u4 zU9`S8j~eev_%@CCvnR=`UY)T%A!8j!Bi`jbVApSYDQB-5%@YH@^glrBvyCKF8_Rfe z1rxroh!dtr7F==o;cL8J>NZvN>Mdh51)=OP5rWoxistg;iRdlIpE?wV{hcM&2mUJf zpOq~YZjilTa!*(V9Z7hP0Ml!=5ywCIMcqYL=-g2e-}ycm8v3$fE0S||I^wvn5;f{AFGmiGjl5Q} zP^@n$HQA#H$LhcnMTMqU)FP9ku5651uCagPecf{NHk{1aRa@p$HCpuUp%dd_XqPY+-=recf)thtf-Pe-qD)ZT+`tc=!9yL z%zJLKw=?CRhaxSu%VwMD)S&$$C#ffm@dA|&l#eOYBhodfU#fPvh_8dy)%rPi`Ud(q zjYvMVN&C9v-1F?*(ybRe2%AG?vy~xFB4wjp7hP8D00Gnuc;DBzAIIf1?rDif8 zI(?j9A7v|YF#A{XQkh0VwVZ@PkKouncj{VoZ1iWWKIG6gy?u&;{;ds&rwfm+qblu=pELZXCLg0U9lRQK_JYG&-!jd+dMpZVTn zjJB=v$`xXwMG9mkzC}mvzFdY}(@ZUOb7#a`Jue=PQG<%H`-$ny(rLjrsg=*n2cY*f z2V+IX0}sGOA#{vzs=~1J!|G{X-$u>E_KwyxlJ4fHxgex-hQme0^8osXlLXGQ6*I$! z3g_mMVS^i-hV8Ji`xy%Sn481fK0-o*WtsKUa1gi3ua86G_{l9XrIq=PbX4y%i<_dW zIHx14n0ez@c)s|Bbc1w38^tnW$|DD?oNA!V-1n3I{|QJb?k1h`{l1S@b3=d#LM!(bPZDS8{H|E$%Q`Rfv(;HT&||k+OL7<`E2ZNm_uyE zit(rG^Jaf(#D+vB&Dg+FGVNTzXLLMdqq!u`$#}D;K7x^pwT7aH&+zN{e;+BdWeUR! zm5Kd8PQ?a1(k$(y2+14gWTVuP*$s2%^Rgy!5c&&xbvqw&jX zN)TsjFSh_=eAKMtWC(U?!lO!*&{Jqy@!KN}uW=7j_`;z6u|UCV0yQS178|x$Db+jj z;?PN;fnIH1s2U2S>DW8`-v*-@#A8Qiw{uxE`jpLGO7v9TmLlLgA2T3cR`;!5hR|F~ zJ?aKmLT4XkbM7b679lDq(9ctQGa!tOhvPFAV>cGOC?hTElNFscn`F=E?o<`lBLbhV z^z>*2X_P(|xr-cXDoY=raJ=*JxqnUGJF1!}|J_#-rBxYLcOBxn#KjG7^GbxHlH=3S z>m@DjBPAHNruY}mg7y9RnfE;f>VuAlZz`}mox=W*UB4=@BRGqo>TN#2B*df|)-wg`P1CRZKFGgb$=tc%DQEpV0oWIx6}?*OC4I0C4XS`km-SD>e0 zV>~MSephwc43)aX_ER}mW8=o&L5KTH8`%2cbX8A}CtfdIGT=OI$8Ki-*vU_~OVYJq z>m`0rbym}8uuEYA^c@u65c5A!Ls`xtNWwh(X4-e&~*;fm=cz5O(ay;pqZULJs6K+`U zzX-Vt{TmPD=JCtFXcuSNYbPA+zw{t9*@1+9-#6`a8Fl*0llgdg;glKKmrMRP-sfE* zksK>Ew^z5qm}tIuc$`NAm$S{^5VW9?zB}w>UAEXI4AxKrxP7GN&QhI)=GO9*+?$jx zSEBCI(Rp$q>_yGKj=EYMvl#wA80_e}sw=>sgV-Up*y462<~sep-cUhM!{j=PW(f5W#6K&mK^=-`?PJ`%U`HA4ODv zgm`wT|23Py>jT}1spBRIiSr#_4qUCT^WLg~XCb41YmxyIM4BVunN6QOL71~i=~4$( z!E_d5vLwrLB0s+GDXVaS9nCLewP|ydj=@SuGUfiPY-u{@BYna6%SV0pOs77qd)IX7 zthpPbWZTL&FQQ)^?y5$Y%XhYS6Y|p3GX=5yDA4)!-5I`#)%DBDnhCY+D z9b;aMxc8Rqui8A-%ft@UyX$lYm}Pk8*E*_^iau4YPhY5kog_P)2_l?{qT4GbL34si05cIlnR58hN348@pKR zs6_E&k6m-kr4FVp$c!GF1#lD@K4W(+`4I78`3uO~yT;+0$v1Q>&5RiA{G)`vei-;D zvTZ;-c}b-W#Mshzsl9y9;{~VV0<`!fdHpSJ`KXoR4!*-!I2lEV>H--bMgMlk!jYl8 z>i5UXrC}HTn`#)vCo@$NOz4d!=r zb7y1T1ivecBQmkntdk2=}1s=cuq(`buXMP?z`64M>C9$-nYzK-$uGO`T6+43V~8+lz~>v{b`*i zuQiZS%DDvcUc-9M22j^V%{~iwN zyPMBq%)Y@NC)nw^c_&^T{<-g1{apXK93pk@KK^B(Bm8?LNm`Bm^T{1tv0arCtzpZ- z3)B8@D)r5CZjZp1>s39P9`O_%((A8&w29bUtj92hImOg?8tJrif2SseZyzZU$^(i* z9J~|VZY9O(#n(#mt(N`g(3#HEISRt z>8h1k#kbuGZ_B-D{W^**)&`Tw4Q$o(S9mZ$8;mQ6?m$&5 zM+A$cG-T{VIRBy$x1#*ZWa#E*^A(tY(DDu7hff8q6;-5iSc@zf=cyJ)PA z5@R^m!!JnjHh~<9y&6TM&MUz;O^K_MsWj2&!?)l}RwNo;)?1m4WBoMh5k^Wokaqwn z(N_8=V@$iTjWoM{0WSBKxs%EzbMC{)YdF8=tmnhlyMg zBi1i``j-vsy;mFPfZoW-FS=4Y;RYOplGEt{r~DV6+8Hp}@vAz)=44MwEJS2_fCv$m zoRzqE<&S+EgNQGswXH^VXyuYlIkapn!0rM)eOkueo%VRbmX)waByMeDeDymbM`kl| zeUs0m@{Bf)A7InYtrr%ZLX6g&Ocf!^@rIX`JnP1l@z0`k=}pb(tSgdk`yKs_M{{I^ z_6_{_JXmST0CE=q0=7C2w?wiM>W2sybTBIca5}|H@@=8+$1{)N#0_Y+Q23V3d%C=( zZbt4^iB&~1it+FhpX;=x6N$P;X%93A60gbj#=hrzUU1abpIA`?GK5d)s^J+Rd>&1w zb^L4EN+NqCgpO8Vm&ZSDYwBu2t1kwh_N52gq6iW2_li@VW^g^OPzU>EsTi$xw#1{X?I$l)+WV^u$?X(k)a_pnNp*ON20WA&(X!y8-! zr|Ao4f@C|Jez|u0!w1_V7=pQ z4Ni+JE%f1tQ*oCmhXK@OxRbtqhj|1^)97zBgI{#X4?_X&s)x3@>7b@1N*c$Hl43uB zUdpeE*>g9Qh)^@c9ikVvI4eWq+oKnBQIcJ<$T1<5IFx~jH{P9(tC&NBwwHM!)55;> zwFK5Z#FM@4SSCo2_fUx8CNO^fvuCS3Abg-;pr(~Jht__6f1bS)-#U$z^a>$&R+onm(4IJ!^X{!-nY zEKCOmjj|u#-~4FgpH$77nb4HauK2DOc6p>mn$Iy^-gGn@Pv~vI@ZNdFZ-EY}r`5lr zr>dNHqJ3oaK0yvp=pX-CjQPiR_LW`Xqc!euybH?L7`uR0Nv3I4vx15}N(be;(XyWk z(tl=DI^yUl*P?exm1Cp+zNzL6_}UVEC(8vmIK zy(lsBc;?O0s7OAs9$Vk`U#AB}7PG+~Ey~{lT8t4_b$Y=5o#<-#wp-+|F)*^#szkD! z@V|_~e0^^#_M0Qxf&d&*uy~tN@;#SInrr>lw*RWm#?!~YhIx@86R}dvKe?ImsJ1*s&X&aP*Xdt8RW1?tm>upYZno<2rMg z+VeMjQe$W#S_mOrX9gR%ZISG-_;YCb(3hTpC;3FEtTzDgiY%QAYlnG)PS){pTiHUyaES|7r_ETB$p&`3o;ol# zl%+H^U5orjXJ`E3;$wESkg`WcOu}~@e!xQZ-1D<>*C_>Ern~+^3h+W+RbFh6WI%c} zH6n!y>3{<*VjKjEwh8cz(bgg%MmIY+v^7#CDd@}PWxSALIX^L}FniK>(MsskSK=2r zbydezxXeiFJK%Ql5O}~WtAtJ~2rANzxt7ru4#kQZ{{ZJZhO73C(i~e~@V`F)6QDHz z9|M%_&Mw@NZzNTE4^;E6Fee+jBdefV4N4ai&{$9u)sc z@~J<{ke2AxjF%9;>u*lz@qaiWrE-bQNh{7qp_J;b&d7B>vj>R~_~+`fYkkE0j^pba z(cB-0k0_Ry$ydlKGC^^!Qc`txqeA(clo7E>zj(X9*?uK$Z*}y`YA#b5+T{V9zukGa{>SBv&i@qq^$8rg>*FL}tvbbgccmBJQM|zh0mU23Z^hexv}T~QhePt1Ktpx5eew81#c}kH-oF1&&Z4a`@ibu_ z^#Mc4uG7uxYQ!O9SFd+M3MWjw55K*!{5Fyu9Zbyh2mtp_R*+}2}?!njx~h32Pz z*pHPEN0^EJpw?&v2`@9noO3sBsBGq^rdgb(Ze$1m zS?RNW`~hjzvrVuE<{>YATh=0`s?0*vhu9MESrm_Rn<^S=oERn}4C}pGR0+ql(ngPO z^Cc5UgRim zHnnepro>D0KH42Do!#^JC7kDFsiu8I=&frkePnKYY07-vJ~8?IH4Ev-SN@RgrO6y`SzBVH$j|qomUk3$NVk01uA6+OvV|o9Z<$FWoPf~uuSX7_ z&(wL^nhL*&S0J;Jn2u{bi-q`@pOAHgt3_iE$M9NYY`#Q}05y9R+wh1iBC4@c_oAL(L_Q-g04=U>v{0{ z;i+Y8{eUQcN=!U~G5v`>fY`UnO)k}J%txy)o#MzybC?Un=S$?#p|vof$7>KeaEft2=5 zK>~G!e>-_zPC1*Kbl~ObgnyUmW@PJ(`j6#>O)nl|k_VCITt|EDu;WS2yc~C2TV}Et zEPuaWcZ{MR)g}o8o>1h+CC3kkkprx4ozX1Lgq=?-nBx^|4Ik*h6NWd(M_og$Lyg~8 zZkB+Q_(Xmw^$|qGHMUoRuIg+><9uz`FutO=w7576>dN;=cdbCwcC!aN!X);*&nfI| zN)fC{Nnf|;w7(s;znSwY*n45ePwk<&Z_z_c<#~H~twQu8`{=Ja@5)0+i|*ov+9mz| zN)n~K!n@Eoc1Mn{>MOAGtBj8DC%~jl)#X5k-d!#_VR#z)$hInBMMRn}SU|70-d9af zr}wtb?#vN|dBT9-&Wk{LvI?^$iPqCyfAbyo`XKw88c_L|ZieL>7JIWgX_eU~`ms(3 z!aSz(uD&M?Pk$CUp|1SKy5BT8DbXsAG-oKjUfRVOmTd#b`tQ_-k|>U^JFc&8HS2Jd{sjywXJv-8$C)oNf_ol^YDH)hd}YPUy7(3KFzUjpzV|h# zPbHQuURFcN=)DX+l4GV0L~m!{Ay1J?T18g!x$P5l)_CnxcszRgg&K*Qep9*2eov@le zO^xxP`Zl;~q~PKuX@{GMxbPNj?U8i$rudr4fLa8!N2C83|Gdp_%RhD9f0^TL+rU?0 zU~3^lF#HXKkHxC@2cw=Rxv*n!xjF@g5;N3xO~L-!olv7J!iM1LH<43ko?Nv4e&?H2 zUP%ZMIZm3qAx7TkZBMq(0nCOO$Q?faqXzUxX3BZC&3XrejMtBD|7-cv6UyWc6DcS^ zcBeey>#pr%>+v-EfFSf5RyU_rhv}sdsdoo^`to~*Ob8M2qZG;hsu-Y^Z9t=IzWXw| zwjItTfr*j#WJxUUN2?QsKp(VC;Lx(ot-%-GQEXdRiUx4*1?K1Eh;u{XLnGH21{XbWNdy7jC_ez>B%s3_QF# zGVt&bmgp@M!|4k++t*J)NT3iIWsN$2RlrNACsDaUA?!`Y(#Ky;O3X-WS5vdxDEiB2 z*FEtFlb0Gam)sJWr&fRCRYwArBXP;ohxOy|#&lS;ToYnH@ZC7M-=~oJCbpIKz=0Fmz%`NC zv!xckrQnfna7qma2!rcLqt8*XCJBZLRNMNBnK0Uf+k`_J;Vsk45fKMj8em`NqDc7- zQkaFgO zWFqStbf9uYmg$6i`Fj>=;SooYVBDzkAOM1fu$(x+kjb?>PwoM?oju5VNalU5!?a!OW2h? z1F<>ww(Zl=aL)~x(3MD)N$8o|X;n;@j{PIRtFxKp16>PVES9nJ&R8h$Bbu?Rmb_GT znqLiLAtZVB+57tAN%|7rfxBgs7D%PBjuGUob=B;n!T1eZM{|L4cX*lFOT#L@3p@O! zR(f7>FedEr0zHMErL00S={T9mcU~dJ)ybS%HnS#UN7>n7D1m*<3zH-TQj;6{fh^IV zS1!oQ0&)J|WtVrOvVqh!%6POE{}kmK&BXXO-cM1a=u%gwD<$C?o7594T2;|qTr6z< zJi~1Lf^m$nvXG)MjrfS5pG2XM{>O%PX(~^Mp~z{Q!r8}EaKfPr5(t-H`EzXGNr*Qd zT-LT<@Ep?7ewW}trC)9uNFc^xZCPumXk{VFj8qfxPJRJg+ceqzFTF23{93H9Cd;{wpp+BF}9iVYyNzz*yraR!PW1J)u`6S#&Xj5$sy|1&wUo1)Ph3DM%#2Vzhd+~AQ zw0)Q~{|GPoZ#?fzU%*l}gYDZEb@C#r3FIaeV>xN~d8?be$Z|o0$C+mga+7NV=o;M3uY_T>}DdCOVTSr!L%L^2`2SxU??Mk+5i zi5@iluBR~Uj&dqREuL2A-0s2oLHS;{lpwivS-Q8yv)Jxr*4kM84eAo`^|h-#=0)rW z)D!E)9rx2Kfm31P9!x|ieMv~r>YyCwy&qAchduJ_eG47s{Yj#^ahz87r7xx(COdm- zoV$ea`ZN}09?|yH_Iiu5tSa!`R{`x!GjIDmxBJmQqurn580NbH=zaG;a-aqGfmoM?$gc?^_UE0-*5v zf>AvoXp>lQYF&(-;{HY-K2Ni4v=f21tAGcd4^-^8Jmy=lHB$~VL1g5yLubKk zuMqWDZiyMF>dGW^*XrF?{X*x52+P@d^A@3l*VE6#{ITlyOi`4)=E`v_#cGUF@G&LN zm;}e@#0ZeU^p8gSs! zpw6~V7sr1nQAe{7X438WbCw_;$ziqCe#eEpfP7OJPDd1Vm3gab9l;&?M*qo@P5djp zW~&IyroPgW=vBL&oI^Vz%0n+UmaRas`MEXg76hv`hd#yyay+R?4nM=4&xgb7W_AeV zY!PI-AX58oZ&t4Qeb(Wv&KZ~2`;|)5BKCc1ESw|q=safaP*a^vT+XvFakHng;{bA2 zR`@X^X_>E=KR_x_`ZEJVwBN${L?i4YK1wmn=|oiI+_zTey+F_$4|k_?uagiLARewr z0$Cq%b*;2b7A?)fpD$;;`Nnlpf8jSTXdOl8K*iT2QwYV5T&#V{+^Xc(bk^D?1-xpm z_N=V+;UC_$FT9Pl_l)Au|kMCdM}CI zMH`)nGD?`}-C&gHM(=Ci)>_Z=TYK+!f8KxLp6kBmzOM6o9!FfhyyPF;KqMmd{4bp3 zv>xI_dVBO^&miT418p(f@DG33D*u1sBxM->#!2=AI7yBF!AVk5EV&d|!f9aX_Rw@Y zl2_OM^8L0RGhJJ~6T^;zs}&LU{K z9y`YtUugol48u=oT;XFC{XXt~?CU3xSX+}=q=@i6fB<;8SsnSH2|{Ns&4SSiFL{0y z!J_7OA1_G_B+>Tt^01(Rf1#J1rW%->Jh7|*@NZ;%dMAQCDC#JoU@&wQwOvN=B6F1z z$3<>=``UftOUWdb%HfDY>`GN?F^E4O>_W%1-)4Z^|0U2%@$&w@;9D{Kvp;cW$^*SB zr=6D7JC_EyX_9|kUHYF7WM$j7bNIxDe>@wwr4v%`itdHwSH;`d?xk}@Y#Fg>I*ZLh zF+GwmexZ}Ly(d#B-84zFIS1SwG99W13mT>&IqPDQ^@{IttdfuJ_~K4%I|X;$VcBO2 zxCfS;2fUpVuj1s>txGhMW0Y@po_+h4vB%9iU-8Q|M|2ZGz5eRZ)pT0+oNvq_!dARr;?wF~`2N_tymDj1 zLXw*E=D>c}!S!?HkU@&=Sua~WZu0$Q@0E`Ik(2S`nQ>@`sDjtmsK>d>%U-@{nz_wS z?PtOs$e8=<$7CRNNs zd-{xScV#Ow_`%AHKg4v2xkZTter)0mJ4yG7Dkt5cNhp4giC|jYl3SE6FY1rgPGeE< zi;I^=!NmjYWnFm#6h*{$p7UM-gJv16m$G%!y6(yBaZKjAfIW})Pn^UYF&`hjPdt72 zJ6>)HP2En2t;+snB3j@EuV3cHNo!HV{PO0Uoh|GL_pX0?`&XAL4IZx2F`>&i--1Ul zQ=_OgT$4dYU+5T0o9uU4G=Yw65_D2f~$7$b9l#3y`|{~{_$Z-+7Nu9es_ zFewRW!y;WCX-(?s*9s5%rYzzbI3^luK8g+!`zWK&V)w>qPR#3!TMy-qNrxWs-_r+k z(cm6o^qoRwh}YxDv+wvGDE!fFWvj|H!1qzVf9Di*u~`fneRn*lyqcbNEF-8KWT8i? z?A$n=M~~JFziW}@q{+9M-e{;;58gj@+%7=gqAa0!h*RTn`x2WiDM(S!I^FFdbfEHb z;O;B)vFF9c3Xg^z`EpL(Ar~<+x#0)+6;vfM$t{uM_>9+tb-3B%%y(jDF!96{(NAGH9p@d*&d2EX zFQA+wPEg%V;~UiO9sdGrZm?buL=$95Y<2Ix;`^n|iw!kKf^QL3Ohnps<|l4BN~zbeBbamLKkfdbXaf!V)C2;LFrE@7XjpN<;^YY2vD_x;8KuI zL~_<%0Y z-Hpl5A^w|d+eH{88KKcfH->w6oUPjPGd(-N-b#Z4j}72AMIV0lXe5bT=uP+okrv*d zP&xcW5JSK#5Ff7nn-JETN#WwOANy)$<6TadpQ%usLkJY^c+}6RmW-(Tj@wmm=9X9Y zX2~y&`E8fI>~h=qpEzqKXRd|MnCsvjUdDBLTMr==j|CNa3h@>=+QP-gG7QX9zkgB5 ziIU3lBhx$xsMCc%zaef$e8X%sDA6;75JkClo)MZCj>H|NN6vG??g{krD$v@TaSq`z zusZySGh6)iif=bxpuY1D*?~vWZ6_mfmkWS@Y~Tj5*09DhAG7hke(JqUmCBzucaF;b zOWFK~1$_7)-(alGU*bUXlQF`ph}c`k8sd=G*TByp{ko7*KJW)jdcClp2il^Y@>pzB zMH;X@-^4m2P)7{Ue%{98P}IN-8b#G?!70fU*H2@={990`+j2uG;B_JH<`=sSq25H$ z@vCbl%{%t=D;{shv}bH|PK!{`%tR1g4Y?Vi`BxLuMmC=aPxl?6bj5R5P7{X%U1=JR zoe^|3J@vd|LfVGZ35b8+ zF7j!HqAnZ5TO$NeY%(%r6CS(Xcl_E0|ALl1M1C@_fX&E44)iCa@QPz>@WJNN_OQX{ z!qbyx`~b2H_7Ad5AXkcBbl(HBiH?+oDXakpUXBBl6~@3(lHO-SP_k4na7TuGr_C&3D|T%|O{l zzaTA7h9!k)T%Cjz-O`r>siiM70THixerQsuEubRM230%YXtP> z5j-s5X+8NOdSjchC#wAmUT5|v4kCEQrmWoei5~M9KG?`^IBUb#zGfNmHZ|Oy<-j`} zj7^E6z&>I;I)DM)l9tm^(L<*~81|-iWB-vLe*G_Un>iwEX6wK}`M%8G3-^qJY1RRUp0nlxb|DfA`4;19B%#s7XW1x6bapB`2Bb5~dFrLBLfpC`B z?9|bwCt_UiN598JW^@?mZC;jSH(gC{mlWoZ#~ucV0D4R>%L_%L2QTS=9)&QS5fub( zW}g^G72iqCHU?-Oci%9MWrxlAa(Sa*r!dn6^3Ic1kkk?XIm$tXS0vrq2pd9jJN z7NJv?(j5BXPSxR6_X#hJXQf-WwpC<|T(svYodlmh-E@=ldP=pJV84huLcPZOKuqGr zW`1BDUWbPY7`kqjvO!Jc45{w=78enL+{ZoqerTCzCl*#t=L2a zL$qJ~@sc9sVF>r?8DA7ndg8?O!#{DfILai`aSNUI&lOzBlzqNy*QD>WMD5aRR(cDO zT~i}zzO7hrZ+6En%eKXo?E9x8FZCK{`dJWZZ&eE(BI^3UuBed=#l5ILuWrME&I5l- zP)?+>sf*|j9UFHIpD|3R3Z0wUI$R>=czWMQ+`hMNpneK7OWmMBYdBdI1O9yH$ug-! zyu-%*<>rXIh#wS7D#eZcEPqUYcc?568IdMvDr3w4EDT|FQ209cZt@(-FVv*h^x0d`rWtL*hDoyO5@y`dcV<%qUr9Vkyv}YYF_b+ zVXgIQzVRzy+$YSt9MV2&*@%4FDq+59aM|o2X;#j0It)5@9jIAfnToT_Xm!J=s3Xvh z=bxgu9zM-w(qrn!-=5DR&)IW!U%mEX+;K#lF?GNHK8>f0f@+R*2WUM0LQi|7v1s*2 z)flMtJh}JKs~MhenN>`esA8NBO;T2!y1KZ!o~H7$OZ>E?e`|X8mGsn42uLTjB9f!d zy1`dknrgV4kd8;M1jbbLy#Zfle`XJ+I%KTgy2;Fwj1i@zYfvH%b%VHrXUfy|vT`i} zYtIb))tn<^lXWF>^cIeo|M7_!chJy$-l_Ub2RrbpWNt*%PyR0IE_^?qGZ&FR|x-1;(K>-8q2-vWR?h#r(Ej`bG#i;fK0f zP9-*3vb2oBp_(jfu1>(v1H2G+sCXoKynYCm=^|`UkFZtv;!;LLU$QjuyS9H@(w2X= zbIRKx96VT>i|S`QXtyIGnrM0FppaW@zN{mD7X9N%FZpbDRVXRxf_2~&&~z?y88mF; z*)ak=3DWtL6Gmr%au$CAO>!nOM6PSk*lsu(?suycN*32YU2OEK4m41udz6TFNc7q< z`ds|dhE=?{$#_7ra2F{l(3e&os;$}D-4$l^-^S3iXEo3mG6fn#R_*kzJlcjSY7GAr zla4V;vz_sF@r+?M-*0y<5_@eQ@^I@}&te3H)@{V!D9Jy>q^>{!L}#x&Bz6vRNZul7 zeRx%nAE$P4DP|LEpgc}eB9VU2W2v>VBUAQW5FAGwU6|tp`_eVDh0Kt7?TY` z%@)BT^Q{kibK9bS;)I88dsIbOMJ5`~X}!3*OE){`_o2mjR0w(hQPW4z2?hIYX!z53 zL({4KKoo1;K`CvJO}J0f?6wdi0kk03tWj6-H#64P^es58Ik!c2_VY^I5Zg0>7&V=> zt7ZdnI3g`u_g%4_Md(kSqqVWi-l3^mSGoN%Td_;~LYBi2A$)_E4;n-C8HK!f@bGLF|qr51M=WkD{E!vOcSK{8`BJb1uN)(e3iuKpyiC0}} z_PTP5Y&kS+UH+yXI{Y)@j8vf>K))Q9Jq`E7#PFu#WP^z{3nmm*UBlTm)Lid1NIzED z1?Yy}4_{^NcoFx-6;>yr0(s{xQVE|+6c9-s3Rk9xIw{|lVK5ylkanVVPm3@-@$^ns z(27$^B)LW%jVn0IY`_~3N_UmAu^%SAq>WEULC ztxrN#Sv)A&V+Dg6zjWm)wxL|*oW8Ae5F}Sw#j}LIlQ$YTURYDR%OrQ3f!(`&?$SO@ zX+r(-OWEog!FwMsSIs={9vKxraNa~x>mOZBiKh8Rn6$gZE5w+zgO0B!3r;v|KmfFT zI7YGGPc4+%8%eT_iK&p>fM=*gX44d1K926C8*UOX*n zqE<#ic=q9HDz{Cz3UDXR-(KYdYOWm~y2(}OZBF6n17{>-`~If<6L#S+KZ&8WmLme= zcgx3kn6%Mwu3HXWIr3)(-33H)sHAu6JCYcpYj&PDAco}L0sJI$Z7f8NKEuHV4ZD-b z%Ng-)wmwvpr3uW=@Z6j}ztSV6E{$_>#&Uic78q|L7{$RuqOUXZhHJQXx;9&>tM^-l z4ElVwg-$9bWb6RbRhhoGeY}Un>%K#xd6AEUoiygH9dl~&!CDb5z)iZcpp7%jF=56g z0CQu@;bn~tMJ zBoVUY*n{?FNGSZV<*lcdx&?g|6Hz?cQHjR8T~mJCe7UltEJ)k}1vLvq#6+Piy{O+ zdWCdS_W6BLUPt4el@9pb9G>KZXLjrlgdA_MfbU021h?EJZm58d5^1|VosN@3QwjvF zu}2!&!Wr`JTNB>pJd^&*&2c&MvC14R6;{9QZrmc|s@FW&E+`BWY+T`muiV%u4=H5n z^C*};V1gZoKD*!H`F_Pc7}cK$f@<=FRtA*G$0>eyxeA}i;22F6))lk?jyVuZ-=#=nLb=uu?CX<|`JCza z%@ajR1I6d1jud`AZsG5mx;Ax!&Gn){CBb-61fx6(ovlptrXQ-&*96EwXqLli9F(Vi z;hRf}9XW?&r<_xf)abtT^87Vn$8R6s8Ca)8sqsKL$hxEM*b=y5kNE87kst&qonr`!8l?MQCTU>%EOso(|!D`S|6S)48>(?Sj5N zi;Z?r60z7td#q;M<|I^aNnrA!ggI2RukD$GitY<%Kv{n&-_#$U=}8jIL)}gB?I;LC zsqnb|v%l5~rZRia$7D2UJWJLRpz;)NY~f;*;Z!H-j-E)clcL*A(jl z-Ns@&-6CqO5Y<`dL+qDsK(Y%f=C(>rWpmm)ZkG44p)V1P8sWRS0nT$d%NJ z%lkWiFyEtPGOo2f3l~u{vQ-e|jrQW$HLr-&bRE9?ZU6J!l_pyM+}*sRzJ-kqJ(d$L zki?U06V&tJB9;m7DP8m~On3WB#{7s|78%$z!#agT?s2M1pzhZa;UV20KZlL1(&Y)? zI%LHi?v~ERgx6mt<~UHju1G@EOu`5U+FUqv<6@KAkp5S#)A%BWarS;j(pO0UVN709DqK#C!R23DZg!9MNX{D4qKs~8Sw|t~k!B>X@ zz_Swj>sjq+&9~Kc2Y1y+mwuGjLo*(J(0?=vt^N}4T!=`mI3K38dS=$s-<>J#AXn$d zD}A^lABKKvb$(FPZC${TEiA~@Uzd6Xwr>XR`B-kZ1A4Xt?BduQeMfb|Uw5|g&C;|N z$KxR&aAk*dVLwz5*w__rPd{`;h0(8-c(85@DdR_w+8x-4cwlr>h*3<|@z?ofb0i`S zjKf_$#5!TRyse*(f01rgS))EBe+bnz7j7Pz1UmNF{I*e`<*t~o5Ry(=4PxYNf!hLv zS*-4hJ1%e4M_n|>tl%_x!%N~jFT32q#?wa(ZBk-_5sZwRFKq@yyhbs&bS9bD5f+jd zl6lR8NJSP3*>ckQ^@6D0SuDCccQs0>GMy8D*G}4;h$f z(J7(<>ek)$&`kSU1F)qeKC6r)lC7&&+%Khk%q;q>T zj4<9|X;B!Jr6YT%IteS-EoL3<{ksh|YS9!9Oisu%M@1*RW(_s3=>8ncaTH;lW3Sq9 zI3zJGtp5D1yzCt|KArE94gWWK67v^&(n?``$pt+al;E94u{Cv@Vg)bn_^!e3mnsd) zPwd+NphDl;_R_?v5OXoTM<2C3;!_DH^YNIGDMCmeokI`ZMnbqg=D&A29qt>=J`Nx- zDhTK4>EeyPS)YhA?t1iNZXnz3xX6TmxmXZg4HGkmqYQo-mL>$)t@*OGx|<%)Jg^7hses7E+=43IV~y_UwN#d7d}NI{Zx0J zw49nDrLjdFqx_KL5u3OG##3>b!t&uHZotTO59DK*!%7`Wk z`qhoPBiRY<)nQez`tjj2Ya1W?)!S#NGNCyn*!>i1L6uCC(}j))1OhFo2tKugm0&$x z&$o@cM`O=K-f62^h`)$^2*ss8h*$&N&9Bsc^%A|p%iamANh z5$R+;@jA0|gIZjq=~>ZrXRvPrnOt8)1Crop?m7S{FkTB zM^Ds}a~G5QvNy zGNS%>TN~+h!Dfl9pvH1pOj6`>3vZN%YfIuD@(!s0Chq!I@#4}Yjk_>-g}^K4K9`&d z7<(B>*>IS#!>K^A2@NCH?hJ%+H2Wu&9A|zCejic267}xU1^>5|trj$#YCCe^HTXP#b=T2fOQm-ahPC@vIj@0riHVOmRO zoe>vex-RI4n9tJZ<5HMWQOZ05&nzbrvQOk2te- zZwnJc-Q50Fq~*+Z2Jy#rCM_5K&#+qk99i||R^d(o%lAPOOxf{roNv!O#YQbvnM#%R z3-0KjO3;^WurNn%cj+}ZuJy*~C~bEjQ|&c%&BfR+yd1)}^MlT#&FH3OcdXWletJ0Q zefWsZ;tMfBW~*4L!fCVOG$TVh(?|wX{;y2#h0bF=^ri%VH={)q<>g1pZL0=TiK%dUlh--7TN`UCK)~f>*otkIu?VX5U9GXwRS-jWKzofz6WKsq?{_k+ zU*L9O2c1#JlUVNiJr8eR5w6P)BHYcwtKDt=P6{nQ@j$oR*=j~ux!99hj(0YF-fhqY zu3ykaWM+ggMzOB;Mwmo;tp37O&S2z56H^2ua`Fq8MJ9im3TYIP1?u9@N;&9v{f>xU z)i0Sd7cHA~Y{4HB8l;B&6wyu_i?r^e_@ys+%27$T;c8>OGF9)=C$$64rD}GMTWhc| z2YMEWS~i3Gyt)ORZK!YTdolexK2I)(0a0xjICKRC{zb zMEufQDbkcx+9PW{uk**>CeIE|M9JiNxfJfb73t9nt5I<^=X*wOu-0rofxlt-pbzT| zWcukJrfaXR&;1c9547G)W}(gUM$m<3KHm4ES1MpXX0MZKhlH zU+9g02^~=`_tW@rz=+xZSRvYPsQ(<;W8k3XK8|F$8P6Dm9nr`p}8}+Z8(R)100~HyKQ4)y+|dj!IXFEe+YS_;xE%vnI>3U72Cb%ru}ITvow1joCmube_DJUdX!Ut z4PdoE4Ir@C?eD!{(-s z=ic&PJk6z-j>lq8xjESqzWM&u+zJPpTM}^VE0S8!mY|@*LUJAh(cnIfISo2uJ>-Tiaor1qT9s|WsOXk<N%ADQ1eHp^O{G1~Uvwc~t1 zmhP{G55AXvU?+Zuj%SK7yqqN7jDf~@pLVz+L#OeG$EVsYBN((86l&Q)@u6E+E`f+F zeE)JDVbMA*OKOAnL-GxAo;--^s($HoY~HBd&2e|hBFc17nRQ>UW)d`Cick@K^;Oj; zWM(kzn*Q;q6Ia#cKv?rZUWjYR2R4q`s3mPbuhohkdoT7aWVtJc1lL#C#BMp>NshZfc367hO%A!V-xKpg8r%R$@7_W^wI#l&>OZ_ zeMOmImP|Y)X|I`i7n!WORvmt-o?~AhW?cu-WwaflU!vfn$G@gJ zMC{m}u!y@XM{2WsSBI~x))$z)t2yMEan?aL^)@Gp-d&9D58rPV33O+8zd3qhe)Sl! z-ySBFhOkTUL$X=ZeN~mMH+9JxK0$KG;F%72+EY#GoEnVTwW4_Fe!j8`VU%z+e)FLI zckAnWOWSTk*Yt;1tEgW;{d0b?{4~U>Sog>b3JT-fV-MF%$Tmks{d1a-Ehw?`h)Rs$ z4)pL_F#k3ks0LIq5ncvhIgB?X{1!jHf){r6?z}|0X1H^isc2jtFFWfS#T^gfGj08r zbl5ZN@T!Le_lH}>glG*pvilmSW2d*Snl^)voI89_nmQhJtWj?ip?3=L7piM(f6#6; zI5!?;b^5d^Pg}&S>qM!#XtK$@QE!#zI^N^Nm_at?3pO%~HlEDwo}_TdumlT~5I))O z^l`7cz;=t#kQR&E884}__+x%2?WG94alu6NELv7GLqmi~B$y9RCBz4&^gSss!TcSh zJ>OG14L^T;ouFSq8<@r>=0TLNuYyf2l~ooskLm8Wnzw6}9dpCf+a}zXwI)>x6n5a5`h}j6x=y() zUHx+T$ua3$i*lnP)S*tt%Q2aN?!=Ufc@>UGd-yG__%8xowkSEU)F+>__lk5+KC6(_j=w#wkgIhXUym5=Y36Y|? z+g0ASj;`%w+Tw))W!0}Af5lPN>1zCObZHE;n%_KjADpMyw}|c*dl1i1Mt3LkPUd-2 zFDZ%yrq!!ARJLv&e#H#?eFk0hD_xMC+$v3dV9s&tvg!A((Oie!@BO*u-`7HH3ZU^J zWyw|?J^){Ul|*3J_{_Su_c5Oa>e%^9bp5mH*3qWTSF)~~6n@e;LYX}L9k+F~Ue}s! zT>U0$ZQ8enhODISVXvlU8;##oC%9S1^+KA>$F592NSF?yZBFfIDpAfpUtI@ZXcid* zXf$1e?(*JKX*yfJ0^m&Wxj#%aZgz^Rvsck^5?rMz~C&3hA@x?OAkFknG^v;sXKXwSen$+tIHLjj$sa7BQ?94zw28#i&zqaCXs^%oy|3`3 zn=A4|VaRje2kuw3CS@wA;xv|(Z5&e?2X>e497`@Lk2n3kKl2ebw<&IGt(ZHP6WCD` znwIe?H7vo4+$_Iayp!1K#{Cw*F{g`uMwQ^?n~UAlib<1Oda{*0|Lq|O#IP^(^hR`= zx<8?k@rN)>-FHd`t2{IT(-yNU zbFrwz$Lk0~!0Y?1phcQ}JQb5c{H#^YIc^v z84{LMyWz8CHWSX`5lq_vuz$KIoALSAC?-FOPO7+7MA(evG~$!#VTLMyw|x?At$@=l zxkOFaaeip1a+w<=rry!3fjx6?%{nz75}~MLfno-P*H1(rvuQ>-ggvv5a{KPs;_aW- z^!?oZ!UN80T`J&rx?+BuIh$Md)rBPL;lZRVHc^cqNjo?$bi`@BaZT=!+)tSM)%Lx? zGyGX+%v*)WGg~F?G#_*}TSKc3QEI3SPxe?)rIpE~9Vu@ZQ*-23^{X4arP`75F8eTq zt(rtWpw`Be-MdB%qi~KcbG~vh|KSw+SWr;pG&?+Yo@2$c9F`>hV_>}rtuni9uGx4-7CPYF?;7^e^QClf%sJ}b#|MS4o; z`8lu2+Vt;0sxQ(EQ-u1SxM~>=6>pS3mHi!gGRDZMoI97kxoedbe!LY0iSh+FH4cmq zU`OYssLvj*{{=_jO@1i4=Xog9;iBddE>$2|K;QHR82(5LOv+PZd65!%o9lXps z+?2k*#*6lLN?m7j(;zi~5_&DJ){keEIPN&-*r8(xh18=YYwtbaMa9|Ni03}-u_!8P<2&niZ z-nF0|J?OFm>Fyi7UrrY!ZYoj=7v6Ctyf%J5p^CLN$&Ac$=16H#^y6G=k9zU=qZX&Z zHA7@`-Rt$O1D%}YBj@jgXnO1^BF(f}+&hW1rAKbBCz|0hopw}goWAf}?n;V$qX|sy z=%F0)pbuuOLbQMBP<;gNY=YE%wL5&2Y3{*>dbfq*nyaCwIq)#28tVE370}U)A{c%X zTd3V%&9!I#{PGAD_vmo&5{!)0O~@I9dOHd(MIdI>GYO!)%n|*Zup<^s}-m@ zLT7d)h3sv$;0p6X^ksU;mP^0arwfv!shX)8g0qtaZ3 zMmplwUGz)Y`owWfwt2Ml$9H!)e0;037Jes(c9F(D+X^7KF7LsBKLm<%o zJ+fM>Q%3%n@HZ*G-_fJi-4T8#OpTuq%SuDXqjV@_SzRDQEmK}kAgaF+V~7lnvJ@O|a_+OvKM1PRl|gUlA~wp43}{jn99a`7#)tJA5=L zYOk*y+Qcka^xk8+xZAst%)iaDecY7nR0%;$zPIkGv#_QRY6U3>7W9l1cr*b6bE_y( zo}ZZoal9Z6L5d}Tp0LoDJ-EZoZ>u!5G+(7p>0P#!2Lj(YG}@MuUfebK71_sEkM#D} zgqLD+{j7{!j$g`{Q!RnIGJ`xDqDw zis|1bu15wCm#kdSuWYJ`u_Y)^p?Wb(aoQihq|bjP19fdM_L4LzX5Q&vox_h4Q5)uC zSx#pF!(NOsM)nHw)5HSEwc4wB+1JMA@`*}{Y`!r_;tfW4(_tI^#A}oMpW4V!$AMr@ zn;4wyeqS>TusCj$ks%njVhx35b9>jr#HL9^xlZeJoT`TDw!DlQU zI771VOd3&{MO)Un-ZiLc^jM%FNtw)GM30Zvmp7uTF;PC0Ngeuu9fb~jVbczF45E^^ z>hT+pIoIP{Yw(3-soT+12j)Yhqw+E{r~QQU4iheb?IE%kko1YE)E^`L>~+9K<^*sqDi=V;J<9JubR`k0RH;QLPwsh0Nlim`6ke5n ze9h_t&h*`QA&0$i%nN2wrsEMn2sUlJuL&HgEe8d!z{aMb?&KW3`Y$o1LLZ+1b< zNu`P&PMxQ$+O=CySbko7uw#8A z#^w_rCyX67@~TGVuvj6O_NPlZr<~G9w2yP$Hb#GP@fcg6Hw+eCK7W4jg$CA9l}MK_ zct2h{sje6Gx1=#8XxxmiWx`eaZNdIKVxTH215`!zR}5vRBJ=Q_@v!Fr8*{Qsq9 zq+<7b`dvshPkL4loN#dTORB6oc!r>AJ2Qlqhf}5X&f;3v{aVW#E29t5;ng8-p_?iM zM1p6nR5GbcRC%$n2YIew^pzZUxjZK8(QHVLzz_>(f-8*3uFf_qzq~z!OwEXq<8IkM zi!ml5Vn6Nkr;ju&Q82&zi-}@3iCh7&rD^xpE99Bq^L2>3?-Bd>M-R0Xt$3s24~H|$ zShPOGMXf*9aJ4(2pbaQ2h*KG7IlXr!2?|nOJ=;r{?%&Sd*PslR8-A%eb#An_TVy)* z$Uc=(axg!h*-O}ZC8=x}Xt^+w4Sq?ro z+hOW%GV!_2yH0<%G~T15{t7HwT1TB?9gz@S`2I6(7UF9nar##Z&3wU^8XDJ+WFGgY z-8ZRZ1Qkp2vC^+Wj2bNep7?=f`RUI&wy+$UhO?RG0y2uYyYhK!4F@PRM%G!>h`ruJ zOi*e~&4M^KfJU$74AHbPBAi*M!W1G5MfIM0jJwN7U5wa^ROS_*`Pj!sIQ^cWS&cKu ze%#NQyu?B=<2SYq{6xOCB33cBN$uP=sHAozTAR{3ipSa;ioVbgD@fR1*D%DcMo>%< zbL>y-KffEah<1|x7{z#8X=Ou__+y&y!gCDZ6u8eo(4`Q5CumBgQhJq4{OxDY+~vjg z#yo#f`}#yqTh6p67e{AT#7+b@l)+UGOVem#0m&tHIiBWSNIlU_3>;#Qw@|MN^?P@JXF$poA2PU)d%r5U*~Gi8r5F1yb`(aIm4 zJ6pT?$IE?#$fW9$$~Q2sqNj^KlIYWMb|W=$)XxWc+-uwaZdR792MJ;#h+{?ikrR59 zNctD0Q{ePl&h)pAe+3V-6DHT^QIxQy+>|W+-3Xepj+U^3A`h`JzT+`#7azl$*tM5> zq3wYOP*G2MLK--xO7>DwiYTk(tNsk(q_@Ax1&^19krmuIcIAgDH*k_4@M~Uou9I-A z41QK5@{4VPaR$!!bm7*_T?uZ8(-@x*N|5f8F9=^p2 zcx!Gfn;0gTb2lEzj{ zu%{;P@x#V8K3s@e_mM2M1hLkFRMGjB8%_}8JBrREl`swtR+o%RmlyCoTiea4A_P`@;ZI^o;;9+l|D94O8e6c~6+$a3y~qKJw_8V|W-uHqaAbcU;D!h=Rd%Y}VbT;Tu= zqT>PP*#>#JN)qzT9t=YHIe;Je-GeOGzp-Q{hJja+;S<0=lzoJ57*#4KGW`*&ctHK=GOUc;IDsU@BzjodPl99Ev(;pH&% z3^`*fdemQ}&COJyc=NoW!{IK1P_Nt{9zHGO2i)Kh;%$ncx2HJUCs#*mc*}i2+QO+_ zS@PnUr*mC*_J_m6|LDqVZ29zNw~saYZ-vl_iE?)npsw>YRz@_vPcb@F@hy&_^B@?6 z8-MDW{D}5=oH~w;;C+t?Gx&2s$R8DBzo@e`=eJf8#lEQsAen>|*1g2QQhY7!!QsGs z_r`tIBihzq3%yBF-Z)J!O1L`=W@M3A!ZnK~_24z~LgH}BK|_kA7|kd8%lMWP><`5c zv&czvps?O0zCt@>OlkP(okYX-4O=-v?_oGFdHiK%gYaEF0-EThee^(XZFPHhEt2QL z8ROdT*4#_>qIc#88v`)jWln>;=}~wA7|c5FauX0HJ)X-Zr6~}}z)LpAbsnXm{#jn8S`&Evc@kFv4Ea5?6#&_)m{V#r#2-+4EWyi*;W+};n zvCk+;3hYX6HUA608K8cbCrV^@BcpaSHar(DdA4k(ZQ=EI&GR#1^gV~#g8Aa#5^u#; zG9OL<8^38@9{g{y83vU}zg9Z+II4L`f_W5 zb`_!io@22?%9=2CiEoE~yM;Z+mHMI~EYt8V^J72=dQlclkvpf5L;$Q(bjBQ|&U@yZE(z01=(x2+!%fCRR zxt<4WA!nN+`q?*%H^~M-;eA6QNE67)b(*_V()G5iwO?w< zbMb%ibsla_wC%RXiXeg@RRSnQdPgAARHP%lS7`#$L+=PkCsgT#-b6YibVP&@B1C!# zMF<_~y?Vy?{l2~5Z=Ze6A26Aj=bCFW&%M_At;D>mEb>&cLR(c3g_BSF=->EF!9Vy- zE41pHvyNe%%^LHd&+-;MH%RMm>>*)C_<{K225c8;tvPtx#S2}eF$rj>dSRJHPR4l_ zfn2!EBpiGA%4b>Pk%1iAl-q`T)-UP*OW?eQ-7FuHeT{%~w{JfClk$X#igsTNlEURa zsDL-m>lgngvi$j~zJ zD*7?r<}aBGXe+(i!9QAhmobQ=pGv~YBY4hgo_=bcXRO&&Z~^cQVzA1a{0|mXEkDo8 zD(*PA&Uezt$JDxVB^YjXsAh0BoK{#RDsr5&e$_BZwtz(}z=$UHUqPu4ExoPS+1O5i zd`C82-4v~nZ=F!gq~HtaQ?(BicU25d0${ffn4Jf{dE1Yf~i_S>nh}% zZw}q(ZGfr1oFY2}wq|EltrKL(T}YX8;T~95QCOj#?|x8FMBl9Y!3OtpdzuYNlt$zn z$rYNnMf3wdlsu0(0q8<}vVJQo%W!FgH4OY4kCUB|KfqSDy##-Mq)V{v<9YPe(w1h} zW1>^%BoV!#`yUPke27bDMt7||3)L$;#40dF0Yp<~Gfon1W9u*2AMobetFO}AwqtJj z$3g^%lm(=T!J@N`Uu_75uYG}gXq?|03pzA4nD9yQ>KdI-gNah{HAp9Z&OfQ5I2>i3 zF0r+{1gP_h3Np{)I)?y5MLvF+PkE*{U1s=j&6L%etoyw0y|${ecY0Fy&rGWpphVex z)5~P~MDA=54!=(f;=2RYNA!8sbH8@&UFV%09>*u7@Yme(iWB-YsnFS3;kE6Lzk@02 zGjy1x8&e4Gj$|kBFrNG=ofEwq`m>`S(9(l)+yu-mg z-sv{FLp)CWdca<3AIQ6ut#n z6y8Pg)ZMhWBOA2Vq5@ty-t`&p?BPV$R(=$jklc1$3ayrjVJ|bZQ)S6+B33X26;uO&>7BNk zE#+I2C;#vpZ;`F-r1{swg_00T^Ot0L?}e6GW*Dk4<4f>q6!y&qlRg)Ji2TV3F&c-6 zb(7|L^NG`G5r>R>q7Uw-A8wqs2v^zQsx15?@8- zRrt64t=(6`PQ-PEK4_{II~!)T7M>q-P5#C^csAoGJPC2CZ85(w48=EK?;n;)e(Dq; z`Fu72G0(jIJKp?Q$N4OzVvf*9Zax7L_jA(+XY2B#u6^zY{=?+1+?yX^D zz;?6^>R$1N16C%`a%o=+fpIApAJ%# zHO9UERK@M*n(Oa-(sgt9SIv^Jb0|%9GbO9XX6;k}74qR_nsDpW_5l(EWhJXvJ%(w- zSs{`m?GY%4p{&aEf@O6ld3U>Z;SS9mGtTH+ri=K{tJf|$q5d~q8mAW~tvjP=SQ2*^ z^Ws@h9YI$&J72ga`a&$E~V>ha;eq zy*HR(!RVYP3g6yyYTI10UEVOh$65?29VA$_YJ~$0AV9x=RYI?Nfw1jnrbYHV}5d_T>T_?LX!NNU8DGZsao^vHTul6m?f$;opUatCMljf zDw+}HKL-7-sR~mWc}hBIIn1e{8ncx3`Q?PwEh5%}?xi{u5jf0QZ>apn6ZXRzdGjr! z^fL7pi3{1;wyNj@-;KUP7$(Y!?{j1&KD0P!M}m$!D0<);3O4)0B{t0UjD77l9 z9$Z7Iw3Q1&jY6Fc!<0O3kWRHNpcKJAGe_suLb5}z0^tg#1q@NU8md?@!63&DU#2t zuF1+&3i0g2Qo^+2UNtUix5MD6aAsREcHE-f#ZtKDZB%W_Q?`tYsI0kwO_PwrDy{HR zM~izcN&H)h0_rwT1LZcB=dz_O+NUii_?~>3maTj4TYz1ZyI9fTq9$^YB{O*IoQtRX z=Gt<_>1KJ4;ypM*%Nstda+LPun5fnQd8d8FWHE`tyZ~yXKT#0365#5ohY+(tIwa~- zFJ4Su+gKO%8^6V{{}wK|4CPLGl$`c9_?ibNRa}NsteiU>yM0?MOI8gp{aet_k}KNL zT@*)MyI&g0(9pl`E*Q=dK9OvZ#6;s;_ssf6@MF5aNtL~rxt#$yCoeT zImL89(q5HBn4wTt75&|dw#eg+kJG%v(e~q}tiY3iQHO8)>~qOp3}xr1V@JpzZM?$* z=U5|+{>F$!tMrZZ2fy*kI*#TfnszHdX%N|>A!S*#mdTa3mzawa-Ju7&5}~Xrf}&LG zora0(;#hqc%MJ01^kq*D8Ddozqv5QLJxs~or(@Op9L2#0T~r`lI<;Hk*{cC#`xuEs z5+5!kIZ;1>`jXg3|Ejk2rl08hJ_JPCJcGWc+nv~Guk8wzHxlE;_H|n`W0G~;0{zsF zmV$0^6TC@bS~#~O4EVTCjo3T93l$_K;^T`+WY&xbE_&Y&#~L+n>?(Xs$lUCHNAWN@ zCE>Dh#DBF8q$bG5BA2|Sebjzj6at&NdeE6UNwxpD28lIK)K4g!uM5SKSdtsVxL z;zzmEj4G_!gy91z7poM_csjwgJAdE&s7YD#!3=zsf3}7`il0PlR!sW@k-Az|2?>lm z?&w}ZBa%ThEL0!?izi=*mBa|=GET?E+UDM(*1ZLn`|*W6)=BU@z$fBBhr;KqVeQF5 zB8}G~fXhQ0T>~XOh^;g(oKBT|6yxnUAY)S*aM<%+q&f_&XwFZu26%v9I3dm>zIWqK z-PZ!@6fWy|u-p#&KNA~ntJQU<__P0cIVciVLes5$bPiOdO6xG7Fi~mhk&xbhA&BJy zhcD#sT}gL{GmPulOECGa3rb}D3)wTbxm%g)aN=vXCMp$u$T2HTextnR{wY;Xv`N6z zPo}qt1CZ?St|nA`n7Hff?|M-`N|>DPQR<>)>seyIHZD;Tu@x%r9t*wNUzu|<9uQBX zE4QrakqDRo(Mb&0C9UscbvZY)UqQ?tJ}c~_`WbWHxvQ}`mGN4;<*B+YH8#d%`HcVW zGnx8d4~N3G=|KW|Zuco3X^)4xJ2J>kJ9@~KZU#G}&90F%78z67dLsRAG_u4I+YGQJ zcqyr!Dwa)jHQR( z(dPt{;@YBE4tX(w@L2w@0{ZgJi$6)K=1g=H5Ujh_9pMkv;`%)Zk{k$7F4MlPPfd?~ zXn*5{TA~}RV79ULkGtMLHq=8u6h!J$scv5$eE%xy;j=Oynbi!wI7gzRFD!-v2y+~j2&q5 zRyba|pvMfC;8&3>>Zn|!KIZsVHK#!45mOaScw=KAQMuZ{&s3^CEp_FZV;SYf>C9yV zCN4?Tc9SmsDkqv}sF-!1m+e+wcZX>`qO;u5nEodv!0`IqEy}E#p1^Y>E(fkpFYC*h9); z>{UIzal3M0hD(io@(WJw)P4?A*QBE{7qsZJx5K!FzSe>{R8b1`-eQm*n_{C$fcUe9 z@SR^v0gGy7tSqL1(93f>(P?yHG-u_|Lf?roO|;vhTEdgY<_C*sJ9d>o#@83-p(4I} zZot8ugce=WDlBfQFiwDhaj2xA_^Nj+z!UA0LL^#ijv{!@GBV`C6by`sthKi$dznP* zwAR)T7^MF@*VMnT#&6h9n|+^Ro=&dxgfjgPIx!niTrV5(UX=hnw}ODV{lzDaQn;lh z{@BCj_Vl-}G9mWrmB>IThT`J?h~QQU{exXxF8?>5m^hsdW+-`a!wg-Fu}D7ozb6=kl-g?K?CvX5ceS$|1S>TO?B&*2qs%?8uD_S;E!KLiU=_xdD~S zHB;9E`On-fc?orIG6STtFYCROMXai49JYt(v&Mi4?+TJZB!73)qa~OWE6|;Se-m3{ zf4DIl=brykn)NZWB_skr+t$e7M3k9Yh>enw?HJIaDwI4kXTQA(4Hir`M{(bhG9A{W9F>BMEfJCWLguv*gpoz}&kUS_jx ztxy$+EU6WJa{6-Q>tTELZd%mEvOAG1NO4Mq*;#k)*Lt9Nn}?0u$alRYN!AR}(@o6D z`iYxms8t-hkW+5+pS0v><6*FtHRZc(3E@+5&(Tc#|Bx@s%fyP`?tZF&_jd9pnp4gR z2ouj*>r zW&nHJ`#}r{Tx5~-@W%1ZRWq4nG{{?GW^{{x?lxznf79~ZpyX6R^wHB89F~7a&ZT7y z84g<6j5@crUOy7ObT0#J^n#&QIb5#tPLm(PC8=6=afZ}Q&TW}yp)_RLzTuS`$XXOtlW+3Q6~!xI!+qcO*wjn2T5;a8gMPU*cJdS zRM9vi<22R7W$*2MdU|dr%w=xokm>PliP$^PpZ--d>DRe+62KsX6Pk~Eu-Qo&mikvp z6Tlk3{41sT2Ww2n_ea@#gs|4Wb{HTz72Lxt8?Vt#z>?)1;57yeCmr(>3&pL#T@2ds zf5R>V;TeKm;l=uQdJ%=M=>1Mj^9nJ-uJ^CxV_%h5(jzYrWoa%JXxc=@L`ag;>A_Qt zv(+#*GC9U`euYidO18>~UaJn?#tO!8sQrbd2LeL;*;~;au*sVwgZ`%Vxia$3*6bdSFick zep%&sdfn8V@=r>Paa(po0;vs*kq6a)nLb8VcO;cHJQcTDo(C^%Cm>WbRSi+=PERqe zzZ1k!#XXGKZ!=7F)&E-q1dJ;V5ET^_j5N89=+LC4p7|Hc2+k2Q6+8`qHC%=Ea<*8D z4P_oP2uwj1*UR=6l{_VvHl&Ev`kCgln$_`wedfr#e8 zpI6QCTC!k{&fyJ-YcGP{pnUiBdJs3=7eE;P!(5dQaOs=)!cBZp zWeuIm;$Nih5%LW#^fGZuZ42lrpu7*gsn7UX#b&t*kOE)-Lketol*J123T9ih407%6 zKx}-P=So7xm46bQe-lh~d~O1}Ums(xR3t-APPW~$Uv}l!-YLl_L{CVM%xNjT$#?Ez zUd?K({7SmReg9}=7Z;7s)%h&``ujgo&$U|X$1n+IYw8G2bXZ_30FMlYOR{>FnfLH6?K-&Eiy2XUGodeFfXiLbBlH?(iERePzfTnbIA zusql8|9HMOB%+z)_%r-+O{?>kT=zaO7WP-$reV3CIOQ_@Zv6Os?QD__K(3nqg-B=?={==uBfaco) z1>)%z(bgmj%sb+FSGpdt+T3s*xm`^dPJ+w%felY9sN-cLy_@o}=+5S}nOEGw%0u0U z7o#Ldy7QgUBMY2w?_Qn1+|=Du_HIN9&eia9C64Qs^Vcu!Bi_qU&c-R?&*)#O52!VR z9!!3?E4D;-O3(dM%=4*$2UfivsI64JX zJG#%F)g=>R9w9^B`=*mVh$FQH2?SEnr7f#npgl|GM<|u;*@N5Xfmt~X zH@nFx;s`rTe*fW(z6E%rWPkHU1OJAuPIC6#J4x#Q?+jz+KX9X7PJ40UN0LuH{+AX! zhQC_yJb@Oxn3p%pt~V0r@J3(S=_p2lXQ7_MZGt17VRBB0-UZadTLg&^!Qn0oa1}e6K13GM<>!q@igXEI@i;z3O7;CsL|2V^20b0Xu5MA}R@} z158o44HctU_^SsG% z8Gu)EU~aVp=QAcF`E~KNO1ayYhh$_J$)?^QUX2I_)$(%wH^=_1UXNi{^3)q3^n z$a#9{Sm5~zy%_QIjcyoQL1z8nxixJqln5fl_tj;e-djtla@uq2{2$(v4bnL;&Pg5r+yrOJ0gMk!&; z07&vuHQP{_FCjM6)%ru<{U!NH3@hhs3(&5ps0p>9MjfIqYN^sL%KiRSTz(ns6xi(@ z%8%#*oxv|vY?0{-U{yTtr7QENJ_9pyC-%hhih>l6*gm)9t-SAm@^rRQHFvYyOZ*~} z4G6>2xZcn03>yl^;t=Zg9rAZMttewt^N2Dgo5-X&RiEI^q3Ube&G?P6;gQ*=TXl#P9=<9z`fW?wl) z-S#UgoA23Z^X~LYhvVEc#HbX}mWP=0+?jVArTjr{EDI>O+ZG3J+3pG`Th-{ims5i4 zu19l~XhGtP-q)1{&S1Oby6tkO-^N&Q|N5@}t&^1pS{~|PeS{0i#2Kr7&8Q!c#{saN z$a8%&__PIfi#1GOHQD7Gp(37U1LyHr0QLtQYwleKU63hYjMz7)+uqt3^j8`N5Tn~W znU~Pj&X`^^n9a32ui+)PsIk_!=+;~IugX@%=79diHl}b&yQ5>$GFc<%91xSL#EH9X z5eartD@h5vcw21T72f%tiQ4S#OT0VWEZOL*4r5KitA{i(d+P%H{Za0xJZtpxZ|F`P z%5QJZa<>Y0ccrLd3z@DW`a_?uD~Xlv5I9^u756$iwOnS&{Ee3sQxfN*3^C7Ne@|LT zn^&{Ykep%o3&#-iM@J!!d=mtIUYpQv`KTo5`K@i9xf<=1DI$e^CwDFL__N|i0 z$)jA0*&#buT@E2)(az6PR=5Qq1Zvs4o)fseQC85ZgM`J%(QFAkuI#)s z>AvXxVj^jp)_U_xUhd&}V>d1i#a~Dsw$o>Ws3x8_z3VICjs6`gs^7@bi4t0W#mYxMDd!ekREg*+Iwow{yEKKnBUvf)f zjrM|NsrNACZNwkw+Ci=Wul}p_eXhta^&St2=U$5m%UAr!I@v@5p8#37wYe^|OG1kH zcpUkQXlw41fN)R;$6K^-9R0c|GuMymrRCtbPdRqyqZ(KGgY0|YyZ(XszUhm6ZM^cm zGzIFEqm?fRO(|Vh)UaE}%a{|7z@#URCDfnq-}UK9ZpO2}WaHT=Sj zs>FUJYP!*a=7-F@4ZGqFpsVt>jcT5PS!iAwY5{ZN(&{?IsqrC8a->>`U0}&Lyga!s zGfO>4g%@^v)_dA}Xxg z8j-WQ7zQ#+BA4;LvCr;bwF28P?}qm_S#WhCXe_%MWoca0-L)dd@BN6_HOLu)yX`KN zoocGSa^6IwyUt_fXi|CXUwzjz@8^m4+moUfYaW^Pk^Ai9F&egg+g{8s@yWM(gZ!bc zIFgNt@}=g33;Orf*f#?U)3P@aVB4g4#nA0&6X`g#R+ekY+Ev;5$)%}Xw11qd2oIn0 zV)D}ITZ(6&n5a_L#CV!1Z?UcY8skS2A=uV@L9;?Vl|nsPspe85fx`wZrr&7h!|Cb1 zNkw$gzDNK}bOV>$03@lsU^O6Q9hjg56olAjJ~$m{&*FaS7k*DX3W!KVA37<_{+gEk z)w{VDVB_da=T$3tTL*7uFICGQoC2E2o?Kg5{^>QsL~;eiTCDqW@57;3Z{PPfv!1;Z ziXfpteps%5OElO>tcUt)2~)^*brZp6x-aUxBBjdA(RCBl3VJUEtf=p}S{>}M(UVp> z8PZ5tkGL!dhnf4DI>fD9W8AlxuP5FXva1BoIMwxbyP|#`JlL2mnSr_<85y|!a+U*TN@WDe{Ek&snjV`@7A$`h z>JUB6eI7Vm1b{tbj?XVE=|0Xs0xj}~JUNB0?%Eo+*m=!ouT5R#aOC;ZCfMJtDH64j zI{q}xB%c0=M>(2ZWfYOQ((&jjqujcsv$-3eSRNC-fP{)kv^cN#_lP$M@9`(H2yqp} z@s8ti7~pwp!-Ga%)`{9TJMY18pn0apsUW;i8?b1*SAgwryf%{N?lj0_aWBhkFVu*I zhas+-*EN2ny=saohhF?aZFA8U0izH({u1{}9OLEpX@{8}b{yn!B9~8a3lMH-wpC1T ztsWo%bAy&`&%&0kg*zoZVUIIgE9VT~c-_%ENaIiI+m_D#4M&`IlemS#o2OlL{}^ZJ zy|^hX@Ey*^PA}eM zrm3XyX7`ubBH|=v+WjC;n&Q+CP!4+E&;&iE@vLfDk{zbdjVihDdE;NZS4A|ZGj@x0 z!$7TFAC2H5l)=Sl@B>FM)`31SUne@t;~XN@wUPx<`E=js>~sqjaN&R8nR`Wfm^G1} z=+WXAF8JNEE;K}O+^PT(NEg;McquXjt$^JDF9>F(Wy|BUzH1IpKOGzXHeHF~eril@ z@^MwNs3R2OxN9+Wk0Bi{y+iMNY`0Ny8D8?MO0?U$ zBJlfR$)qCPW`FWCod{Ie10<#1*C6oXeEB&iM*Mgb7sY=hsJDpo%a--OvRk6zL zjQybEoe7%mn3kbu0@4ly*iMv?Eu-$;E zJnX1#-N*QA{rPF;+G1Bxok#wk)=pQRvs)xHv4g$9WFz2e;tRUCXJ0TUsaC1P_WNkj z`L8$S$?g@r$tpe)4JG($i8XmR?+D&_J=|+VTrz5Nk4TdOOvs>)B<4Q{ENpgxSSYcz2p7ZH!19vA9uxP}|*~F9OXs zUOIW`Uz!SC%7oV?v5|#hx$cYp$`!%zRIopp*0pxyS7f(LEIfNlzLH2SP@PGYx4gEy zxSaHjGII)8>65eizJ01)SKcWxMv5j+S?q`+Kq!h3Trd!<-7Tz7G6~rBj)I{hH zahJRqOBT_qo=@z`4Jx}{*)I2LS_cl&%5n2}kp#MfirL89R9>bFYJma`wd;7s_9_ft%7KD)$J%S(E{A@HGyYcobs4- z)PF(S8!TC39eWQx=i@gI(&)Z;K$gd*D{pEg7SQVNX0jo2%iO$qu zdvB~V!%mL#wZ!U@2(?G!rL^RorL0{GAA4fWj$gahU07|$eyFfu@2g&h)Pj!;Ip>#$ z1TVfdu26FMG3v%O4TNIirsrfITg$^w?pYW<+t!|Rq#LPo0P2#9Xs$ngCnfC3nfYBZ3UJ~3jIWtZ(Nr(NtwXuHo{$m7}7{TolPW8-tHx9cyDA6;R$<)4=xqN-2r zK6GT9U(D5GTYkmVIT@wr8b(ux^8WNT#MR7o5I52jD5^9|_S~E0sP4WRxA(#NNl;&12}dc12sG&iBi@gdjHy_*E|`ngJfFIgU+CD0OnN ztH+SzXi@%dg4^Q&-@u$_$$T^C*{`6TbKCjPMqdze(K2)duH_+`IPF|6qQZ^!$u_m@ z*}TQx^+9~(vH?yP&a5@jBW0DvJnZSq*73vB;k&kXvv+m_zi*#yNZB>b4&dt(^k!dU zq5jbzR>T|B4)&d`*Z&kuG4U~OuH#_Tnnq^ZI`11MTv+T|86%5ZO+4WF2^YUg#cKGd z{muU>=5XP--l5Uq&zvBz$^NU; z>IE1${^i+ch}FSG;gimu2V%#hz%hpK0Y?wLy7+Z#Qt7tS?Zf@b$sKFZJ6o8aWC2v8 zINQzm30HKY`j>hsV6Y#@ageQ}9f`!8*bd1kICktd*qvVT? z&4aCjT_>zo4oyXOruho%=LjXvjCLjAsc?mfZ#b2pCO4)OzRwo2wk?M0L15FXNx`XAK~K zVviX=D1h0$0sy*6g$krq%pOScmouXXV&m{#aht`ni&liaEcv=j7)-W~0&9j#UuooP zSA!NNaaUv#>rG3}sSef4_2(10s9uZIr5gzAj;CjyPq}F3?<{qL@hU#L0Q+uz0l{@t zlp6WPj70KE#;<+cOC32%BiN9PU8!T~{`u(-$`#}j^-yLB8?L6x0C!OfmnGCToxqE0 zq}+>u-(p1Mad@$s1L;3C&ue$Ltvdc-hqZ|&YnG-JwYn9wDjegaEVR$UULipV9M0yc zGmHBx7n|iZ4l!wvC$BP!#AERpUJGwR9kVp4$xzp>XJ}rWj09tRl(--fdHo>Qy@2dP zBHDl5f7##nU#+HL%jg-a1vyAuU5&DheN9L)T~eRVbaoBN6Y^6NlZWtXVqornbZrpZj7Db>+w zD@2iV=sN%<7&pY9+H&DT$EMOLFlOv_?G|GQ{*7Z6*)Pdy$(%!Tv0b|@?H1x`>Z&ng z!FRJ}dZGYGp)p{M;|B^8Q_?$mgmCMM-5`HKG-&r0A)x|jZu$ogQQDq0Oepk~e115C z1ZJ(kq6rBj(yZ3s>GDka*Cp5f@u*m9=tB21Fb}&frl%ZA@F2DJ{e5;gFRv5p8c_FY_e$d( zin>mfQvcw;=*0a1ow$+p(YwKN-qTfkF&wdS+Fn;)v|sOXSFQ79<7m@A@x+mze%Q{A z{R2(he|oZs)3u)4gqPy_7)@VY1Cn=jK2qUt=bFg>t#v+MMv*xvl{4;uI!qB8^H@HLzk2rk{14eAKGD9 z!5(K|B8qyH`_xL$(D-zRCtA3=^Ul(=ibe+6!@RmR^lZ~*tg3a0@VOJnYqy4ipcSt? zP-P_`AcJ+p3?w=qgC^dZZH)uVJVzI*irU9;dS4 zI?0(LJ|>U|9s4!=DMd!TSYL{MRFH#@869~np6GFDWgZptDY^u`#H*QN>-)nQC@gda z3JY_&k>ao}uDZ7m;_vRYUV{DJrC9>Nfp5$PwZDKR&wdrlN5kB$PMHDVQXi%dGX1-Z z`Tq$^<&cSaljoMJXWC`T;4fbA{}j-+b3y-0K(FWJ1i79!uJh2Xhu0E$isZDws~6jA zgNeO!M#ft_DE!k|*gD7Sa~Uho8FT$P*)e0Ay}CW^O3lD$AQB6DF$_J4ME+R)l`r^7>CT@v#@+s5N&t*Flfo_{%<_{kEK8M zop|Ghl>k39=XLjyZ?D9TO{E-E=|!s{mJ!+V*Pdf1cKimo$bwyVJmUt8cWZ4v{K+;u3$aOYhIl|_`Pc-Yuz6WU zM(6)GF0}-^i7>1(C+$k&)j)z64LTk{lEsfkPmTnbs95-eL+z5eIzm>mY4P(KneW%0 zs8-A|6gNR2^K{&GgIx!|nnbvn>;?m`Im*(sX*f>b;`>JP&15@d8-4J!rTKzn?Gzpg z*GQS;zOvI#(sD47&cx{fD3q4KdW$XON$DVMBH0b3ZqoMohU$nE_2xW0#$U<-yr^PS zbl)$?^Gdd8k8glJx6v<4pDOrD#npd}cKdaSFr`}d$b#Pfp6;*xf#7F8K9(GT^O%G7 zDNgToUyS?;Xf-74LScM;ysmq5mlK^m~-NK$qUb3dwK8GmWLFqfE~y0uE>wq zphhzO95Z5_#N}e2ckv2_pDfk4HCWz5_BS-uD9yeE-!F+E5pK~*jcwI0%l4PcYmI{C6RQXID)J0MbQSECkN4nsIUtInd zH?`n%DB~Y)YJy*m6Fxx2LQvO)F)Dxe%QgH^G zrWcQlxy@vQzZ56&i}*_N%zxkoh|iI){cV96DgioA(2yXKH24(G>y8E<@F2vi-giEAL=-)T}vzLINiHQBw7Rx zj-rSGUPoJFvE4OLeiY2IHUw-@-EP9Yi8k^+-L7DDxP6<5KfIyW?89Bbj|}Wnb0nEh zm4#3D9N!FZg*;Zj`DWbqX+MLM=8BdBLZ*Vnvs%6`v@?VyHtmgDW1qwQf)BWlq$h)V z&P%f=&T8}$EsoFv+Qp%}>LS0aWCekuGQIi)NM-~p+ zW1#fbOzkO&!Ca*$3P0e$n>P5V*@mA}H8KL^`sWC z65f)JuYIhEpxwNg=i8esSu}*#IRf;Ht^;@d+(|p&Guv?s_5p;ZQy6 z9ep;Ov5V$Sji_keUszy2N3GS394z2_wOd9D`+h^!eK8NsYXYS&iN^p?fo^e^KHkU6 zQG>p*v!0{4)gdg|ROnI!^9T#dYC`zvXCY^{tJt<~SJUj9D_WsS{_ZGCSVY?`UXtbt+d>%vaDqg9L6&BDgI!4oW*RHN4yfc+irAX^;G>9Kv$*&+3r}4W4UFS03k1RujAAyvE|H|AY#oNx-X~Iq64GM$PrMewSedS1Y)9PHE31H$ z>2}mJS*=%^^-+O_z&t@#50_kfy{#uHR(shoX^lRGp`eGo6*_l8Ga=#~*>&6AA1vUa zRZB>#mdnj?VVW^oJN1Bdb0ED<*;?7MDo1?JIj6TWZf9j)RdsHQDO32QM=Akp76^9l zWx>CdyAv>Tg&7v6y;tdHDDk=GK`MfrB7^+i)V`;m=eVr6D$`zcp@0?ij(-`aT~!G3 z=wu$=8Ar}U9`kbGRwYo|{`d}|u?Tu*|IDQlyURNN8&4V;J<}~}FT!!9pj;-RH0*c% zQ>^pu?)ShOAv{=+SweSnpA3`03vznE+}AIoDV=nMbD{>0dXb#gI@ zt~l&=*>&u&HTtX6oB>*VDnE|%6Kg7O3)l)KDwAYyMt1A+M$kfo(=PSi0tS?T0m4B0pC(q(4Y#HRDEVZs$y9D_AI=}yeo>fbjx|0;J!YRjTuHO zmfPU|TFSxFXRFH=0bg8fkpWA3?O(Z+In`_-yp2xai7EP#@sD=3?g8Gnfiou7f^ARR zBuQyn>YK#n6oDd7ryBP!8tR|EuG2fWrvh@asK%6)N{bH{xj_?Imw+Fnvb66|K&A3J zns7w8Jw5v7@27{5m2<@S+qI4cm$NmLgp9;XZ?cZm!%?x=0U|P@(;8f& z3%$PXhZD&LgC&HpKbIM`3~z|w=y8kkxn_}3mg3ExA`GMFCjdFni`^1#rhV{!pMLAqCu?1TZO{7x4olIu1>>*Vx36^`N%_mtPEE_*UU_iuTgTKw5~FE} z>mhC<|B`8EYdqq|L*0DecOsWfK018r6{ZSd{JT^9LRx?#2eD1Zy9AFL! zs!HI>zfwG3ZIqs+UhMCp+WX@3VcujM7dNgbYFTduR&=Nx4{p`nKN;5%zmFW6LoKc> zTlL#Eo8A;K>tjw!BGnkB>VTOLPa7irjl)#PW9A$=)k;SSTXV2#o}{XdqMamy+{D-X zs-ibd9oYWkBy%cK#%>@RS0Q~U8%+jTL7ykbxvB4{YiG)1p5T`dgZ$(Dxex^9?wlqc z%BIf~5ZBuO64w-_Wh5GLH^sje+2uFB;4lN`V*V~2%Hs%O3Xu*r}_V- z*B#*GyI2a?`!pR={U{bXa(Y5PE_I2UJ>fTxBWj{b#qi$JrNXw9@AT;v$~yg>uQQN4 z9c!BFX&GY(pT3Wc6pxSD<4)@&HKk1EednIK))8HMhFtKY^+Viif9ds%Z&5=tyKp%A zQ^`-q#veg*nisFzzu{m*V)|sgNCw1JVQ~_1^7HH6_P0oP3+jAe!|q{vd{!~F%aKs( zB^RH<{FJ>r5NDBn4DCrYDPqC`CVc;~q<`MR%Oh_>uey|Q=+yARy;-EUcxH~)w}lA{ z?7G>D)U*$+_zQu>E&5G0eQoH#_x>O4Ekb2buzbr8TojUZXObXL=aO$w*PDj5wn zN_AQeK%u+KubE0^+Skj{AZ2~VnzC;4Bo9swL=Z7&!xiGBW$-BT9-1u)=xL1@CTqP1;ip|9hcVc!=_YbdKP6>ltka9%w^tgT?eh`B=BhXQkD3qFgS|B zv9X=&-UTDIDv6D^g(j-*WZnIuSsqMd25KzO?7QjT#H{?$a%OrqtKa71>_iAv$xE(Z(}~qx(yl}Bx@x)Z zejMv(LG}L3#oA3$?_4oFAv&qcbeL66`$`s?78olhbY_>(aS?1xsaun zG^9Ucz@HTDb;Hhqi*me&DLHd0T|sqpa)};`3Ivvu_VU$}5^IYkKXziV2~;^Wed(`6 zROcu$ot9ReA`@SeYw<$s21?Q20Fj)XtpB9>ZNR>T1a#eNPee#Mij0H7S8iiJukJCt zuPB?)TP$Py(%jc*_Cq}XW{IkiHQRj?hJJ-povZ9Ar8XDrwrZI8fGDcRk_PwW@THvY zo%eoO>hfNnMqVn)neZqkmU-N$XNC9NOjP9sC}!Dm-tE(1Wzmk|>UJ1KV9sWFtwoB| zv9q|ovX=MxdZY#PH8$~id<2#Ma1MgF$`I~8TPA2RBw{$HGHLO}I<$a5AVRJbzYB9~ z?%m05?y+xcRp}!7|Il?7eogj&-^L;oDM=Y1(g+eFIYL0Xk&+lNy1Th-&>`*+{ZAHcbtpS^TDzsKi&yiD#%fj z;KSyjF;88!XL}gQ$4+88PSVa-+d*t+EcQCw3ncSDhm!xo8)(#&lFVBpX0Fc~prh<_ zm29ueKOTNLb$)xod29*O(M^X(r9?n5ve^!-T$6BIRQ)CkzEB~4A|H5~)okjdILxG` zPamdpR*G9odWRIF%Qvdgm0pI&!Zoo#nn8+k; zxr&-$EWbjJhW+efjCSLxL^k34_>bh{^&OU9uT9 za5+zvh6U}b9W3|hpxwlD0@#Vgs&+z^lWHHo`{`!>{_p3_r&U!C}Tr(VI5X#vMUg59u}{;3Bi2ST&7_m z*|$fZGecAEJaac+rF$pYa6j5=tGf5eSNn#gh!`oJzK*i?a@{zwP>6V|(1)qMfxQh% zfhrN9;Q^A4IryR7Lx{6^!`RxQKMVeLQ79fF zH~GQX2djyBYB#O%V814u*_-B5K4KPTt{HnVzea=Az)up{y0P1Q@^5IJv1E%dlC{wp z4R0HyGp%zD{6RdFcIZj+dPQsKfLXg!YV&*eL-qw9^bLHIX7?wzlGeQ+HHKzhtN&nJ zy5xH>@Xnj-tcCQXzhEzQisWS1~U-P2xnfF|_d`Y#q8rgqGA1k9)pv=J1 zDvAGp4)WZX&@!ws=l;~5au8SWdA+95b$hwb%qRB$T+S=!(ZH|23e7zj(Z8=Yn_uZ> ziwogth{_WRFdDSKoPN9;YO0-KOQfzEonG?tC9pa*{8~HC;UCV)V4@f63haYF{ga<$ z`a3^a=W{G@Zr|QkuFX@dZzxn4+@pzUulo28>+JfQb&de6bBF`!=q~chDtoU!Nr9*I z4eKHO*7DJO*AQoO8p)}VulJ7o+jrH)gUs<)=hXChnE<~_ZxIqRP7Bg^Gln> zZpVIAEZIr3i<#n9{J-?FDwpo(CADbZpjgyw3=3OB?-EY2|yBM^15dBN~crI2dt*y+lB9C7JPnjD} zE+|uPJI_FC3Nvj-rCgn20C4!ND#RitE4nm5cPF1C6u+4H=X^#QYlrxSQuaJJOU~;% zk;MXe1CRxhRGmr~BKoei7+$!Qogb{k1y@VWEDw7|{`OuCas{ikj~~)v=VOWCA(woH^J=-tV;DA!DFIvJE{zxCgDpFwJoSayT% zMGV)kQN1m>kt|Ktz|6f|sOP?y7Id8Cm`2C7UoLp;nBiUQTOM@O50+;<>)Car`; zDKmCZ14lxaAJcwy2~<}8OkOwsbi|{nr-2#4*9a2x>wi(XPNB4Col+e(UO6jw5o`2! zekXK2@wJ}uD8@J}F)*6J1=ocZohpd|WupdDX1 zp_}zu<+Y#-1oy=5yEU=tg#a?6Win(V70Kg<1Sjsh)ACkLtsz~PK6rO!y2n43ap;_( zZrGhMrFOP>K@ONta)sE1UfK?%G2ng)_i0jxgaMxXDC0a)MkD?MTzI zHzKqn=CHz`3QyTk^iZJ{h)W>AJj-gyDSPXlItJHEhg>aAk%AB~1=G|`YX(4{N70s* zo>=Pn{;suLie>a`iFrc-=R3LGUGaf~w7^CBIh?|5n8#_(tN2v`1R({sQ~Lb+RkhuP zk+kS+ug~LiWN5X@pr3zD3UcKGG+Y!bfJ%Ox%lS-r=-5vp$WG}c`;utO-Rk_`nMJKv zgHQ@nZqBzuDZ$fOYRLNqP+LoGA%Y=kh)6*zk6JK!wNrc#UJdLgld}>%&ywi+7>q(m z=BL{hBcL3W_66niQ46N_3y%Z}4nS&QcA^01|HC*60)_FLOJYc9LzrKjrOp%eJ zi%H`t5^5qc=4+V2ZWdynGyfya)J1^(vG~Hj3qWP>E6WNKkZP3mv_h1WTJtLJ?q z_n4v8_r=6X<}zFeXZzI!9kGO=E~NQS8XTZbI0)zG=7TLAo(HW!J2W~ZRXjMP4{@v7 zAEC^KKtvcV4SeBcB=9VrX&y=C!(f-Al6lYNHFux0ug1A#Pi?N}D@|$yH@sjv{O~tR z)28khk=O(_uB64kuCpfaP-oMifrJeYe^<6UNR%0nd5zUCfOl_ub-Q`!E0gWSJgVQwF0Y>iorTmhF|Hc<7wp;Zn#RQsYru`9S1|z_P8d^0(tic= zSU)spmw$@YM!%VuhZ}Bn5)iom%zm$*&k(Ml1Vr~f;O$Go`QWbT z>Gn@KAMbGuRJU|+XNk0Nc$?ij=reXs4%$T$CtXKgWBXoMuZz59JXH$x-tmSL^8;wk z(qznIU{tDfZ>Yh^@GUnPGWtG?ox$H|u1|5kZT^TL_yHqr+aD>WF656Cv#`2lh(Q|ZP1XL$kfqciKqDfPMx#hu<6$Rpg<4VH)wl^#mcQ`B$q-s`@_K%@cXr& z%z8%h`JGPxqI*+);D_e4Q02 zmh?!^#|QHvt~%p57qK{m8r&(4vh|v1{X&VEOVn}Wt-tWTZ#4|~H(6w_k!1rlI%~Xp!a=f1?FfUOI8$!(h*RWx&&?SZR;pB$JhQ}Z_NJ# zE7SrsHtP{4rX%c$>O3p~HVCzS1=|u4jtTq$By88X=A||7LAO78Y?3Hl{iN#jzLq4& zLt47AUsv0hxgK-FgRbmEK|!tk7F8|601+a(Ik^~PK+KPZfnNW~DZVrKl~e3B^u14U zk33qZ=UVQrX{A2zQe6_N2LLzUz$##m$sqTICB%5-d~{S{{s*JD=zqPhpv=;M&C`7m z<_xAvE1Mrs0pr&T{!3W+*_7P?d14%tnmUcw^6Si%M&E_qGt+Qlzi@I*r&SXCM{RuY zkZBF>O5W1>qEYzLRlo>O`&C>wFGz^{@Y4DLcsnw}lRDyv-gWImHs`7OC(SJczVTQ>& zw7bSXzr#iEFo@XO@}6y)c&@+q?0J&$^q7MzEEg_xH&V&?w!cJj=u~;It)rOpyO);| zcy^TT>&dk}^}*ZR(-iOPju-Q#=pxoa(s4syJu~gwJ$LgNa?0&}TD5c7yt_B7tsz+H z%2&|t7c%j-*y~3cj;|sGUybg)qU*Je_bNK8_p?VLYH!~oL9&Aa6DBx#Vu{1vas#p4 zCYZSuZB!dq1>z7=Yv1XwY8F@nkv4T&oX|h;9}}!=Sng;&3cMeb1{L!@DL)h!c=VLu z8mAnqfyK$-JoPUEDlG6@9q}r&GmbxNc0Wxve{vPh5OI!aw*WLDqJi$9 zY%g9>(Wh8NClQHeT~q=HCe=mFuCt7b&g z9_#0F-D?v5iPl77{PTy<5mAeFnf6mHD!rhf=^-r8{DPy~n8kge*X}x(kE>5S@x3a& zsmxBuEyb5z1+Z}$)NT9KW}>dMeFA}gd2#eBho)D%CEoax+RIi;?%SJoe)l&HjkA0Zb~p=Q@ZH-8mBhV=`0^9$`o!V*xIv^TBe^ zyMkY3@87O_WM}43SG2H=v?*0N)m=XF-A;HR|C%TzZ@4I>u&m^&)9i*PSnBmg0PA5L zZ9`0n5?&?kLTCz{%orz5&-8XZt4a6ToJt0}bz<+8=>-z+2CZC!3RI>PrqDhs^ix0y z@f0k9Y1@sQC-uSY&`HgE--fn495a8MNF13*c8Y2G2z)PH&?D0my^oM6->eRUz@`me6`OWIFvj>61bUV<5coui3=r8RjF+qp=E5K;vzyKefzCBS_HmoJ88i;I`Vu~K>@x8(op1H$9K^CFQoFBFH71Ls}?c2L}Dj}_cgt@P2XA+mv zx?71)Q76BF;Bjx;e0o104@a{dTV*8q*t+b{xj9>JLNl=h^n`M`&v=a&H=ECUqx)&`jElfEVukrBwZl6$GAH&IyhJ-qK?jkX}wy#VC>sLlXtguYc^@s z%6wY85shoId)zgiT!-&=@U*HGkF;R8eK7luE|o_Up1Cq7-+@H7Fk z6tz`8H_KMFzB-lB{xDRQmm$MfiwM(MYqmM3F4X(b1B^I5$^cR?0-!WVAufDMY>8>+ zU|oSkHqG>!;z?ieY?2t$843moL^gAb$F>abP*uuNaboFE$4QTWDX&(AX2%b_MTFYd?6L zI`=&79XB}NqqORB7Bzyq~1hbWbDlDJ#H1Kq7nKb+LcRKYBPj|mBPK~*FrL}s0>EK#c=k-R|Ot= zyPAPNb;i0MT@vH}=J&pfR*L|f`7qSce+hvr?4lKPQ?^!Hbo(T;)Mgs&Kjg+z`#j;A z)vZ)v0i7vEm*=-&^-W$1?1ol~960xd(_J{F|MB`!|=Rb$;M%knD#%MA&c%bCozVjk6;O|EKhW&+>W9QC~k5X7*4;`feLQ z%)Ck6(t*qKb9*1_1?Kl@<~kBsA7QV+$x)T4C>@78uZlSWbkhB1E7m^xh7{IK3swpY zk-r2~Y7m4sIw6iYv~CPJi-{cTc4G<~G?!QqbW7Q{bx>7_rZ`cED39?kG`k)xLL$?dckGlABuDL!?C1 zp5PbzZ3F}i(;WSdI#MN%*N_}m{tnnAIs9IN+!oK5et>KE8Y@TGNZ&+0HO_(AYhSd!XlouhZ9gUztFfg&+O*5l@p-#pcRx^ z_jvh^QbY?{+Kj1Bv~RvfBHnD+{~28QCP)7X$aCt{;5I$`G^5%|A5WTP?2}wsPmdF( z?Zth&d0(}89s9J5QlkmQhjVDLTd|94_wVU>2d|;g<|BHa+sYsDPmY&cnaAo9)|If-Gt%&P75)Crtjd{}HZI}sM| zZup}RZ`$QAyu-qo?&ASgCmEDU<}sipq!UQqOLz2&&@-HCJBALf!s*Snc>Cvfcod9r)bWo&Zk1aPpR8Ja(TDplIP3ge;aD*e4oSP>&QN&}MjH4j zk@c%tywC2y#O>UYuZ|)%uD)ZQ3FBD)40{fxaKlP3X7fH1jtEk0tM3gP2UYdRZ_Yj*wW`w< z@_Uoh1=g7L?fujO0kqqdOE<19T$fqkx}>YV8v0U16xb|0m6hnOb`rQ0`Tjd~i^MeL z!STbCm5k}{J=ps}?g!Xt4o(dQ){^)z$7g=H;D^Pl($QPgjV50pNPxT zRG7x)VWldidQ?0YpE-??PaKN4HaFZb;^QSH1o`^MGwIS=9Ue%{%)-NNur@)KUX@&c}Tw}{tuVlx_{^kIlW?a(Y^ zRqLQ8RWi+kPE-Y#?{e;kTYC_C6eO7iM6lAE)|?URV$dYkBH*FTUTN3y|oDfNshE6?P7V@c?E z#0jb2aIejKvGXpf4dCOvbo^UF zTUnT1D53COq|RM~5#SGuLh`cal-J$1kq-|S%9(GLY!)$se}aPcv#g9+0Iif2yH z?+?B$EAARi99PVr9S^j6(w$qHeh~?UF7zHXDj4p{UETg)sMrIF2?&G3JPLT9Y&UZr zNdyN@TP!70;(o3nyD|-mA@kkSB^a(Z0htFMV3Z$uc$!q0o^9v&q(ZAOa#x|0H{uP8 zsdv#_xy1h4B!=GJtH`WpGM5_XLX=nn2uD+r*bjQg1&(lq7xHy2dHy% z*75A4k5y;qxRj>_fK9CREHv*z{i^U={aU$fsuJoZcfQ}_%N&)d@~m_aaK%PHiysfg zIsn_8(;5j^e;wH7t)9*({~@itA=v$oFDMPc(jz$gzyC<%)nNYN?*}Pf;6Z9y=BJI| zWmQ1f5L0vlJXA?PEwuRk;u)p8&uX5)yiYgI$Nd?Q%%Ri-hrKVdhUZ(Wl4YrL@iLnU z(2jrMWsBc(U8M_+M8dqI!_{JyuPgh|Yskf}c z6Dj7gT2S~%7Ly8-pM9)t^qrd*ZNt=qLkx&CxwoIB73@sDBhzx2KJGVoH>Z*r5!x*N znz3E^rUyp#6j0z4e(2CuHGL7|14R>^{M_|)+nL4azY2R9!OoLK0b{SL|126Lp5tL< zh`quhbYqtOtjftgOZE*{X|{Apy4)~C=yksjDZ;xLR2Ua7fJ&Hv#YE!8qy#s24!YaA z)sbXGP8LhX4#-%(a z-R;?-ET+~6t`kvItn)>H8FRY2Xi`Hka_(J}=R6RmnAQ=rUQLAjc#e)g89}u_dmFDR zgiX?>NMEdMR1uR7H!_U&7cJ3bl-8VIYVlDAltV>;YDk~63wUh$#l0!{& z?C`#r6*4ssML;u^9f1lFx@PEEYZA9o8#6wDIT%<72iM)9zD`j>lO0QvLrmvk*~M}P zrTs#u*C(hx8E8Qupd!}I=HMVe1}pJcoIVhP{K^Qbwr)>1JKvxRS39axHGJ8@{l zh-q_P_OD%i9GK1tPKxXji&!AQQ&L2pMva;%vBFwqh z(GMpicQ@Y){jQB8(UUbIYmYTWHGTz&Knn>qx*Ck#95N=dKz-&KWFTx4Js^(@V~GF> zq*;KU$wwQ{aBpH`#q%%N;fw!^9j@9$m5Jq~LxzTxS)JY|^>IQ`)B>FUGWM%KG5B~y)H*OMHWeV(jD}tQJWP$&U90%qO=Gx4yO7iY9PuCxx zID}?%y)251`9sgFdgQ8_-XNG?eeQe%;>)L znTMj#4uR*2wcLuY7LNl*emdlUwpE=|SU*)V`&4s|yo_Bc{N1|e((}7@@9J#4=r(zD z1PkjG1n*h1sR5A{0vv3rjl6M{thsr7Z|m;?y}cl;n!5`J;i)~-?1q1fHX#NEiq7EzNQyDiMQ1)Z@>$KX-497TBn`p0!RFAWx*ZQn z{iF&PVNsiGSiH>W0T=B_V*7RS60M4#?r3pANw#)Y6;X~z0dfRwjHx7!ZGRLF$GUYdEqQB!E>W**1! z*932+8qH1LgP|w$N$l3r`R+>(IT)fidC70rtAuPy52(KWUU2 zTffNpr3Gy&^zmsYf1w@qp)nkKhqXG>Yy9F1GNejN7DfvZ`)=GVB7RezOZdl zIF!`o{Gxi%Nap9&Z4}y}1BtqsjysVLF@UDL_m6G!Od;t}C>wo{zn}}QV$pLkOt0%B3NIEUIOvIvkiI=VfpsOU}C7bbrx0b*{&-*`Z$qxFW0|WNHV;E zs(X!Htenm|zTb#mgJ3xb9GbaSl1RnUrR$%!z)oJVZ#DSbP-@5J$aE-uSv2e$3cZ{hNa;u$G zlO-jP6#M4V$Llk-jpw{)Rqs{RdUPBH#IiN^&{|V;3{BH4Fjfa$Km4Dr=)bj_jA_x` zwcQL;p7F#^e``0F*+iWWjzw61_>~b4MBy#%pZJf^CHQ6tZ{AWW1A4p=q_BT6HuJfD7U98r0U)^kTCwO=C zXut)~gbBPxfuh3;s~pSyu*mEIedlVU-7b*e6X}7M$F)f!Dt??rfkUT`@}+jb2nL^* z%_mE{EeJxL=$%v?(E->cg>^~3`&a}b&W@1=ebiX>9%sX&4Wna80lib!$*XHxx~x6h z=i~g@8)(2|#jA|*80mW2CkJrB0~vZg4M2by{6FG=UGEQ#qYYch*=?i@U)8ahzx8-= zG~_4Z6!4BVF0-tqoo_KRrX0-ejy|;S2uz4lk@t`_9K4iE#d!EFq`p}}9T0Cu|0CXv z71`?*z1nW|h8%j?)?dS-iv#8SN4yyqeOWsu0u1TNcCMLl^_at~&q}tdMWvc^S2n_* z4jNsdt3S)TC-xYK0-l?F?6B(ML@7nDdMLhU+bQhUR-HR_tl9eu?>rnckzr&#Wi4Fs z{RY_~4NTz;$z(Ckj(6#utK=2pzwqFr55l(dK=X*m>bv=?0}vM+nl)2DP8pS(linJ^ z(?JnNC&tacdHD`x>1Xa1O^a)=7riq=8oN+=*A?O{AmQIw<40_iOFPRebNA`a!R%bqc)g!zD#XTcHt_=+Yl*}*@Y)h~gzGNa z$;${+jFX!%=%Fp>@H6A&zMZmizirN$H1lII_PBxlMFHMuGW9Xu2Cy8! zakcP(SP_|y{nVqLnq#56()Gryi8ZP zY#r~Qqz8kj)SrEEcpAdpwP^Tu{)@4J-j)<`*$>LAb+x5n;0${h!SI1e3o-Cvs_Wgs z!V51!RYpxJ4DO&eFj_SFusSignmqarH=Yhm*}1^&%PKi4c0zORE;(=dyW2c8pYtzG z`Vr`E=CpUwDzYi&8DzLiHjT?ut>QiRWM1ZpMyG^o-z@yp3I{zl*4YYZq=3jWvHY7QMYsOSGfW-%Sr8yVYquN)d zA2iNZ{MHNM1IkVtv9~C;Vd-_DZ?l^EnUGy}@_*hux8aV@t(yJyw|8$ozOiZRnY~t0 z3O%|JoPe)}`855lwuObZ!lg9buc(9<=SVXoO^VCTi9_ro zf7vZme>;)5Kt~)JB$=J>db`fHVchwkb}3sp$TJffF7P?Z^fj7yI_jvz@n9gBVG9?* z&hMrpBq?H@Fj(dqEMcG7Ee4I=u~8rkQ+8Kf$ZsZaP3Mr>+_Ihhatq;k)ZHnd_KuAT zFT%X^;b;?|QEE;9-py(!Izc3LaBm9HbsaqI&Z05RrMGAFTcjUd4tMs|huJOSP@s&w zvd-jop(0;(yv*(5gK1d5!hH>mX`5lDr+e8tZLwD7@cvuvWN;zmU2=KKTpmD`;7{sX zj;@BU-5*0N*@J!Qc=NPaMhvFvVI+?kzv-?vKhFmbOodV`rkP3v36Q*KefRO>H7nC0 z?QfHOW_yrUKeb@GZKYSO)ILkf*2fz$Zl1d>odcn{lP6@koWPgsOCA&=*l|Jj1^`j~cbLNvduF!`hMI_Ktrxw_@Vr{0@ex-2fnn={u1Kjuei z_?1Y6PQgXA`uL4^N{1Nfioml(t{hMSEIqiWE;h>@9MwGa8Y7-%wt z=f3f>&?7QxuF)P{2SLu2Ux0uAA*1Nr( z|FE7VHTG4>mqhUv6-cSv5gy1kvQng$vJ&6E&;~cGAYyXe?N(Shl4ANP5&)T1KmWVA z&c7XKuIm6e;_h@$eQye;w-*?TgE)d;vIN_s7r@kFeQNK=&NLj`$B)erU!JiOg zHxT}$Wv#UNC&V})2F2GP;`xQrfMU7)r7VMuz^KKp~fBS$c`72 zq=U{){*O1?^*6VPKhqD;$MVT@+@cgHKQv3-JGTkP6<v zUSqoi`B68NK4uB~G!2e`-HY<;!^{z*f$mi{rUX}ZzKkHG#%J7I1fUa&&=l8re-R^F z-XBk2!?jbXLEtK=x(>MhL0}WdD(xAm6mPo{Yo_OF)uWm9uce^f?k#*iL7@K6n^(Kf zRq!XEv^2|ID-RDcVjcoLl9zvy{o1sR`XpwgY$Z_I*0DNDkw=!9p7Qlf zy;SAHPuXpOCL!-WkGlkG4k{^Vzv_HbSpj$0j7VsH_)a)5TYcIo^hlqGUQPE0u|pu{q_U{8xYi8~(JO!s*m}5O zwG89Tbk1Gdc?V5V7N6NwSmal~kxx_NYri6uaAa$~>8p!zVdJ;BudLW06MUuFb?xQa zUwE)x+=PHrBcCG+rPaP9^-dBx7Ia3S{d!)53BP%AD%`_~9Iw=^Lf4K^AJn`Vs*OZ~f-{&$^T){U|W%WP{YX;JKkvw4i@a0}m?iFj$A=Sg0>T?cZ z(Js#@(Z$TY>XFmnx|3Bm%nZ`K6;3;n6m1{U3|Y#zi`V#&KXz7kuJaX-#qO%3Gf_(57+wslsWHhgv9(&HB~j@pZSkR;JE%8+_5mIZ{Wt+4a_G8RuZ&+m{OJ zQ(N7`Y1tvTDt?Esy4bXTQg2No^o%(8f!)mcgTY7>3cqa{&!3myr#Oyn=}lGdaSp<{ z=93i`q(+Y#Rr{nV0Lfmap>JJJMuU0pI9Ny?TLtMU^EHcT-djm~Fpo?i;a^sGXFkMv zLy;i4bxAJ(IU|Y8TS`9cf54BKeSW`iepT^>(yItw%1Yj4Ui4h3-oC3EP$0}hVlrAH zR!+94zCBaIX)w3{IChF;WN9=qZ}PEWjbTL_>ex1xGNF`CJle_MG3>3`0y8@JQtH0s z)PqrsRady>P05GmZfEv2$22A7X4wt<>S@)jfvE9o?nv9%qa_1Rt*9h=%l4&*R7?|Jg9(DaWO~?kfjr>+(aKzo$HgV0SoCy^ ztMi$JYn9bZE3v6F>b0Y35|AD-*t>b|1Ug3TqZaO>^I4Fk`!XsMtBY(qz*~=5y5G=8+;Pnl>U-*pA8TqztH3iB5=hh zudpVp#An+cEFJ4-{-clnRACjL*Wp=c>2=kF(t0!zcEL=;PYaAV8?b3WRz_*}FSdP{uUk4ed&rS8i1u+pD zI<4Ij%dN^Ao*y|1yDIWt}L#h00 zIv^heT`govl0TXc$;{NR*quSuWNX;*>!MoG!QMumP{aAKdi{mHD81>nGq00O!$U)5 z6#=Ml91q(zZq1bz^^{pd%bV5Rqupw<&WecexvzfaS*^F#zl6fwS^E_kz4d5Mmp6o0 zsLGGYBwigf91TmYk(ltKVC-VDO37QkVoehca9$r*y1dGkGYJx)`7$U0MfYg+C3PGl zkU{HqLjJE1>zWYZGb*xsa}B=R?(4w5Hqxuy&nBf%?X4P|ikLj`br z0wtq)OlIalYxixy&F(8jkE%+`poDQJ;ZDIpj#BiW{M=9$>u)X^JUdpUvezVX1_eYv zcO7YeO2yj6(sWY$&V4z=>SQO3bI#3GZf@NG-)l@PSID%uE&o{TL~qp-p=93K?qn8` zk>8A(HkJSXf?u)U;P)fdqun3}gotlnpmIKq+GK|frY$OP^u^I-8KLbx zzk#`K5DetVCPv;o-BbL)dE&FcLMOI8TD_GP^*;88+gUm(;*iGmB@2(L*kUBABih>F z8>9cdYy=~Ng_L;U>mgVsw(uR5+An1s5O5hpB;Fa+I452DXaVz>hl_si-e8WtmWf#> ztJ?5z-@ob=0_312zOMzpfB{TI_ZB5e$o=$G#>|Xn>kM-kRXEKj{7DIJ@ zm`7+c**Q~PPvHceL8_~QRktAd)@t-lpT{K`v{Q5iLoO$8cE_%K;!`~}Z5*$~BJkJf z1l1O!JF%}JV0ZLpvE(#1$nank`0X%O(W8tK;p+GHG-3F5G{0V|KpHb!(cZ=AI-xE- zUN45$+H?L3FSoSuHA0Nq>RE?siP^oM18-UWiOKc_Zt73r5dPMa8=&}Emiw1u2`=G+ zmNovq_ZB`TzQ=uQP~B~aM+T#s5e;k3bGy*lGjB%Y74g~|jMLJy`4?yP*+=8e&4&Z~ zACoB!g{3b(31jSh&+R}(#?bt^uBtr6o!ZW%Bq()5ux3dvzE=5r-md4RyD<+Fo{?|p zy@oU)QhWN6k}PC`z-xU-#I3`zIosKV*6Ly^n`ps>2rV@>a=z@L4ToW-*7D?g&^z|d z+$TYqXM1Sgaq?^U#X#VnEk*8o(hU+|gMOvhZb6M?rp`)bVeaH$uu&5fQ?#M9Ns|m* zR@#{+>}9xLxt9u0`r_)>vaRLd!HEZLT%E0~)SASPB(j>Fm08PVbr5o(W=@6%@RyO5 ziNVjdVJs<>^Ovjj+Q`nTtUAy|?Ch7bGx7>G{7cYMLTpy<)?MqY`t{+fvC>Q>&uXN^ z{?^EP?iM1M6imYX_6qLYW&4sn-JVlNEGhV(M$r$8@JG?JN{~P?+>zhln>P{FsIH=%I?x$e_OSnMMoOzZ*6S;*^0{!MU-@0nS8F(;EN@ut)H2) z?K1D9^bU^2PL}VzSWsAKgM^IVC3&0)W!Jt^|7CY<8>7?8;{r|<^z!7`X}o9>eFhKt zBp9}%vMRTKCKZ}o{9A>L_gjTrFp)+r`ZYu7#ybG2b3#FB!7c6IuPbylO)ofHGJ*+h zEp#Xd@{-MRZbC5nE~(FX!0$zXAZ+kH$Y|oeTppHs-Tr4tdJsU)c}N}sgQ{W{){CWLXVW^*I^OAMsF*PU)Wr@oW~pAdZWuRj&&;A;u*IckXFB!HIB~mX9Jlxs@(A_E!rEvzD&qt{#96b<9>6~v)3TRu-GD# zW4uU92-(JVKr-9+wrIu@!}9Alwt_}dG$>5OJnnEm9kE!Sk#1Gz**To$VME4)_||t; zAA!wfjh24Y$C;g$N^1`2wcbgMb|+F8O0lhjptV`l@RbFgJkHC=*=)~0X0=4ZuRd-n zJ)L4S066odoM@Dw|I!`mUaG}fu+L_pC5#;<5KVbM@y3lKN9<<4nrL~uAa|H<{b{iG z71qWAEQ?lRqQmeI=Sk2q{+nSUZ{mEms)gBw!1MIa?fbvel1zzbK~oY}z3pcQ_Bx0JZiBLkmfoBU|SDBO9??+;UH90P(bp5EeeP#^_(?uX#I!C%n=sf5fLd#b9 z@>>el^vWzSixbx2Jfce(tVhvMdPpv#bY9iYIRk3N&)p^1=0TW;60zapEx-Dwj~t#+ zJ)Q04&mh{Dm1ouAjYPN{oeB9so=$BX_*EZQXee+AhU=s$969^F$<*y0;Sg?3rdd;D zgybAG^8mYi_?2f5dV@9Dc}w%@vr3eq)^t{Rd`kCJgNvK4&3GrQO1)lXOyZbo;lc)5 zKkK{+EdMggk28B8(0bu%k&_(Ppzx*(b5Y-SPoX^y3`>5&d!WUzc zTb-}UQ{TMa!C*EEpqMVtTJ?QHon;`&LsnYl(MJ-=2_Ass_>^nd&`@?sS1VPJG=f zu9BqCslTUmYvyLzeh)W zGn0euumcZpRK<)^8uEFVgwH&*p4N9&L7tBc=dH??Bg~r|S9gQ6g{_1og2-88po`3M zij>WvU{9Q0KN6G!7kD01A7*DQwJI%b5Vh`OkKMS{t}z;a`Oc@Oeh{{cjAmIUCS=Ae zAI6Tg&LHQEmz5_M*RI00>S+e@nH&Vs3Gb|Q<6rj|_1`S{G%;u$B^HnyQ|&o>jUuJa zdv_wpA?w`qLs@9y!2Pn|`c*|VBmT*DPE?W8N~;K!G6)726Y=P}qjXiZX$6gmI9^4e zdEBf8I{zPA=i$zF`}c3%OSRS33|d>MJu7JKRaX-m6MPD5*VS z2N8RZApNEH{k`w!`#i_*4~XMPj^uKE-tX7zJf)kBC;WWHQyDyhcnTfD93L{e+$Ng! zHXuBkV9_nGVB$ov>-XfJ`gx7Nys+Cc$sqD{1*phx^Qtmhk(W?Eee~zjOt?*i;JUD` z=ow9V0{bQMOs-)b(q4Ih3kU9{L{aQT$^+;Bf5h0@DJ88jy=j~;#`ZLP+8gZGU?u7M2zq|gyIL^CX^XGxQh_Fv25NJves6ap z57{!k(5-0jMMYWtsh{eOev4wgbHNf8ptWaw?DQ`HW?nOWKbaQ}$WtnW&Cf#$>MzYk zxwns{--cOP7@0n&17;-EY1;~lwz@m^DSQ4&DdJB`GtpLBph8c+KdzEE&c%$#ZjOU_ z4E$s~%JAGMS@TiWb0TTc`=@9uK@|Ltlu_fse-mPN=l>$a+Kg^*me{10@YAU!;`n+M z!Z~c!Mb^bcGhZWjLK>v>6 zH7IPiuGcNwHqsVPe(@eBXxC^|&t?|U;~Xe&(T*M0P3p4IcKucL-F!xhnI|Iq4!6P$ zAejldlA8?sz&roLQqjf_v+XmHl`6X50NkX78c71*9gS)=)?+a z@>9Lg#)@8eFwtyQk*22?XoU}dy)xwm@D?W=Tz(PYH07$pfzp%fT8PGsO8AW|OuSTD z?#4j6?8wLpD>qXVvqCv)HphoCJj;VMdvnrgE!6IWCrJ6xKx7|^LsmxjYZ$Gaum_2) zjz``Yr3j)}P>gSnzm1$zgyOgSf}M)2em}xAMcCZhXQzPYTU)cV~$ggHjj2Waie--4`Eo;40sKjz@Ia5G#pA1O3IVd$%c109KRR8nCF=; zQE1Vx5%(>lq6p7BZ3c^TsD!=ju|!YAuJhP_Hi(f+73eV^2p89D6W*%^J-^C=HFN`d zw5%Sv6~6CkKLS$Q#Zcr`E8a6+e0-VXs!azV$=*n7B{{iiRDRnm@4FfNdqxbV$xz^} zPKE{sx>GkaeE>f7$PJZ6d07}~zdw3FQ*n0eqM~4ih=2pZ{p8S;OdU~e_4xVv`+3b-R(4qrM8>zk^;y_@Du{>^_`TIf11B=Qky^Q zzo}e_;N`|idBzn@Rv$J0PrY$a7UL)~5^%WzvocfUCPsa6=1kj1>!z+4vzP&)g`@nBY^g`vhN z21%##qWVKdXC2RK8t|b#MqO2GqXSZ3>)KS<1Pgw4o}7Yz{fKnXefb)40=AL!Si>Y1 zI}JbzTQbFRDkyi@0~GtB~@*^ z_hKeT#{#o{>TT2XLuB>)8aRhy@h>#*!fE-OfD>Y5JjivD2fC(kJopWkEfr?c0TtPT zX;KzKc-SJQ$hV}6{#1QC`qG>%WP1^)j6nTboek58g;u z)u{^LXvX1fwn9!_Je;M*zrJDOA7wqf*FKKY(YE0X3*wGp8$iU;lm09XIJ+?)soCD^ zcb}EstoNO4cWw+WcOd>ml)YR5qnW^zI}Lfd2L?u>jC(LyzN|^va1x3xT01cZt?zu< zibv@z&*jTbw#p|6zRSm+#s}9i7v_~mD!zwKdFC`l5Zg`g?gsp46SspS=vR>Lcaqnf z%e`JeA%|b9wdRik-^eFPM@?YxN3~9hcXZ?uGGp#kaM5J!6ybT`7tFdi%W<#R7v?i6 zlV<+Eoco@?9opT0g>Hy@mCMHojO`~Wl6&%s*ukdwS_32vSNA?>!W4$bUcgN}7K z*oY`{?mU+}S-^p(%lo^6JaS`ovlNASjt~o24pFerg)KWjA9ad({EY5Wp!6FK@``A} zKG|f%zutG%D368l)z^C+eZ%))%`g~a+1inlYAl+GO5?=uxgKF7GjdbeIUOVDyU41> zq$cj3484%~BbyZUH>7 zVQ%3%raH7w9#AzM6|$6p+;orQpfvSMl#{9qPj4=9A+I`Ky1S@oP~fr?zTRaG{Q(in zxFzm6qVT zb<5UnYv4hK8Eyg{AuQ-5CIk{2SO)h4CvvV9(RzhGb@)u{z40|_Nq7k}vkitb#ddxT z<$HD(EhzBTwH&SR%~P}Kh;mVI7zteXp}PyaI1U3Ji?G8kYT^kF76bG(~F!{cU!q8+mSN82uN+;2oV=x%a zlf1xi_XVAR6U^~Ad4l4I*o=s820&%}694u$k#c6Cl;HWOkbBiCEQ$kMR+>;NSM8`a zHf z{kn&r?(bTM7QGyG?6SIFt}E@N%n`%5Bst=yebG1}=^W>jz}Fj}%O*tI7AcQA`G(Ue zlCq`f|MVI>ptYVs=B%T%2S@sRiaiYRJSO$6zz)uTGHm;qIp%By<#1ml^z0xENfi8& z3h(Yied#1#&0Sa&Uc|9+H91+%z1>ERxTOK{DKAkk2hscyUJ>>Z_H0ICZxiF*tVS;^ zd%P^@6Rokq7$)x(?qbg6WjxSG=)3XEf;ya4!$8xN*`WQ3u#1^#pY;ASV%2DKff?KN zJ9nD+V2)CB4-zOi61(&pKpUD&MY-y`yI z`$CD)>zNiQX4W1N%jd+^338unA+}Z9CpJe#;Y4QfED@%wee}Ac6G&PW0h4c=U{P<& z;c6==@0*cfv(&7+W14Q?-O$~6twrg5t*L9>$aMAT`KCm7_krgn?kGjVEOaWy&r8AaJurQ-$XDEH}SM;w>10G zDXUw=U}^I+$_(r39he2 zo!FOMLZKQnI9a>oYWYw><=Z(|9#VR+Z7rf(${lWT{sbCqZVIOG*t@22;7hp?(Porp z)yjcytVO8BeI45E`$wH6LflZ1J-(%0l2NU8%_=#JqnJ=x7O|a-d+kT}Ckj2^aVa=) zw6(g=G%nNY9*#`0iS8#&*Qrdn#5rfWG)(pVJa^~Q4jYlbNUYeI3s7Im1s$4@af!HJ z9E$%Ya;qnxZG&lfsex>~4>KyRFdENqYr;;4k{wqEsw3KjK()s;DiphmQ8y5gchu%x zZjQaIi5_S4VF$XA$c92EsYxf6#%$TdulMki`@6V~*RF0g(Z$PmW?~ zt#ADhn9)&Ce8FZM_3i!M=y*y1!5?kKCZV1R2NIl>K`LI6MP}wLK1V~Kg)LVR!A2f* zS3^4DIq7LlIHn)}vVM;81JR7uLGq&WqWmw}ro7yy*#?ZX3xPKM&Q)4<<e) z%I*g*f-n^y=$5KLuCwM&=1IzPN6IQn-bE+lE=trKK5WCe@97;AP6X`l5Bk(EiPTS7 z-ARv)48;W>jb)9RrzhlTzBq5pL|L0O`Yk=^)DwIRr<0trBzVb}(&G4%TWhX5 zMgIC&85X zmyk7BXk7U2_Q*}7b@+AR^hp)&1^MX;ul(|hRt<1M((s%o!M?fcKl~UXB%cn_1#n-vw?syx8UaS*n5u+fcq*7U>+ha;X^nHd>{pbRhYV`T@iE@_FX4sPH7e0 zX|sn_$xft_3H{dK2CjD-kHh_*D{QHy$)FZ5 zRWr*Q?~pQa7RP@OkuI)%(!4)}HqbjTgSEklW1K#bHhaWo%zx=qdCmlbj5@Bk?2=k;eM<(Es|4SmjGcOaZ}edFc4G1Ul1S=gkfS6jcxU7q22m zjP-VWS37cjsb@WrxFBWS$}ofHsTZHp=jUo=`#rGY8FAt$XRL6IT(NqT@(5N-ePu^W z=<$HYT$Ss2>A6w2GpI&|@>-~jY34US7FojitjXyO&8|*d(hIz8VQOalj!EsCpwCBv z*#KsW3XQ3i18yfL38}hWYxwT=#JKCDd7QB(nO@HfX>Xo^BI7>LNc?dF)D0ZWJkODx z%A?B~n9J$wzIaJvaAChr?cL(-QA5F}T5P3XZ}CF z=u{tp1$~sQ?rpKHn{lY_cc|{%Muc-FXJt8!`FM%panqRvc)G(}uD; zX~6x8ThNoax&$n~*i*el;UO~Zsf=t${PZ!%rUX7=ScGjl@vU_5v2|2(5UGYg{ia;- zW%uQ+sEgDQ;v#`h?sV#dn3S$!(T~fATpB&5Q&DW@FGNC)R6d0rysu5veCJMde{O^s zFamFEMR}OfZxh>IvDxWVr9V~i+8~!87%bH1aM?*C2)M%?;XUNj;MX(>+74TA^71rV zKb1pb+Mbh9UW+TXBEtxoWek#h{;*83UzmTSLD|qe$d_h1I zqV(}`&GVkF{j@X4JF=v@fu5hefxAY>6%ZVLB>(uep{0ioQ7dD_2nUm#BDW z-)+=DF@`_Q-r$wOKb>dC|LHt?k6diL+3~w?FSvQIwftA-`C7mR5-RWoI|)|$1Ido% z4f`9CeIn6bZ?^HEfc^dtHs7f;gh{-y_=V4rVDkz4()h?kXIdk`olh1l9sc8mllQ)L zec*8=J>h+yHsD=`KyGsh8!Z&e^PkKRY&@pGPHGX6!r)YX@sr(cZ-zKqK0!7zZT8

*jg%;bp;u_!8xZ8a>bkB{SKq!NR+! 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

}IHpVHxOJfgFPOuhSjEho_-- zKpT2~C;7p^Zo9oVSGR5l)4jrlWlcw$BiEjZ-y0;xtx_Z%-8(O8UsWZ2K@!Y7tZ{cv z8nS~&zofIps6Y1$1mh7fE)lH`FsS}aT{%oTARJ3OSgTrCv}7~Td8t~DT0tp4Na1No z13_5Mf)<-Ncn(w`zpm12d(!t_$4@vd4;Rs`Zr3?SZzyw6y5Uk{FrndG?Sg9hKbkNO z-+Eqx(QKGKi{|5yVlp)`6E6&E&nAxn6ro?Cs@(sY8EJ*)aJK9`NKL< zsfO)=ucn1!ZE<~!HPU9Dut5{c)5emh1Quo8H1K3JS?}rAk0Rr@Zq_W0&nm9x9Y;;> z)(GORmQ$~QVUh!GHH$ZEchGUYe=L0t^_{dQkBpAcBU0R`!(xxpW+Q`WMC+XeyIi&+imAz+00;$#@||p{+IY+R5*Bhia3iBNkB% zagA9H66;HiX{sWm?>-Yg44RS3+{Nl4fdE`c)4A=H3>)tW8*_CL<-0HMUu>kh>{fAb zw>EjHsrJNc^ES8z^LwQ*unMF+E_y@2KV-Sthkb>@=`XrmagmiI(9kcfqVk%(k1uk7Z6U~ftJiSOwObY1;I zCkEur*XC}9KdtQ*_Sv|ke@sgYD=kZVq4&M=w6~PW;(Q$;K9FO4ou2+g)A5;~dtAvD zf6vO0o^6=9=uBw!%vR9Wr2+>Hr_3l{ik32xEbHnmqh<^a$*Y3o56IB_OPn1H3KHqZ zq{imL9Nc&_IH*q8pPWq$pB5fs;=QvgbS?ND3Sk)eD9(M3Q_6FVECpGjWHXBSod&j`>YLV8?@0r7CW7KW#c!Bs==Mh})@#(+7?g`~+({x2 zxjKx$o%`b~R6o-o%6Y9;xHKA*=y|9bZNSQ7fX#b!>C_-k@UWS9wY&ld<+zHKsWbYo z!CSs{x_dN)X1nU7EHvSUJ$*;RVJ(Bt20;FH@m{g#vmc~xkfcr0zaZQ|O%D^XVfVHC zIo3r{)c#tM^=W>qCfOl(Nb~WuWvMo`znkFboP-+P z6MFGfAH31I&vaRIm!@B1XK)+#%o()zDxvQ%RPDk;)8F-iOz+e8|FF1C2)(1;rdnE{ zXB3eWjjkS6$Snb67_}osI7?ib$>K4$^A=#KR@{{#boce@Z$l)dE&1Du&y<)Gd^zDy ztO@C^f?J24yxCe0mM3M$oagm@a%E{pwhRs{nD^OpKX42mCFE<`Cp?=0ki71v4j{Ie z3CPNl+xjzz-H?!6>YdNoGk4;5Jz*~0lp12jLV#!};+yS2jIdq_dx47^8)BeEDjNIw zQW2b`l`}Y-JI@!+gb*AY_)DAWU}^6N6~n5j+!FMCf6%L`H*CpQGKis_0?`1=qC&gr z3>`f(oChuHS(3Ih>0S~?O1q35=1j|YJ)#rcfrkZubQAV-yY?QvzIz#%vrvDeK_{r} zd4trch>h}7_T9M4oZUwcTT#Gc%D*$c7j#goxiR zkMLiehgUPD8&F?kE;?0x_Hv_PG3`K1t~aecJb{JRD{%(${mS`#yI?ki1?3t{8pwCO zxDcowPisf86}LE=UeHXiMhc_H9<(*ur$mx?#MmmSGc~kq505+~axx4SBc6)S1{+;w zl2tj3ypLGDRXU&r77qHGVCoG?XVpP?{k)AqKIv|YKMws8QIxD*qJA#Zdfj~I89_sO zIK7s%(~)Ion)Bcx7b8DiJDeS%Dy0}=-TBWCO-n+xUH6!eth3-pKf`Mxvz)=}+UHHy z3pGN$Tx7e*{b?;en!eG#oJosoqDQ*4k?X(TJ8YKz`K5mIvitK(wW2lKcsFh?W?oaP z%Ml6a!PdO@6T}<>EH=-t=c9XV%HfX$Ssj3o&8Nv6Ozej zf+x?OQ|16ojGO|=$5)Mi@sd+JKWP`8>gqa?mN}})rVT}_4E zwzv>$FxSb9SbO)NznVmXYofra4KXptEcJIisPkyTQQQ(v8pk;9Um+MSCwimM*BseP ze{=Hl%Hd*CGA(@I4ktO6cmh@SoaW-=IDAdJRXDx#!f$`@!o-KLkmENl5?_Z&%=Da@ zG|_n%Cm%G5=PY~oat<{q4l0W-+Z=hU8vNbeXbl6^R@mKp3t{jJt`=ft&# z8|53jHQS6>P7U?|2u9JA!=39st53xY$@0X$a^<&mSW?2Sx>nibA=+Rd!3t!WU4oKE z9W-WF<#2(in(6toYH!!1uig*aY}bZ(%v({5FRQRtR0QfQJq>FtT^-?X@v>Vb)D%)m5ah_RaE|{&)m>8ifw;?(KRfCDwzfV?= zI=2~OO@lTguo1-Ze#_i9#(N&Lg3k`ht{D!02!><3W;rz^h-|W+52Z!Yx2|MhB)6(O{cwGK?yk;?m#sWq;7jzt z)Ry2tw0}U^2{#ejYad{n7U#fdU0t2K&;woQ792}xIUJeX#|#Pe#YJlu7c**MT=A~K zhx*MC;%_emt;yDHzf2(co~-Zv+K4Lppy!9_Jomb6?%d1^4_2S_P?mlJ)N?P_n%X>=))}fu{hQ!O&f`>aDJuSg zU%^Ir=o>ee{dVMa^UOIg>64#_T5X~t=vZ#6wj~ZKyOC3LtUTDRk@Vk0A+?bp(;t)r zoU`TMufKR4?>(PW@=KQXW#8va-F*9I8F7D<-;I)kn}&(nUwEuPQkdg5MRuYhFI0WC z%m2zdJ&e&TgUm^SBN@u20dT##6X=gG#43KU*V&F3xzOE-53ag{$H znOS-#))Vt^XgTXaaR%K!cS5}KCUUT`aT(E)V;@E+SxP2#)R{N!Q@=RKltuqVHAW-R z?@5x*S<`C!n7+?-0RWI!eK~6z9WKfL)o~%iR@xdhy@L z_g30}5a@4X9A_nioTT>Pjz-D`&FBE5njN;3;IRbuqJcY~*&Mipe;+7CE?qxDhC^KK z?FU$gi$hm17!&*=2H0Vfxx6dCQPZQW3#^1+Eix8F!6Gcb#o59b6#RO08hamNd+DK< zxns&S4@*Penf+f~?3KxVZT4q&HlqCUBNG=nr!%^IGbQIxsqPIjd$R~F%ARsmZ?1I5 z5A-$DsMXMp9d6)B{ECAN858JAxc0-#N=4ubX4g2on&xR1;xOe$|zO_S1~toy%|DHOIY{LH{#Wc~Wi%gR^!dKX{T>T8#2 zVBp}`bg9__ukY(P0gmcy6X|~`{2sDUDyLvrQQG=7X5taxP<`7h_>s0mgKRnG$w$V& z3EtxS7iYL0ErG>t>wYmmeo=W59}eqTbXfWp7$g|ZdTsMSE%XTKp4Md(pj1XjFrB@Zfmo)&s?@moUibQegAqwxDDM`CiSRajG9Et*-F}5aDxqBhitjC2GY!Ksdex+*(}LfUh~gJUvK#JD zVPqCAuR5Jm`!FFz0Xhhht>q!t13DT|QMGaR5@mf|LF~H#~X;@6TryL;Hb~af^r)o|7>efJ+4I2x_su+`74&F0M#5~`iIUg;^v zNrxF$jN#xP7_(WKVn|_y(LS(!AH*fo(g}s8mO3PftC1F!r>|uT#YPpqqb@%;!Qu*H zO-cb@Vez_DY0<=-*)QP)jWOKb>u5 z&}Cj}uspD`M%+wH{+Cv|>i_GF{>$P~3vZjCL9*@SAS|pMe>~~iPN&pBG?Y5D?|)E6 zscN4M;SE=F&YYSv!Q_H7#MxLZ0BQ8~5wnXfG&@2fL`Tvm@W8UvOU^btdq3cVfZ?cr zZ8TT1rX$Z~x1q+~fa8BrI$8n(~I= z32V2Du1hDj9(mnt#bX=6Ya8H+rnt9}i%VB~5Dzw>I-z3*jk)ihZ?z%V@tCcxcyVLq zk5|_-WrQiYVE@9~qA%3MTd(iSJyoJ0JBGiCyC=F2V5RH+K+U<#H5BA~y$_CoIs~nR zJ<;Slg8SVGchGuwX;-3Ye?aV#9MzcbH-}h&gPiQeDro0sqvHrJOSdk> zl3s`G<veLA&W zw}#kPo9|Jz_8lL5`DAljWMZyCuKPn&PbbS@HBeU$mb-eUsB?I_W_Hn)s0*ydf0*6LemQ=r#&lx{?cHEQIb@XGkD2JKe=yy1CB5>Emi4#s;a`(Vb~!z2T+nDl#~=RC!y2~V2?h_O@i%!VhU;D;jlJAcc9+lIT5NkJ&|0(qfFa0R z*{i5Xbu8(}lwymM&!5chJhKYp8C<0iHETMS#vKAfXyg3Hc1 zsuV_0whitXKGIjtI)4MY6>G2h7xgnvU+BjDBWd+F_=~Mgc3V_%$|5%VK*M$*m($?p z!E7pppJdME^A`W&EWWjx^2M01jKUIGwevP!i4Fewd!eIW0BgaF1UccBZsA2o40DiI zxBx9a8?BN};6aR6N&}lTDAT;;;O=?>?qylz_8*fpuK|xmuf-M;-ZkJhIS)FU&c6F? zh$0R|qOpDRT3vXGL+DWA#or_d4I8Uhr^9ZJFVew}Q;v&s2`K0IS=N1lu+w)R@yBr!It zZNll_bb?`kPQaB)Y$w3NRO#+2&$zg-XS~#2k1gG!xuwQ1~IVS^`0u)Hmv=aFZOl#B9i5*RvY`Hj!xIbHg&%dG|f6-nm# z-^n~zr`@J4upEKFBt4-U0-a%+#Afdgj-)lYSNg!o1EVf=&6$vdJ}l;lVm!z$;7P(3 zCBC#plp7%mju#J1>c8#Jmw!u=Uuv(J?sNU_vr&>#-q`Mb)EA!lJTTkg)YEwd+tnd8 z;c&NIz_ANr3=)GE9N>H#Ew%dM9YL|5y1r(5&7Gdr%9tD292X)+@}T?WGRtC1;lNr+ zp(&lr8Hbf)Eap*(M>498f{t2g<+srZ>lGd@HYpxivB?XzQEuXM=z2zCen5LZPZ}MM zre3|xOA*+uQa}~4xwoXXvK`wfdzT_0`r&Gs|BnCZSTWDsnoV|g2r6Kw zbfTs0SW6@68|Bc4SBf{={95MmvtvdB^^#~9*gRN#Dl6M!`rs+G_?`s0K({R7>sjRi zlJFqF#{Dr>g+I9Kdax0i>~kR&@l^S7!NVfeg>~dkvRTqsig4gy>^cwFoiXLMQCVIo z{H-9?@`#&XTg`w4w)k%O0AWp1Wl`x7_S}(&o9IE_<2$%23&X<%=R4z`7z>ldYpGA_ zq#~px-2O_q7xs%b{Q!q3{bd<9p=*B4uQFrL;6s23Kj*3Cx+qQp+YgjT}^7jd=#32sZt;Pngd0-W!Qc?czFJt|Ivas({ z6WcUy*}I=U7+YGhgzCrif*2YQ=})IA5wzXPx+D4frt3iY-*1yM;|Sz(kb@a&z5c)z z3Cd*56P^+SLMCKAu)ImJe^4!K;j!eOlHX;U`!3@gKskeFYuP-?L-(66k2D(8R2p8@ zabPabrtlKgC4PB&*J*;q)Wv(oD~NRA?k+LT2@>)*N-_R>K~0u%_ko1}f*m3n5-&!1 zqxdnzyil83hQ61CBg;o^O`TCf9Cgc2yrCv-ugxg4+S)Z(ovhS$NzSDh9H|5n|E9B6 z(eCp&;Vz$Hb2P+Y&x3dNWyQ0pz|1+H*?n&%-PrI=F1f+1g7adp5N*C;T5Pg$LHvz1 z9ou}JCf&t+vYEPk6j_KQukJ!p{EL7uKEM+A4i`PAK$?qQn&u#yn#`*RvIaHaeo1<> zoDb8BHZ+>>Rb-(>a+N$cPtIbcEo7{kGaktW%kl&6!!O4*w`Y*JyX+VuMdKmLK1W`e zJY1zMu3jNxLqEDP{_PrL*#y<@G%UR#+LuO*SFqn>&3+}E``y63x}Kh|$7`$C*-BZ( zg*8F^+~p+~BHd!XOxoyvZ?M*zx@->8U9O9GUbPwtMTwswg=y*J- zKOU&Ao;|@ThA_?dPqv!6NR}xXW9jTX8{OIEhP*uy*2l|%2i1Igk6I4!Or^V+mS=?G*NLo+o7RkyoLX>Uxc0nI)#-SzL6{^ZD=QgsXel$=`O2lt z2A;9m{}Gu)_x31E`NKu^BBKPp)_nHdp>)TmGuBk9sh*DUlNv`mcfR4w2$~+#OMT9W zaL#k?nBC=S9lA);XMXhB{C*h1ozU{DlS9-X^->ZgY_(=ojP!)`jh2L9&jxP}uTGKN z8T|_8V`DBj>(S2sULAH+Su!^Kx!udih+bq~oEOScuWxhC_cJO@BQKp4B)~!T!@zWQ z$4}O&_El~cXtrC1su74nu0h3Ui^o46-H;|_)eSmZ1KF3qdhWO24{>N!U*jhw$?DpF z_6TcuyK-E?hk|By?VmK{8 zV>&qRg=2u@blK}BdXr}6o9pjBb`PtZKgStas zv*f-jPyz=Uza6q*Fsah^DyfUllUKdpUt5u<&7 z)FkB?((jyj3r@WT_Uiv;Io5;L$cC1=2rwAaCFwG_2-WzBSj(vt z|CDG7yyJ*=PiVIuNlNnU9mS^tY1zaRn%03|ydisQBiY|5`dI0*pQ*??Rp#Ca2m4^m zLL70~58%o2?8^H$Kxv-HfSz-UtkX#Ex`9jHX=8(}*z~;kr8{}gJ0qd=TE8m8f*+NH zI=DvA&&uo0NO?cx@7JhW*POHO_s-Wg7#@v=zohZY+sO$%<)S$65rWel^0)b_{cQF_QlB?97Mu zC(Nk~u1qus=i5wTRjziu^RA8T0y{oeCIZF3zfJeb)=Xb#?sbbfZpPY3b+0YF;P6+I zJ(Ik{WIDrey8RQae)#Jfxi4eEmSlTqEeQ*_C6!P@_f9UutK|tCjJ@m4COod$L8|8n zq_^N28jc`(3N~K9OK@}_P7{xFKPTK=YHSM*mBs{rwuz*L)!ZB0Dk^SaHp;>poJX#d z?KJKbHo@;t%hR-X=6nKZaI=JQ#{P_lZ>oFxrNwdU$pX0=Veyp9#vf>Yw<2i{0=kOt ze!EtGjk%ihL)UR01c!NEzVyJv`k-v#w92d%f3XMV?OEO#aMn}e^*g{U(Q)tsaa9*T zmHeSk+9~0T%I!;K)4VcddyU!xGVUZ?th$88amlxP;Gt;q+Y^lDEoQXG5`uJU@j?s` zOjsTew0-gtFsWn+Vr=`E+WsbhwJZI6_9FaCvO$Ad^u8A|5cfs(LDUnD2QZoo!-8n> z`aM7$10m2*(UmT2M$Z`1@lSBX!DB%6_Uy{2%YFUK5VA<@4tRgC2)8g1cVzZCId&>P zPpdB>a7)~0|I>1dKTq)|i04p6C{7kBT-uAWJwnw_bS=qB|+6!H%H%%Y@fDt-ra#%K@(OMAVfafiuAiLe0FNHrrYu- z4WqpzEGKOtaWj3bO^bW`0k>7xkuj$`uVeEczW(i3Tq)`C+pu)A6NPdWV=QUI=2E`v zvxe6bN-H5{baCJ9(ln99dyqaAC3D%Ku^0Td{D->(2i3htu7eZf_2+MGQb_FLDJadA z72~|-c%*?vEbt~(eK7}Hol$~{ZD6JttBgl_eq`UJur(v9n)z1i5bpG=ny&d@SF$k@ zbGiUr2~|FDCB%{d(7e4Y;8?23mo~txGkEZZv;wpvpmx)vM}py}gk6f~rfK%)$tY0+ zmG{7vV7JXcmRxa&29T-Lo!?8_ll#iwYWCCab8Py|~khqpy+M)hj(>MKeJUsDtSlr*ftT8It%Yl^i zdv+fU;iN1*iFWNc60l{Ok}tHr;eY#AJFIICZnElE#^uz|hCEu@Eo?cw)J3HvXb299 z@!WJC3C~sMDFzcfXkIv%{*8E_y%I~i_nKKPhL>Jqf1FF|C?W4Rz&&vf)Gnexl+7Pql8`>bYAp~Flu@am@@=PSn!lj zT@$cuLzb>NftRdZ2Xjq1Y@M}c#r2rDddLg2%_Mioa-9WKF`W9HQyJqgyj0ZzO&N>Xw2Y=zqHW!W78$SCypZAX9qxiqdAY}@ zySKqQQ8o)0)zu6KE1dSjg`@4rQi{^HNY!&}Ks?LR*S4<& zl%VQ^3a!U#f*P&FT*Jf9-Gty@XZ`jed_EFbIr}uU17#o|3Y2`OuslO-oe6Y;!IjCG zc-n7|gu{3-91=GM&(3FJc6^_h4(86S?t91sE$b)golL&1uns6hIXHn!UUS5sacUm; z^5sR}UeG2*IB*~$Mdj2mA3sjfxCcl8(Nw}+Wh!h6)5d_r`{%6QwS31nMo;D@OJi;e zdZLhwW_?b2{&jw>(RlP=YwP$gd~q_w2PJ6Ny{HUoq+N-RPx_d{UH1EO0!A|e9LE$c zl{*oD6Y3P==Aen7yDrsG?5%`7nU(M!4=^f#fw?ZV(4t0SW)PbQ?uI_fB>s;LhF*PU z<_R5h=h~%vkO1g$yXe7!8Ak$1010!LZl^+lQuPZpi%%nbGLT{KIYVlOoP`r}!Mm8) z=MM2H2|o%K@UZD7{u=K+DQ4KG?1S&OU<`M2I`aM^2U6Br=d(+D4fcz#(YV4r&Kr<{ z#2ZGktf$Rik_b8`G0l0E=eE9CDm6xIpwD<~D{6E0qYuKKWyO2>>4Bh?HUb6Ycl5OV(WcM{+xJp86w2cH@Vb9kO=gQam zY6)d^h*TC6LbnI+bAQu;lGyE2K8PqIU6_Jy(=%NXdtYjqE#AbGl9if$6kiY0f_NYcOjI`;YY{RdnRpesxJJ`57q3w;R`=`4w z_r~f*Q6XNN)}at5<4DlH<%d!2JevH_h`!#F6N*?9o7?_AXRNGccYn{Dd&zf>$hqYhg9K)1dg zLYb>_7r(UZam?@}*MEkO7n06L_OqpDud=^&VQ*t~fb1ADekDBaEGcK*->c!cCXNkj{uQc!q^$%!5f^&4@Jjg$twudT zrv;2ZVt6JSG2UuyyoTD~8ejO#Yn(?2i7{`Vf>nM#h9J1Q3%T?;`6jG5w&|J3^D)rZz2+>NHQ4Zje7G2sAZ2J`I^CwrWo(-?hg^fg;cS!|Qrf z_4k>+aBQKCF2%MeBJnlc7!pqvvX|3e6KFJBH6LQzyig9(6Ec?Fm*58V&sv1TAZpDO zo;4k+kd6j8lY=dSHM-Fu^q0Lz+Ppc>h;X$A;tT}-qXAL9X9{JrmNlQ&neA8^eV z8*1lus(FqZnjvTs?GisWgvq!YhJ&QEM|c`b|qN#_Ydo@e{(f1RTsD zCd*#>zQY~y?4$+TT$N!WZUM(XxrB>tYw5ak>$=fs%a#y`(K<~+FH`WV8H@FxSMvd< zLcZ+v%Oi9Hl>9_*uDdc_3#6(Dl2zBwdk<=2oO=#|Q$CFIq7dM)N0U9%($aEV*@z?S zzLdygiSZ9A|FdV$?8n33l|Zt{)dldnh1MTl23m%K zRtT+hnwTyPFF;d1;?v;YOUHWurPPFj6H)8`3MZhubB>6bi@%uJ@WD-HU4nh7v0e$z z(Dz+@-|oj4S%wy(b6>r6!;K$@6mVE$kdIaS%E^*`Uj}U>r6ftk{zb}<@v#2FhK>)+ ztIculsRmm5$sP;r&~ychFP1MYWYW>EBuct$tNN^RzuJ8nW*aD*np!zbIP`s@^Aw%iv+bQk_S&!#I?yEmMAOd}wxdG<)q*2(rlCj%T zfE>QCU()y~`|c5%=m?8j$HM#d7MOnfygn~lsX|zCDjQ*0!BcF=%f+i^L-P`n2GA$2 z579Ec^jyB1or|%yZ$l|sW3GQaWIM#6kq19d86zn}^BFi6t>NsK_gl9k+wQLcc`7U% zC_eTSgEF2M(;P5+4WutfYYER4fZ7Ebo_|rHxA5P;I&vmknG^pQ+owoXxx4RmGGm>b z+_bY_N+dg(O5PVqtzp8#eXAo=i`~Nry|dLlA@S0I+9^j$^bF{{ixDDNjh;TfSPTuO z$WEh^^Jge0n>jqYe$_5p{{jw8F=3lvk59y7E zjYzaf#(FAZ{BMG0M>e^!pjx;2+eSchnaYHpNv4ez@6LOfT-iVZ z{dDmf4SYNbe^J*WUJo(L8v9zyfct=}GClGgu*$%HDU6wqp?ydL^qK+IulT?H0A~{q#|Qs>um|Us zuKs62TEX2xjH8TCd7q58?!&e(msLB8bpNFo_%{+^+_>Oh%kBSs|3bW@#-D4DzYH=B z?LC7>{|9*^ljL`3T=tt{H8&IRT4#n{|Tn%P~Ay@a7{l|nB zFnz6_A6I4AF=`m25uHoVrRtI@cT8-JhW_X80jh&jl1BV@VkDeZRn5nD5OvYj^AXe8aPzc7I+SDDxVqhf$jmIL%4yIkI6;|avEs2osy`#mPdvm}Pbv2If_-_Ia?fLNTvOm1* z^OLJ4QIv>l<8o#*wNB$eS}+f=SQ!hn)LaVKu;HAbl`rCu5LT>F4@wC8f33*blT@`s zxg_fjgK=d;$MuF{k-YN>u+Y}Q4zTq5rbCz8xXCbo+HfL0Lg7c!mNe(ndEVQNko9@1 zkq4w=MTpp^2C#=;O$~Lb@Ea3y`!pY}9Jd$IyxtoJJB00QeWt&~cMT@uTx@Qqi z%G4|kR9e^9WD_U#;_YinO)rOW?0*?_Sh5s_E6DKr z<6isXPo|@pIQhB0N^X9x8~jslj@KzF$=tZeCQ^=6oNMWuqS*eN_;_6U)tR%b+rg;6 zy`VMn!Q=O5r)q@Dd%nN77Bc>KX)Y3=0&h7U2=jqe2t?71l6%ISePMG+xR>p9H4kB2 zTTM;^C8lxQb5vCB{+(A-5bqK3fLeTCiq+$-+CQQ6523)jxKBR8j*S4>PpfuqtjZom;r(O|6 z(3pk0@{ort4D^!uGxJ+!UV*uUIGEA89jB*iOrl zp1En_L{co?%j@S42M!8ZSan5bKj?-|K=nWK2P)9-!UB2@MOXUMSt$K5nfG9990>}N zcQRMjH!h@I%63IhJ(CFmgK+<+GEeoBK>Q)zRO#Lmm371rbZN1U*AyIBMV`i^J}3S< zoPnMp@ZE8T_><8npHqB^1STSQP$Zk#OYUTM_@ow?XqF}j+lenD3H-j_bzt6xTil4#aV^ZF;-FI* zkR1=V*OzzqU|ftR6Z=B@8ukgwnmad1O%fJu2|B|ZS5*DcDJMGcgzaYku_*_ctZRjc z+~=ATNUgpQOCQe#R|2e7 zeu5N10oOT}eq(A7A*Id)B&Y448lTke+y^NZgYnnN^cKEqY^f2Ssnd-g5`1*KVG%D zFaPX2jhR`@T{YD_Hpz0lNX|9xYl|f3Pg+x4CuX34ExMod6oft94(!Zz9cqA0F6cBNA6T!$G6TzJfgGc zT&>xxn#Z3DX~`@Z#$>=jw580^?bK}6O@t?J-t5ASP)e)c4)zeGS*$}kJ$z@?Hd;1Y zpMOxqkl&#%V?JB?%l%lSHQJL|>B06gr6NDNrD1qpt$Vb#v_{l$+G0$IAyx$n{y71> zc?3(yjNVw(m}&`%M#p>EbC?)f4FA6D4Pttf_t-yrov(ORP;M z0vtS!*QOe0fH^JRQ&mpp7c_xX?)qs0F27X+uS5ywEqwj#c{C37@y*$}QDT`bBl8i@ zoWyQu%?EfBJQ>&;CLhv}81L$=t%O>DuUp0QVrDlm30(APqUhZ*!)@;{J8qSE*psXx z_*7zDjkx4~%XJ=o(c*5)97*EgsW$Th;aWZQ%t){Lg;ODW!#4((wj!nY_ECaT+k`D6 zCL$kCOZ?NBMHp>Ad*l^3IaT3i-~4c3_k6ccK!>!2Zt?`-(ft<94ZjTpEA98YLivq# zL4OlWH@05qbj)oCal9;pCb|4RZ9LnSmnpOAPt*Gpn0(p)RFUyWehyd#IVI>VASN41 zXa!;9TtA^tln-##RvVc;QE??-aNekOzaA%ms8N^AindCRaOVBU0MLW@Z- z`_$+rD~mvnc;yFk&BL3J?JOqQ-z`3SD8|K|x}D+-@n*ZXFp#J;iGJb9eGaP5CrIAB zf!^&4&reFMY|ms}ml^ivjnP!vpmWiP?WbT5=!Xo}!fRBhrO3I{?)1;4wrze{3W{5a z6BoaOdDn1WBV-VPSauTK1VWaT$0@&poTyN$ffqSJ*=BExMIN7i>B>s?nC4wV8!n*O z*Hm8=$s=!uYKd3VwGK#)`n?VV#oImN5u_|IAo;>^(LEOBjHa~ratnxVWDLA_l7+$^${=V2Y3}NGi6vV#g2xgTE z%iI|5C5?y&OSH1DXN)|#d(?DvrV}_@%KxaxoM;!jWm=AKzH6m8Y!mlZL82%yb>!h} z1wLbQD(jZMJCn5_hq$7XptXRkBs8s3z7MN1tQ&^6Gtg$$59pbj4)9&oHah$zJ~9cR zU<~8uHLR9yhKU*B2OE8RfQ4f9wA3I@AWbeTX)~O+c(w8!RwrJibUmQ<8w7nc3HqwQ zHW~Ez;XX}`K?-}*ae`OUz>4xcr_ifxT=}_%*B->dSI;h$PJRB1(8{JY@ZqLAT6q7o ziU8HeF0`jR@#6OY+|OG|wKWBCw+LADv_px?keiS=e$WN+r2M0QM3gh+}>$*C$bsebWMq4Wwb8(DXCYZlMUAVY; z6VL^4)56_YONfT(G@Dv8MBqWCZZoBb6gMTm!6ev)B$LQ63oh;CMP)Gzr$h)(VRuIT$fB{BY+lbS?pI|JYnRn|aZ-(M7y8wYQ~8 z=&227|7|W&Roi}vLGfRku{thVTjk)+ZMJubF#q1rPbDzu^m*kqO%U*$&3@F>Bp%14c_g(2s)H6v#EfeRZLqvV zgIa_8cSoa+G|^?})d)sBJc1xlRJMlnlGuqW0rg^IKvW1YwF`)@*|=1@Rh;b_goezN z$?0d;4}1QKe*ctOeg$lTe1zM{5gH)wnERy&#B~dw0xuz2x(>u_ViPibi6 zqeD?mc#0RBU0e!}6x-v+xX#6!gH0!1@mSY%>LXYl&yNOz0tauV=w-@_gZ$zM{qM`D zb0y?;Tx9PT!h)8Kgggf7N(e8BY){ zAb#UBtVCP1a^EtPzV&9Z#{h4V&BGgNf*HX@LEWb~-`{cVC@VxOJjRoI*t0}~dy`Nd z6TjI9yOU#&pC}%yF;joM@A`k}dJDfO-oAYwixwm#WC3ZA2I&QqF6or+TvF*}6_8v& zT3WgUq`O2|0b!*imR?{9=}v#+C!XiNU(fgV7tFOYJG(R2`#g_h3j1_i_;7w$0wkDY zlKp{KM|CdnCgu9F!AcM?pB59?G$@4bB4LGIz;TnjJ@`evKPS0YU;Vn5btYB!(2S;I zGVik(8=-bT1-~Y9K1b?|;^0E;DGbj(PTn$_Ud_eNJFQ)V&e5vBu9w`OkCT1R(>&n$ zZ>)Ap$hCmiyzMMTTxGovkM*g6L9g}*lJ@vVlSM+TvW%bQAiMiBo+@;U$8>J%k4hAK zJJaJjbxit2CmU$`W#Pb+C>!(uN0WqtKCxnVG{}9BNBMmU7(PI;ZGA+aDCdtTwmxPr zP1g**t-!V>U%Wfy5X^rk3ZMhi7snkQjfZOC7EXeK0BI1ffnU}Ju*vnhcqDaWJ{+6@ zpFXh6QDZ!MCVu^SDFbn@j5zj!bM#t-Xp)t?v&OAxfHclzN;_)CJHpGh!PFhEdME)~ z7F0kQr_wh_pT6Iho6f36wO0=ENPs_byhYgp*iMf|)*w&6UB(ne_~$G`fRd1O?H>yD z?dd*A&BcP4L-~8jGg~qjDS_FAvF<5INQxn@++e6R$Fr5&n{Uc?dxj$hxqQ9(QC7Wo z2R}sO7APL{yY1jz&rAeBl7!Fktg{vJz7qYMl;(xlU%Y&B{dC~etZ*|ZBVAPT!JKu} zxblF2IO&&GN`pXCdSk&+*!}I(`H#EMAPJ-%{ScK)u_!Zqi zUD@$9_xsaJK$GsIo%=ky+4t^@|HdM9ZCCI;*!6(jr?b9&FZ4-iJTkp<_H6M-(MYez z9}iCB;CscqO)E)cMO(ySNy0)#V1`RGzh!TN%;mEo*=pv+;%XS>tnJ$2snE*)EU6zw zhXGKqWjwTPBBJ|p{oHlSW`@1G z9oOp)`d#g+L|nd)6$u&TTy~zEn`iUtTOU^-3K#n&;cVPL&4KAPdz?~gKVw+a%fJb* z($K7}`~htrrS+N%Gt=T?`B?-+vh>cvCJP=PWxDGMhrdCgVS_VPmy-=&KTm%~7?VUVze7o(6mgz>%b`SfLd(0hl=SyUHDf@;^cqDC zjXG)5t(Ck)TJiBfo_A=bhiJ*AA8Ge=bSzq_B z>e=(}CH)Y6j@f?gKX@Z?7Gbrp?QYu}(Wj#L0-l;K5&qN|l~FlEWs=xSZ5L&y|1BbG z{kpz4fsVL#^DHwwnT2^QPZPC>ICo!0#Hu<|X?IGSW6DH~X*)JGI_a9Lt*s)Y)nCbl zVn6%3-+ME0EyO?qXXKZ91dB&(m2oIb)Rwu!6JM~M+1Y8Rro8A-^JU>c!+G2v*MVMM z5kQ-_{l9yQ3Ao+wY(YF)XzlX4`B6u0{HoJwQhtP8Mys0xF&j<&tOn843G$n8osMPY zVYjAEFr(IFA@1eHfr<3B@~=f1OBsleNzXdUc9ef?N1%stiZMO)u~hvt7AJVs6whce zPFXXY8FW`(zMW+L#pSDWKu$9bNs^_tt=9h3s)2s2%ZuY$Whf_?^~!qHcqg@WrZ7P0TGE7nUS^;k zVYD*ty*0a77Sw_|(&(cJY;t=Y8z5$#U;3>?`4_oqTPN*6%a82bbrg4F+0N$=9!OWU zY|&S5Np*if*|&4sWk)~no}60;^YAU zUG(j`O9ad1@6y`nFwoR+n;w9CuxIXm$>2J-j;0~OCPUC{R6FN1QkFw`f2?kxzNfJw zB^9byd~=tc%s9W>R)_b9Uf2x=(7QfBB)%45!X-^6dw*xKc!0juJBUu)+Y=d?a$mk8 zRJEM-C&};^oo`>rEa%5tYEQOLMlNY%T~MAu| zQ}2*#xuR^wn80?sIL|st!|9n<4GSJWW$`C7!?_UMwms}xVY^dkBG|T}&r*0){Viej zvvry7ye5tJ0b4Q0(x%5b+I-vgpefVOT#Wp;gU6Q6gG?Q)kBPO9t^m4{-+}yWPi`_9 zlTZ<*uA$*+fbo#Sl5i{79`DtqIIsE*C+n$W_*8$Q!(B4LA7cipE^ny9{U5B382`pn z@J1Q!R@;7Awi}9Qz;F2L2BjGCkf1j?p7lGh3oQv23~xk)IzGaulV1F}cDdhx@@N>W z8-lZz@7R`Qn9lr~d86l}quY;nEWf`p7^b4dn=MOj%QaF;!KacHqbJ#X>Ew%N5y=Vw z3q*SV1{QoO_PXB(4%n`oWCcs!#n8Q_t|-xa_tAK&wQ}YI=&zg9R!#{wvm|H&001n# zYnA*|gB%P#n&7;9+buw<&wuH0Rh+!ry>R@)m=;;xIJf-}`c1I!^~*fGeWwL_ z^D$5Ja4-Kv?wuFIv>mgNxbQwdHjPzA5ttrUqcL3;PM|Dt!`8E63hjaB8(b86qYF~5{Ow~iGr zSF)BEy4K{2L2J&$P8%>M*T6#Ex6@o2A^Re^jl9_=?>Eil-9W4Lv^*9$W|A%^$-8t*m#nl!OiJasX(-F?SeP!t|4U4oL{l zY^v@Ag9L#nhs?XG^zxro#V+oHqaKh`fuU4t4*;8wMdU~szqH4)g`Jn4^h&E<->b&_ zhAo@M#SaH>GCty?kwe2|hT|%Y{KVcMZ3Cg0$&ZYLFAu5_FJ5|MHm;APcE_qO6w7z# zQe0L9+XX`>HYqgjd!q;T%)vi9b1#I^lfrwwSNbSO-Kl@ePr+{-da|#3=7=RjjOF{q z(8=rrTM>mmXRQ*O#+jn0hW;NEv@(=ZNY5^%CruH>Hxx%28mJux zLaia`lf>C)Ii|GH?Db_FKa3Z?pkS^>Cq}lhlDzG4Pqj&8)&pWX6n7725-T!_wxHPo z89!R`ceWF7lmX9f1~J=9zlaxe@CWFxpdRqFCStzCLlc96O62v`n{exz<;RuMiY z>%Yp9^=g2pDYEd#kCxg#O(7_+J&6;=*jgKc&B7CBW zsJX;(Dc(QXT6s;ceq1rKNtNkqd&T}!&7)2+dX<3Aw@UKt$9ei6rF{nY{OKRD`@ac= zvieTXgJZ{|*Mf8YZl$s_^A;zz-)xpRH?vyL(OaK}fWL;THBfAy*_C!vIf2;O-=O-n zzd-fSYJ!cwD6Riy74{UtEL5Bi;xviFvk5vT_R;$F28^-X;&0Qtzg)T)zS6@@^1e(b zlj8M)KH%D*82cPF^9NJE3sCAE|Dn{&|Dn_$Y8+PJv|kA|p=yzhY9sG&>As!1IK;$| zLUUIt3vSWQbNyT7uZkZ%>NK6RKE{2}%>j8RupG_^HVQPFM^gIqh0PLysq;ni3K zBNpmLAOH;Oyq#oT{ObH-Z!7q3rM-9`15_CJL6Jro)1|g8`~o8KlM4t@YyF3}fcJ`C zx9TB1t-iX$7rGc*Lp$97P$-R`Ww=@vQ$Sj0qv#)B@^7p^lxdkwoy%bQ z(@!zKu`K^R(5Uh)mO9{K>DAmD@obor%Y(ZGu(Ev(dRg`bp!U-0JLks9=&nm>{#KWg36* z2!q%?bV61A;U`d^N!qv9uL0MVEFv9%PeHx?l28Q(czx9Y)*KXtKS2919}Pk=5kF;i z&*P^f%N>=`h>f#+Tm|h?Q2*vC$D+i&o0cb{?YVIL=;d?ZWMwyddj99=oQUQw96Df) z9Wlx7aUzPzqNa;iivOoM>Iuul6VZ)^0Vv(E+}C0Up1@W3Q!__4UI9aMVJ_)H7?)Ks z=||30mFuGq-|j)A_v>M>9N>E3K9S+y91(241Bv48f3RUM55{VA|t0Tjkp zG2;i`JGijQFbmrsS3A9idTXQ8bw7o| zi6>Tp$+j7v=d1W^wa<3`Tcx<5y7Q}LvoY3EN{p=r1yuA8ft#rEukS7ZXSB~PXn(ffPO zHO2EKi}&w=Yr0#N?LtzYl#=tb(l;U}w<-&lljZ3Glh&?^zW7i#va*M2!NUki)^C>v zw0R(%FIu8I;QUB{Qcak&bG`e_t?@EAXq?`O{fYZOuogh)U9Q(kBJppYIo|$9-)x}LpoN^mKqbZ`Wqs9+p&g18O5;U)#2y!aVY(sf+3_16 z&>0@-5^We}s_B!B-HZAQhqQ}6H&20?_}&PYcVwNNAxGE8ku7s8z%HJcfXxsN&ui`b z_q?T&e1jexmNoOb_G>0qaWk2ZL(QvfXdrcaJ3NuyPx!7Sd^tFc09rW$oQ8+{GHaoI z{r>-BywCk-ykGjiU~WGqIB%86yi*SiuHfbOBho z?t_FvbMmYS-zwDqA$9*5X}6my+PV5qJrz);dA}-P?Ly4@_ zD2HGu)JxtX>mI5B0cG3JT;&%+=T*&JNxrWPGh8VD%(8F)HOqdONR+|B0sK`>LE4m%`wxY{(AE4H7b2@XpeLD-HWDqA3sM6sGAO ziefJ7RwVm-Ss29_xKzkTqv8UVME5r5sD&3SGcx0g|3!75cNV?vVijb56HP@XG2q2B z{Z|crL-nc0D%^t5+7d~LkT!j$&)*zT7Oe9tH6#o?ys4*jU|dEJCvfS1Zk*8Z7A5(Bw-H zfm+iL6ojrenz#$j_x1u0!sQR*UeBn~M<6gk%=Z~_R*D(8j3cIazCW*1i7 zTTS)7#s0+7=A|Vo)_7F^=LD$#eJ}xWJ5G=PIhdx1T*E;rjF@`xY3|(n1@`1=zl&lf zwB9fYQW!@z<^ckq0y-Y_*O8w7=pj6-{+3jY#%E0C5#xFkH4Gd`j1t0{k^L4c5$kMUF*hv=>Q7cWSp3I!;lAi?6wFPtH@$JmdIatRjx9HPwSy zXY)m!a5U}@l8q>%z2kfs=faDSKt=*(@`s9)10U|gB|s*2YSx}gXd23= z_}J2;deuq8q=V}tCUy{IVW&r)iinEW&2vtL$b5@ z*D1}q*u~yaEh96wrd18y-GGytj*qR-6(7UH<|%YX`kRCiU2e~@Cg0&Tsw5MvxA=4BttMUr{I zc6obYWnnviX#*wd(nyo_8|xF~WWIO)vE9@Ox^q_Z@Fm)FFnz<}LUeg~pA-UgIaq!E zg&-@lho+3hX6SczE*POYHRmidE#n?2C_`21SI|Hbw<~3DZY>PQiz}HX-tTVAtw8Y9 zw5@qgp*~*ZwN4yGa)ZNhSt@BPo*@vvyl^UAM5N!M!Z8@Hu;|(6h{*tl1zNr$G{%I4 zqqo zJJa6J!PjCd3l~M)bsi#MkNzJ&I?us@2fV%MV~;ZDp8B>`Vskko$rClE*7mIjGZ@0j z*%BP(7#;?MbXQ1X3j7<3whedokraf9%wNbzWSR@H+oY>v{6&gf8fa)^Mg3j5+xTq< z9}P%qX9mD?Boo^mD*7;VOlRL~fsObcP$A)uvvoiO@|_2y?vsa*yqcxo1;=huQ1!c2q23EQs^Hc=Zoj0dlOp+nv~v z*?mMxnEo^*ixYRk0}3T~l;gnUPI3q~WIUA+1ZNyp^Lr)+1HM<>uT+$U&@I3AtrJ-a zS#9ZJKF2Z|xszGk5u0n9X#?n?F+Epcz8<0(s-=6jnjF4Ff$ilRg-u^v`7V7|siL?m z+D>W{-){q7mNGtwfYsbH{y_>+S_q>u7EwXtTaq&=Q|nW130U_A4^D!{!0y8IT{7{A zV+u+Qcl1@e61Dki&1D_iXNyJ{6kwl9sdpQY?Dkku% zqkp~JpE+_g|A3p~9j~$FcJ$Niezux%6A1#W`S#~1b2`?hs&92?$m-7zO1F)OB%)P6 z3A6*c?Y*Y6eQUQK=kprD9?<)%H@FeSs<8dC29(40o(L#j^ zZF;6RN?mXTekM9D(mz+OM3q7;gev&Z+rTa3CVl#7p=We{`%u+u6ZN8?n&OM6Z>3o3 zJ;MWD*4lQ(k!xA&D6v|!!1la#)7RtYXNm|q`pO(@W~kcMidWOY*SzYRF87{CByj=H ziu6xcMs5?97jb*b!OUPqA$zn{@e@UkAc0Msq0B3%+UOvRG)`6s!Ep~vGPRx^@76^L zeMeb}5v{-~4MCkV8iQ7$uI{5&`{CJ-F#M^!-5_EXxVQPe8Quz_GMTndBvMuY2E>dZ z)^m*r1S-j+u)*=$MT1%ug0%2vN-#l3K4BuyZFgHKF zG4|B-*v7pT?c8w?$vyq+uniu`G|DRsQY6{Nj{_%Rdg^-H(>XqD`k# z1=ih(3>@2hyv?j7T2JBNC1fqb-I>NfTgAp5H3womDjI}zbL z4g#5SNh~(@LtlLrI`y-AA4Lr4&%Fddh3;p=iT#_Y<~T*aBzecKB^+~z2A!7JSRYt% zK4qt=U)wp`tt8mz{f&hOl@%s5Hj&W5Hiz^}ej!VHko=YIy{*2G0Rm!Nf2FsXp=P$Q z&o{_#ILP1`bqetdswny_axP-rK7JaIKrKSXk)vdK-&^8N#cB=+t*pb#<=6D01rj9A zFw)kOaC#S>xEZ!ue>c-H%G)lc!%>}NMAC#`-0B{QO8)6hI>FOgJ0Kcaoq(cxqRbhg zgBT6F-0ywFc~@}9h$_;Kfx^lC?c8;i#{iGApsV!wzU-K^JM>re5-Q+5!t-SW8|OZ9 zB+#!zY;?SEdrjPqEW%J3h&!u3t>;gVDy3Oj@;DsaQ78>V@DMv72e~{A(7nQ&nt0mC0`AzO0&Ipkw=X$r_VAr1xZ*?{T-7wDYhBo|$k1_TF5sftXnxXef>vlpJrDx|t7Kv?rM zA^h9R8jt&Yi@8Gizfn`*k9Fnt55tY$j`0o&SArQ&UT(_{oNxE8n`E?k^zYo^dh<0? z`%9B+v!5G!_oI`Tcwt7F_3{}_$2`pVr_LbNhRgFl9ToLXmNgl#kh(;H=2@Aq<$3e; zv=s-P1T;~s=7If`dFq0+IL0Q8OI45Ty&AE#2V;11^)t?Zgj;H79IMpMGcBTJUl&>r zaBbq2V)Zw)g`sZ8s`NS6pg>U88&4~M)13DVp;OTMa;1a#;2n+CkE$T`d01saFINnd z_%Np<`Q=L`V}lSR@udE*bHubayOsB}E!TlS7s#E-PT`!g_ff5<*`#B_gC2Yq^1j(| z*A*{S*wQl!ze_{5beohW&ena?Ii2u;$wZK+EGp^!y$wD5+!e9H0FhGy$GTTM!ul=c z>#c0+N>={Muk52Ty{&o;5CTL2W|3d4deg%821%Lw`0`8@ojXg-i)z|mKHta?HvEiW zi8Fh^#wT}o4gKPzo(3(4No$;kcuxzc4K_t{C_d(%+si!ROxNu|ilnOU+2wQe%X1$& z{0$_CBJKU4bP3CL5_#b*MgL2@m3E%Cc&TJ9OigNS?fc>TfcS)B881s+gM}FDrhB*p z1)fvUKxOY|cnX3QJr_ycOz1?SrNt9+{moeZDQ|FTD@H>5efX9W7sxd$S})!nuXISod`ifYjP{gGfK!6X-Ujco$?Jp4738dzk0B zZ5;=TwrxOO8MNXsd)bJysA&6`>3;kyCblfF?3tMK%72!(q}$4{HXty$)j6J%IH4-~ zRent0WA@)zE2B4aoe&LS5yIih%#n1GLAbVorHRL@E8kr!1PGl-X_T<+K8BpxJVx?1 zC9CF-bcaW*Vj?*yo_*tgucqR%g6VYHJuIvXFQ$v^m{DaF*0dBZJpcKGTgZWzww`cJ zR6Zkt53Xu7jhMSw4|EM|VhSPRUG}sXoTOG@vMR`BVJ9)B)7mL~ZcnI9cSgCI4kjkQ)xFwo@85?QZHM_aFCfM|TTAnHs?pho3YI2a zS0($_Fe&4(u@T|J-Y6qmycIYupefthhN{dMq26HHx73YI6e!(db*6cJ+*tWjXGH^O z5xZ3iQXfVYlXi5P2L?`?B!vS^j2g}btY#nEpe{`w_D1aqSq_j(iw~E|S<$C{xtCL% zrPtbjMWha&qX~OK`ZWZT6aey}rEQ$L{K1y!Y2ypcbfdR3;YGywRo%silqt5;nSl%^ z%lkvVXb+~Mx+%e3O_}_cB{(o~R=Ui=^0T%|>N`N9_O@K*X6DKE^L!}$u9^!WDHoNy z-U<5VwPeGKfY^q<6Wj=kzBXrt z4kIA7N#192UW-L{lRuh0dZ|568dl9(;M(X*Fp z^+}7DaK}K<)}~2lg8KLN4z=bzYC&`;n|>d6{rqm@x_PYMNKnS-`HQ4NkOblMNBJTF zewnY;>3U~q`F zs4jz38#kDjn@!lM>0?C;Q~YqYFH5~?*aa{RNNdI1il%S88YT;pOM^JZo;RJ#pI0Xyhsz2HIfz0Jqu6z~Z zK6&~Jtud|#gA%b2k=>cw;l*Sb4E>FC26skPkIJN0#hHk3Ar?Ip;_!HTS+-s1LKRd3 zY_G#1BmZ)aZ2`U8S2oz*9Y&eRToCiBOD{lgGAOWk!LX4-aNR?J$|9e`d^J^_e}F$p zIe`QC*+v~>^dZ8AEbBDepO~oCrRJY^=sHazq4pA)i8yB_BSl?T-|9!=)i(i429p}@(IbEm1QM)x9I{xl7G zm8Hvc9MD|y@?f0bt{P4T`m{>yuE7eLe?0(oPM-`IXXU|;nYO{mF zpET10(O<7LBICejm>CgyF-cz;dz5xzcE3;bqGLV)&K1no^pR4Qj;k@Fvk_wc`HFIr zrfG3@$1cE+ji-&dlo4NK=3FY^aOQ}j=SSjM6Y$cZ!@6(>faF9ZTz!(~VT>^lfkt7} zTn$eOe8D*-9tSTyI{W7?RL`uuA~!@E)X`jTEai%CaVsXayTgWb&T2=YiWBJQGif_2 z!U97T7guk1(w?W0Mi3p&w@wmO){$7Wiw1<^3#iXFGwOD>7^Z!09jSJuwMo}3h;2Ho zpw9Qrm{jhWD85V3&FgjOF1wgeet*o$_`@6gPLgd&FSkuX>rEhpVZ#~imaY>@^niJ< z5Z_z$T$a~zquE_d`0|q{OS5c26klp+i=3J45@ptFrjV&@aAF|J~T)q*muKhd->$bzyOT*l#Jui$$G@eu!j0 z@TJ!vQaojFTvBH|m%HIx2su$?br_dD*vlIWjPL9ZZ|5q?DDueQH9pqR8%!0cy@^!< z#8oRicRMdb>O+p7x2}vBn;5qjZHg^%eIR5+!oKC8SNqp}pZ|w!qK;|8n zqnl3-1qHhd8m+9q1dlzN+59!1mg>Cm`Gy^}GV>lZXOqhl@9+O^HO}2BH5==vpjPyV zvbMivP~=(dL~cV81|%`-OH`wv%^_w)(s7*7DN~W#Mk~q%|D9IZ@b}pl6KOT!0i_8iJr`8X-Ei7~ zyub5cR|eGLH&fI*Q)Jfz%MqR=w_0Kf)5gX!44w%Dd%@Ia@}#41`)WU@#okvTBg?^^ zIN5*Eo_#TM_593-`C5<_P_o_iP*>^dkT(|X_@)htw%v+O%~cY#sG;_F0dMJJzhd+> zkXDsy`Cy}J)2IB(J1A{4K-vIB-(w4313j0~n~MrON2BEwi9kP+s1!V(lhEfREYQxHolKKUBFgp3Wh>Gv#9|8we$m(-F^QQ43Xqdx+0P! zyHS?V4k_gExM(S-qMV@g9LP9UOvH2Wv637?OC4r=L0^Ma335=KL7BU@vEyYAby+*& zX}Zj?c{?gk(OW6|#~o08`akY~gbMZ@FKClcMzm=bNxHL!EnQYPs7BEv!H(Ej6c{W?Aba!;`SZV0l3%?xHa#u$-|3?k znWH*TQPcDb7WVYgAb#oq-CdeJb7KWew%S}}1~>F$fI48gC+t7e0lR;j1ES2oMA;zd z{+I)*F;RyLF)BeC4*Ai+W1U?*YMkv!H42%QkL2XVE`9zs2UrU8st%3Kt_@!~mt=2^ zK~?B}9^%wKk!8a(*WJF@KmKV42qJI1h!m}Pg9QG#1K7gjVuRB4j#RR|OFv|1g_9*n zz!Uqa<(v2cUjjl(+FTqcPgUiDwzO5ZXFd<20hc#c@=!sbM zp*g^G)Zo#tdBX%4qcm?nKghZ~zwxK1a9F8*)E^Lu+{L`Sq&GpmNb(`go>%p-fj__^ zUkSQ``i*oHRs+u$+O|Bw)HBdoTje@TEM4Hua5LsBJh>ZyihI+SuJnz2Rjpw1$HvC~ z1O5F^J6hir$_cAuW0#SbC4nv6N$m7WMR?v^Jfeob>9c0~jdjmuG6$?3a?FJ4`G!a~ zdBiIDgjjQL8mMOHOsd{xX^xL3l&k=!4Q5HF~|Y>kQ*93dQ$qXY>)WLD=v`-O>scAA5a|z z{ayp9Sq;H{vQKgLDE)q2uwRq9G2AdyYY436NiEIP&9`(d)v5Z+sSOZ5ijZV0Gw;_B zMrAK`<=}GIdsLOq)SUGw4k4JU8)_DkFVj22ukC^Dh}lbMIoD`6h%dA&PF`MLN?&$Q zE6A5dSP6p^8=%^6rcrtp2~%0TG4BXPHJ?($eNS9cLD*Eohg_Nx=J!GLm38fkqwN}_ zON1HipZ33)5Nip5y|oSpVt7cO+UOSLxv(v>MxfANc7f3QG4 zt^-5{nD(DxDatP>R8{5LU^(Ij9C%cvdPsku+VSWUu$$y=f6<{9hrkE6`WeJP9@C3y z977pQkI}au_G@I5oImwOjR~K(KQXRYbBRxJs01%=CS}MK{}tBPI0B zA7@!|a>>`WJKgt`Dl;LexiZA2DOaXkWhmkJlw%VkKp-Xx%JFP zVNBzD-_9t_fUXXdn_pR8#1*K}hjl=<@+!mzFI#;!_bbV_?I{ z<@(%diL0cuL>#QCzxBgw(Tox)Tr7_%bbH+6b#NFC0mugWMv)N9Xn{Soe`cODvEy@~ zA)Dj+k@Th*NV@`X(%jp{HeGXVmf>no_~@QzJk=q3J%3S4XAMb_3^aVT^mcUFY~yi$ zi`M|-2`zEcP&2fCdgN_d2o`YTDXpQt zn{KS~3MxUeshw46rnt8X$8*>`=e>{DjCF)fvJ%(hx&67dPN=>8F|`KR4j_D4|6|}j z-!E}npMA5iO6T{>@kQNKaIB~=;uYCnb@^HZD=yOj6CwvJjWnvMCyTrvy(HFhd9@`A z#8<{eLRTA6D;_p6DsEV(^i?1&=RDJ&c3iCdq3~vyrPs=X8$*6FH^Ck|q1f@%Ak<=fS;;2Ud7p;FG^7wDG zeiYc5M7BQ{OImOCwEO+FZxSBKwvwvIiV>_PYOQDr~t;ylKsI{AP9IOv$F=mTCY<2O1Z>=_`PV2vdPRlo#I3d5X zcfptxYhkWxffD+h(30rx+1;gyWM<9<_}R?0U-GuEKoP0^F{N(HfFn@nbYi(Y?3 zJ%i8w$&WQbDbXDx1i8QqTxDIc&USB1YZ*t;24C_V0ca_XB#!#qQDhemRwua=vuA)I z<^!~2q>kW%cAi(KCysU5qc@x^jr_K7y=4$_wvM93(dpbKQaXS~U{%|?Eb^o~Nei^=s<=E}{pd26hrD^Lot3$$mx3!bVqYnO1DA4Dp6Z79ip2y)) zK4gfn;<~K6%Brddo2J)dqomZDT9(_zutHt;>weUKqcbl(a7vka6;BHwDd*6e?`BR$;mDh`1Yxo#ShFve$J%jns>a7Zs}x>=*|Ua_}K6KDQROie*p(kA&KA4l5?SGD%?kAOFNaQWBYv; z2RVeXYu4`MAGn+9dGDA0SJmpym;Pz&r`s7G zx|*(a$k5^V&vFbz)7HAyJ@TlQ{v;jxyI<>}`a(Bv@IH6Pk+924)|POQi~d=URHmN3 zU8Z0{KZWN9sp+qOdicHBx)`)hpZ#wvwSF4BWGq*1caHfG*0M7`#%dR{J-b+hxF{* zboXIZW}^X%OM6m)vG9e5*+LqNRRx8U5TX~=9=={@w9zsowODv;f#2%tFEOm zWO2xoUo$Tyu%8_zDKYk7Zr4zipP@HFGnh)goNoE>H6nHG)Wp|BU@)4DaJ;Q% zqr)7aLfVZH1w}%rPaczXj*>%%xPjEx`qLQ~oT3h( zTNf=SwBdypu%hioD(?VgOE#Ve{)g%0_%n?@fzD(AQ}w>TTtm$qAyi6BTPHJ2@WV^J z{hg#i{U4WYS8>`D*RA4#mMWf2Fk|Lw@^$)L;*Wb~|h`Alf zq-z?hZU=Z zM0)UuankTuo7`m1oSOOZ54>I;jt%s%G4q`fwBDrI?~j)9D>RASY zgNh~FwhSnC12VAvs61Z{@KY$R&>{S9k)Y#*UUH5}6Dvybw67=|QCrJ#Jzr}Mn1JmX zb3j`?{l%+24u-FEggbF|_b%*A%=`R0+JwBhQ9li8Uy{@WzH^En2_*|fYxT2=AxrkCwQG#>&O};uf2W$M_$G-BX~)rDyhTTdbmXa~E&JDIQtI zacO;6U^;>{?jnWy;|C!Us#C)%pYQ|1=`chbeH;l~E#E`)>fG798vR|*mdDU-VH%n@ zY+k`!c6Y0n)U0hiOgn;F%;8Yg(UYd?@#>WYTdDyYJdRgUb9Am%HQq%5YQ9XJGhd|U zcLd=s-}oZMZm9z0)SIkh2UOH+ef_c$8tesDYgRRnrloK*b(y!sl9y)~oxwPecSc}* zKd=w^t)E&ZqEJ)Hw-8KJnq*7ebdgsW!{~#_k!;O6n;7X&WS#xG3e9u*E|b~DfxZed zl`W`9?V+|wMa6Yuo}qepOLiKjPpOSWm1J}8CgD5pcF(GcNkkzjpY}iJZ&!Q6RsK@3 zdAW7Z%MKn%1-Fm^1CwlHz{4h?=>1{O!c*Pg>vMV(LcDC~Rh$Oz$Uz^Sa*_2#AUcJX zzUK>2^B)3y8h4*_tCSnWb`SlMWnfrO)yo84AA4_FYNeN;3wda^9LH@14K5oszm{s* zL5-_T9F4U+Ces3)3)Hm`^{h#Y!`6j9j~t~l0zFIP7wgy1M^#snS4@d+hyKPY_L8>6 zk#Y!7P_lkw6VHoulEZcbr`nHRZCSn*SiGGyqa6sXd+Ocf?V;v-fjzN`zgE?~5SQ_q z>OhF{;WTQfH_k}l^$z%2DJ@t6-UAYfrslzasJeX4f!VOjc%|WQGY@Ha8&0S7UO|rU z;4NcHRVVB8N?c##Ka~b>7ftpDAVu3HaB03}k2^U6&Ta`OI0%25;%K1N_xr&_US8D!pnSjoCuL4=b%*NDs*^?M2rP3i3zkX)faSS##S+Rw~YgF)i zzu{co{WT*wjH7)e+k}XQMYGbj@eOdi>&3@Ev_0hvZ+wVRGhE3it{>U3xk#lKgxk^` zpA(}XrYC5x97qdld)B595BXS?vEg2vJcZ%JNvkR4=PQlWv34F~UK?Zzu^Ll68x?v7 zPtn+E8sTc{A(4hj+3!!3fWLbs&t6Wjxq`iw7`=>R2(N*DmnJAJ1#I$=|TD7o5W#RGQ9mh^2TX{Zf`SazSZCIYdrTg3lgI3IOR{I zyWjUDX-0;V>1{p!E$7b3A*UOApWF60Ys8P>?iL7XFLJ|A4%}Gi>ZukYDIwRf7erF7 z!s3~<29EC(tiSS@D%z|9y@Sd^y7wFJO94Ni^ysC7f>SgPa3{f%7xZPBlf|j~&N?t# zpUha3i+rpv`oxfZTCWD#qVVthI9)4k*F?)pD)?^k93RPB!z$Bqd@ouRfvkX%>A zojRQHjs+kA+b*r*xjj@gIXzGyzW=&HC7VbmAyUMrzxQadM~u5RXOrrz>ZqvjhRA2k zCw|IpsU@4&pOB5Zrv>i@9ZkX+>lm=Ep?QqwAG^uQMaHklAX26j-+J$0g6(9|R$AsL z$MDD=HbMMi2ww5E;MH>IWkGl&`Tn8Fe6zB3A13ksu;f=j3tP_TG>F~i>1=SFQGZrD z(Azhd;3!*nWDKf)AD_H{n|V`3d9i88#67sZ6mCoF_AWHRL8e zw3?bRz?MDP7MAJ!R@Bes!sN$V8;5++buX81XK0wifak!9au4e2V~WUeaa=Sy);aYP zkn}q1G~2>4evfB`D&QGl7h6@$%fJkOGTG}+BuLQu@+~u$rVw~8mOlPeu?-Pac%Up6 zrp9dA;9+S`*>!~zV#=3fT;Qv^ah?=-@I)aD7s2J<4;K0X+B*rNNl&b0G6^-2;O`Epq_Q-TivWz!eJH!qvxETsU*0jyMgm(IwVsKj8m~LjgA*1}?rrX4b3oqW=c`iz`Wt0W{sOip7ZusvIpR*AzO?MJfyE?CUNM#` zxA(S0EZ)2#dO&Q*-?uuGaV~0Z`o^Q2A&#)xb}-{9d{Gp=9VvV+g+Fnb{~Id-BG5-= z`Ss%mR6?8|9mUp7i5RJ)w`G4eS3;Y|M?u!9o~E!(opkug-d8z1A-c+d$A{i*UmgKZ zt&lJ5HYv}dhH}kR-8NB}lMxoKmk)Q$W2HMPx**#ka+v5m11i0Kb?mcpv`$bTF;kLm z5aF4tAb;tiT4>L7qE2zL%0~$={kcb6epK=0>Y_lU#d$1<<_Ne)ym+`f7Px95g^z+i zvd5H?)yXl4s4W$bqZ zip8X4yrh|v`Wy=yj9hmyk&Dr7i;xZgB49h%0TeZchmVz zgbqw)_alsO@%yd_k?AK)JYx@_6Uy1~y?Y4jsDtWAH%9RzKCmRR@KKDYR|b8=oi;5` zH4czGVxV8Lj}_hTVh?qYmTkoGGBvbZjm=R^$iJ5IVSjpBq?c-+L&RY-JHYq#xiUIB zp>XjT-Hp1`1&-9IS3L@HuwDC%xu(ma-|Liu5lkB!t$X*ua((XRWkePUYBjXFlnrFi z$-S3!?H1h<;GA>Z=8IlO1vbrmz5S*ldiAq>`Od_Cxu_zI3TLg9IVX>bj(1F)&ioRU zC2F^k$}++>4*b;*Hsf_OLSha?Jrg&LMId!E@yIaIS_q=d%Sq5d>lQh?=I-kS6Zo}^ zS#4`?BftzlnX0~o5~(-OXokOW%xm?nA9L*__Rp-f@5RH0)Q%*g=;0fO8>C+oNt5<47rYt3(XPhFeK z-s{ZIG|W2_HPVLx?d;F9%~&xt_a3e#ex3u+A(YN%iLg!MPpNS0L>j_5+pmHA68DWD zg%#`7gZ)Jnl;N`oiRgTD%VtDdzcgCGaBrZ8e)W;A?&@}+(4}($3jG$IG68?=MWs;Q zcn&}<=|9i73M^+^5Nf)FvJBeJX?4TQQ7@Zo_<}7m-`!BNlruMEqVpNrg zmD(eAf}-{g(kH#|`}6&Ne$RM4f5F9-bzbLrypMw}SBJRT3jy1y8phVOExvM}@8@yx z54Ge+8ea;^(xK@3^3)f7O|S};amiZIq0K6j4%#j0u&rv2VF(KFsZN&PY)T2xnR(+% z(bX(E$7T=b@dCZjU557JLM;6Ay^Xm`#$uGP)BzQ3i!rDl!5*LyTpa`vl3Nfof3N1E z$tb_sRaTVcG-8(k4s(^{Uc!{@3)|8WD=-73O?sKfZh-lSUj#-H^fP{D6?f8{=5n$6 zWT-hnFe&q{w*AP_Ok%vYO;_G4YWl6*m@Ozkv)Bf?XP3n%URF%NL;x7)XYO67KOM%D zOr1!b?c*TbI6`{^o+Rfdrf%Zc%V!OhLGXo<2`)QJuNUXs9#y{)h__S z`^9U{H$E~NeYd6Oq-wP&TKLl5_)~VbX-b`@780K~clS*7^#Iy{wgX)a-BG1$bGzqM zx%=X|{rp&WJg_TGW$*>xM0@yD-uPxR;if3>l1jP(XR(rw;Y{3m9FV;02s|lfqNCoeny;+X@jYlS3 z3C%?XX1-qXj}hY^;UDTNdt4+oxC5G!A>KU<@R_*~q>qNNY{d)GDE?Iw{dO^?&rqZ) zQT5bJ8#gu4I_T=Pn_JV7y!Ix0R%h(hvG>ZPl-%KOydoD-nI0F*HRj;DYMnB%zN1;K zjvwHvPWO{aaZgqDkIHXMcLE*INcrA0zDeXaI%SR7K_+hn% zO?i`|?%dwf!p`v)drP*<6(_33`R&%XY0Z8}l0;lBTFT6r z9O^n5{u19A47`-W2PGSyi3_3yC5mt9Dc~MluqX21bvQ@=J$ANjZYu0f590CFVOc-J zs1Sqr^fTgoBSuY`89g^8C`31RI`C8WAijK=L%KEZ1};-k(mEx9rDz!4OeE+%)p9R` zTB>X%Xli{k)qAO0<47~qkcpkz_1yKbD2)ru^KC)247}KZF{!aI>ko!8z3VIHt2++R zaDzZcHjLJXb0}dDCAs(UK>&Tjncm7ynS=fEfDN-`DQS1Tm=P`kqtqOCz_PQ%{h^vq zJ%OPq8$!0|k(=!Cq5ztiN#9SNsI=h8Lz{0E_sWK?@D24?%^NQ?)Li%1WYlr+(U;vL zRIaiza{?*$k{t-BnCkFCKlaQmzj%HK=#;sro<3y)weH67n#yzWQ#?gHa6Ne27563Y z^XsUxHUV@R$5lB;XKv=L{oZRt$!8;7bL530#hj~T@a+&m1T?C`4!MVhl??Q^HoU(H z)xMm{i)ij`Oxrb9xNk-5(KvfOhKwiKVJL68esvM&;Lh#bw3EP-A@C}N7H1;U+GzR} z8vYv(-v#&H3vg4T+B&5`2^uJ`$1BFweDZo>ULh9TGzZ{a)!xd<=7}Rq&1``DSMH=D zjWQ14_VPmyfw+e1MF?i;`sJ6C_%2kbb-UdM^41qSZ);heLu2e{g`LUrX0jeU zV8y{Y(L+;~moXzBEvrM#@9Ycw@)ZE@0gUe`@V-GR%9vJQ^4OXq71_HGr0FMZHm9%V zpGHmlGGv6g!2pX(?k7JfnMA{sK3^FDP4p!px1D#j-`!n#2JXB&qOd3T<`{OC{}VF4%b>Bg zRY>u$43k%i&P7cKq2t=3WXGg4s-Lxg{sx22HUBivtHEsC9j^C2(O1#3{-zbp{L;|Q z=ko5GE8b?@>aY%>_3gV^$da>#o}*2x#P!NUc*j_c%uWd!>3nf(ix=H+0vK!9$P-ffR|Nu-%j0-vbrT7`K}9=6!KViMAI`kkRfbEMc-}6W5`n- zkqM5DM#a1Uditw&g{HN5xPwnsxWOtJW;c&+fX)O9vVjZFeR+IkKC#5JnyUEjq7=WJ z(TP{H+~qB{bWh3af`Xe+pW6gF-1{no4_;p{XVZ={3%j`2OPc01+73!^LJ~fza>_gI z9bWA)6P_c}UR|FM0firT=Qgsor)#PeEr~0~AF*b$hb&f_(1p3*koDIiRf{FceH3t~ zaCN24L*YExt+RvRSYak2yvxf@AKDZisV+MC){6#sncTm8is~Oe#W4Q$N}fX9;7p+S z^@wYIlf8PbCL@kVWpC=2PgXJOHpF_@cAOY>p~74=(roPqK1)k5mdZRD9SP@~HIQ8!^J$s4_-so zVi*R>;m7dNEC)?jJeFktklO2=g8BVU?irtF@UEY*WF4FEVakS-Oh5lZJ@-)Z&0c@e z1|gdyJDqOJsKj`HxejHf+Y&_TZzlx^cNijzanZ+yHa7%>bZ#g>mA28?7E%1MSSf*ahONF9?CU=Looh*O<P0nfG&%d8 zX4}G^f8bA`KO{`n#}cP(2x%9UiD||b{On&HpcB;dw4zTFRxN?*GaYM6QqIG?@R>k1 zzm)A63zpTjHF10;XPld z715iQ+l&4CqQ4TPMuSoycVZ-r7>WAZQ6ps&rsAw8pDy|79DZEOV#v+MF)pltr2S+P zVvQ+_^p=3N#I36EKRRD&zP#nDEujQ)`1aq$>&qWHr$iiU^DDV}4^qEJXr?VK2YZPD zYn66jFw~)bQSA{uPg1yN#y!p*6`8!y=7u9T$OPEM2A#5GcaL*_2oc(xejt5dyYzY| zm1$^>i{K7xXB;YVQDU+a<^JT;-zxrSxBsa4YqEoUTG%{1FO81bA5afIhQyGs`_fmm zu0AY{tZm`F;4JKl%SF-Lg+hqvF4-ToJnC|)?fl~}Fj|Hwa;H$22rwjrn?Unzen8`) z<0I+4K%f@Mz`c5LWOs$n&FO@rq|#~vckJ0qFR~ zjx;rYlm*irKZ62G`G$B7;s34TS7zL!fB4PI`{tBW%*WA*Q%AT3dDA1~MA1b3An{JO z1#TQE68Rb@prs@;*DQ0zT?l?yUXQ- zN6(1wKGA(Sv3?j@?Rahh72`860gOFE1*o-tTQ>un5gghdO;zE5rKc@eKsh49I2qRaG$ye?^G&*KfxWb++S5#!Oc0* z+%uq}qJN>W-C?-6YH-}ujdUB@kId-yewUfX<-ZX>HR5Vo3w2@0hA3Y`|z=Y*Kq9aOT`CfPVvdF zVLx0CVPtlI1VKl78yylSeUnB%;LN^gHB+LHxWYwvs(3ZgymQ81`#1rX_Nr2Lb_M;qu%iSG9lU-hFfKtRO zPQuH4#UQ&Za)^WlbD|kPv-5C3_vbG(xDDX# z>WtRSMY_MwU5Pka8OSeY{Pg_HHDt+MUb>(O@wVU1<|=P%P|%ToGdIxi@zt^`J~T9T z7~Mzf3fHW7_6|(iuGn^h%B(0fhO&%Mxb*y~swZph1iXq-VlCHq8e2K87~IBENTFTx zGeCW5WC99k4Kld8q)l1>c!Z?l`EdaAMBUnX{cA%1!y+<%j+5^xLCJc*d^U4r{Xls~ z^~nM@*W>ZQ8h2GFBW(Vipf)Fty?E>Rp`1K^)!pI(bjgrTEIVM-G2iL zUhWQ<^7!K&C@Lnime(oneO2qhI1#>+U*}c~iT$1`Z}Dwr8tV1A&h2KnM#GaIOGK3j zD72*U57jkDFd8}+hSVxGFw-jaf0JnFUrzv((@UV&17|`v%iEa$zSQu~57qzUgK!Mf zc?B^tv2BXOe+!(l0&0aiuj0YgvHSfA4A0e{L?p^ z<9{s(zL_HbUW&)>TXeB%)1nNp$|gJ(T)lpKBk%V)?qRSZu=It)D-)g?lTr;2ZW zz-{nHUps|*wEwuI`rFWO@VB7>Tgtrz#Q~SfaKP+MAu}6@BzEC{iU5C@87mw!gCjI< zZB$$aU1FeQw1;GWM-oWQbS^d#j2c|wmIHhnJk5k=WS?K$rs_ZSc)y&+oHl zdC2bEE|pl)-mQ+6ZLhK7E&eAs#UtKavO%>d1Ww$lnS5zMrx1Wx4_`&=!j z;@C|2ik|KJS^Sr0q2~VZ#*@Gd575Y?fux@y?>kO-OqnT4Ln)fdAve?Y0upVOUQtuq=kb)@h#%E~Sz`AVh=)$M%m0WH6wG2z3wBWi(UM`L@!--C>O^mP5*)t(G zfmT#9?Ft=>OpYGjj{mMtF#l*LFerTmsWP^@zEZZXm8f=t(7y)75h{aD;8z(;wcUd6 z0~^ns!^65doB9KALg20eyQTFsTfOJ_7WfT4=T7_TbTQh--j7q#PjSyn<`O;nUSTYh zuOy1v6`R{w2eR|Ze{LgAI)q?ayCH#MUK=NgJ-GSS&SCJLuMWicK0yRVCm(nmqQZ&6 zqhIc4=x$vaH16bQn{k5GKJM8Wc#{5#Ld?uCcfL2V!fpw(L2Ez%bwTbyTZ>k!JjDpt zuK8C~9_y^=@k|T0DV@)?%O8cbULRr{P(QZr)5m?)HkEQZQNf+bAz3rBFAa6zF5?)Om;_O-FkN!C{evHTLMG$<&5CQ(fj zA{6xVe|`+_Jvq=jyI5^TqWXyY@~OGzd22a+FIpwfvHY(NJLIy<;?)?IrVAC;^g zg)26xI!2MZ?g3ovg;>H0K78jrR1rD6PI$ww`@`D=fO`nhEUEUD@S+7t2qoY_nBG!C zH8?l1jS)ceX8(@mvKqzWSFFc-0qg~Couq8jri>a1WK`$D9Z3Vz#<^~J!{%usb1T)J zGBMql$D4Cj@a^329Xip?=18(R-htT%q7@=NzO$Pj1;qSeZ0h~uFgLD#@v5JE7ne6O zY5BkL?ni==Zbu_&;#6DNn99lz5BvxueqBg(O(prA@#P3XA)1G6uH`70sU*fiG#CcdXha>sZr;;#Zs$+C{g~4wVZ#z1z_IMN9<;~y_?ZuwR zf8%7@i8#1&W}Xs@?r|{O^453fk0%ZH&BlFa)}5f?eUy3}`m%4^3ow_}jt|-C3lqgA zz>0#gIqbjmvqV=H#oE%uqh&c_iE-Zqjr28cRYBTYdGDlDU0bhZn!jbK(Zkmz*Hkve z&HK`SDQ@&%l^Yt1$Uo#wGr1lC@H%0|i!hF;}7jf8x za88e&_N1mnrKOun`HHSR+gh|uBQrW;KytnpcwpxWKJfB!Mg#})f3{m-FO>~`Wwv%A z>+ZefFQX!zE0WQ=xUr}}d;T1f!Hj=&mC`O9dwAWbz^H~qpS11yW*prgde9s3&e}oU z;@lvJ_-K1#PahO@TgHne>-q`Trzd3>U7?}-Wew%pTu!+)FK_nGe$2MW=c8o3 z`s*^>RieCdHQc&2Fly7cFK#3-&UwEY=RY7$A=>dE3-xy)Pa;5Dw7!&VUm?oWx8uo@ zQ=vq$T)UkVTY(dkojRt&W3WVqc`{Hl%xX3w?Xn~bLm0P2Z|th%R+z%`N+5gsdH#$2 zS7q5v7H>Mm#&5C%<0nNMAZ~2R{TR6~Gw)q%A2&@AX)W6iUx{7`FDzQ*Tmz5S?GkxW z--%Q~P8T6LZ6SK}4ys;lJ>fXGxBd;v9?TNUaaO}PDTmTsRAgA9wIl2I0kiZ|8VH(f zY4Xmv=Pk7=f)nV2vk%eCJe_`KUqJLcKTXlf+Gg=S&8zw@Gvur(jlc2!TFoMV$S2V+ zAx0Sn0wrhbv~Sn{+6!VIIF@hrdoYy3rZ%URN|modqwWu_xen55aYZFc10@7NXW(9H z5E=NrU_Dpq6yMeL4PP292bI;)QS?&5=0R&A6q}x_I!Id6rRLg0YtNeN^&Y+b(nyUq zJHJp}Sj@?%ny$HjJ)*s#6%xmt&3H1eLiFMONVn#rg#C&2wp>a0ljv-P@2037t82?0 zyWYE}fjQ=Kv;LDR9@?4<8-4v~1be*9KnpEc@i}kfXOK1RH-dZuj>fe4DALj5aQAdC zi9i9bdvToXo~))*gw_}|wHnO`I z{8R^>E#I1nd+vGJAXoZrhtkZSy$x&ZTw1{S6sk}8HHUDO^1z@u@W{a3J}R6VYdb_k zI&l2@5p=BHwr3lRAY=1`k;}1AQ;7_}Khj2~n%~-{U+5~&J`=qpqlDA(q!w&2U60x4 zjh{{=Jaj!(%sy$1iB{WAo_RPYorl2bvZxZIDVQd^W;XS1=Zgimw(MFm9&Ha;)1UwK z;f%q1NVwAF1e?V(mUp(6H-hU~JQ;vJ`KD$Y)Dd$yl896Y@GTpB4rwiDoOi3=PjqKf zJFH|(yF@eI$*#<2PY{$Hk0V%}w9ArKB=fXTS|NTxDyc9&>VJc6hw%4pH3nE}!w1F> z^pEE>F!vEL^9ov#;-j;q2Es@FE1MtjbKPaF=570+Z$Y^{exuDvEN>Yzc-Ov%P~vI58c_fjwTiYQPj8ySkD+)+%BN3IUDMlj=9lC)2oAI_D<$T@DyYM})wH~7K zfw%Cj2N>M0ePvf(dE1MR6y(jL@0W;Wd!DqDUsR*Asx|qBy zsB36WXeolq?claWwhCHI*?oa7UQa1Gqh|}RX1$P?i-#{??Pgyumm!itSRcTDs9F_>CKd7n_ob9~y2%XO&LX>h{(YYbZD8TDK>Z!Lo+)I+^rk617ZH z{pGUpZsoZrxW8t>Y9Y%}j(kar4gNtjkN4=vgWA`^LZmcXr;=-!=tP=`&R1Wfd%kE(5y6)ZFvbvC)xm)y5dCZ#o&DcPy=VAWh4=-FCBsiV~deUN@=$yTnVVw#7pn{p=5nti#5_f(P;9iY0 zFJsNFu0PxKvm1!~Y%k{=nWaAO^0wH=;U~P49w>+?6o;S4I+gvEv=`bI#Q2~z+=N6r zUb3)8I<=@+@g*O5q36_zNRL%lL_Mkhe;gL)S1p>GYRZ7}n~ z+6{wnX~u6n!tT}K4ABou=-xE#4}Dx~#}8W&ZlloXfNLKl4vSk?ddSEPV_LkdKOG5p zSfV2RF^zr~a)4knEM!^v<~XhcG+s_vJhd-td#ykx&}PRaa6%fC%b>B?hOy{`C|5a+ zl5KPIq#C3vn!d*w-syJF3FTd21SMEi>^#NPA6(e5%DD5ZYl|i!>h2xO+fy=SJM;aNWyR<)@CHl zya(_D`D>HFES&3O{l%S%ni=%LQdCdkq!?YeC@>aH|Adl!J2CeDne@T(#z>xP92;Pc z`@WNHiIR@tM;zv*UlHZi#=2|&i5UH1Dww|xCbi!Ot_0RJ`W+9Tkt*q5WyKlqEG(8~3JZ#NR+DMFXVndXOk_B){MJVK&GV#mLSRM;($g+hO6McV$k4A) zSk{jaXx+T)o*_HyJf|bZAD8f)LWyueI^lh3TFDLzC;K|cp}TUfdx8%u2}W{69B@k6 zi<&p}yJ9@7Qk}byc=7!CxWQ%ovrB(ecHTF7A<>|$%AA#5s#hxCT`DoluHK&SJGE$( z%{j2k>fm)}8kLw+KUO{k(#*=SxQ6IG0o>7bLYolP%a<=a%&}|N#<|5|3)oH z|I(fqQuab;$%3ytFUP!PR?(hBd{gox_27?(vBw5CK|W&w5Yw%gC%=|un!uQ!mlT6z zn}{uLmglGVa-=EXhYW1~G@s?$fXhS)^HZl)^}Rb1N2-r9p2k}&XSPY#O5Qe7Ac-Fk zq|Gl>nhPP?ukd;`&~|M5B3*A>7D#}1me2ju*~S0!=bZ+4`h8J4KauVv_u#g*)CeXb z9qrK2NBHEAOUU+u-Vdr$J^7sY5a$36>DVc|mbvga%^4PQ=cbvzFCLrxA2hq{-fGQ*`ky$LE7Y3c!?2HcfG8c!L<9F;ge^&|Fh0Yj3P4tX5% zcjuclS8G*q>i4u`s|>JhtA>i8Zdm1qJsCb|Ifl?IHz(y`>1IzyUs7Ba9fS?&5=7ry(;^JE<&nT`X!D(}@{n1a1C8h+#bj7xNN54HZ6T2Vv;hyTj5S?JJ{-*En%ce!BhUQObv#`s%guz-*_jAr@R|`7D+$-@<)wn z6`wweuILc)LtSq7a%!ArAxoa5-lm(#vjAq_E##HoADOaVHNd5W&ipdcQt9B&80CN%MmN z)ILSmVyr12+;hFy*NoV&N%Y!bDC|Ol=3g(N!6*)j!O|gp_oY9-*u{Izd_mCRQMJpI z=!%bvEeMgrYGY!mPXa)2Xh7{jyEu}G76F&;zp_3h~iB`X(1WCP;X^|OhQaBuv zs!XIV8dMiCVDOj4<)5CP|4L>Oh)`hfRl!Cv)kH#Lk6 zq{l6C37+|0D~Rvb`|YH+vV?_|T^BDNqAlZ)v)IQlDAN8EcvazJ{;_33=BZkN63 zT^w>ivHT}}Ik)p*2ji{Tt~b&=LfNc5^@Q8K-F-QGjq=QK(FU4Ot zLVq2G4jel3baoD-#5H)U3=Fv}B*gtR0;NVb%9GHCCSzmYjaX)u-*iR~;SFs0OB59! z7V{1#T3M56ERepNVE?FpDIMgLeKkRk_xG3)7;ha*2=Z1gKNQ9#uo_+L6R39qS?)Iv zT+c21%%RN*+t(|=e|K+5R>I<*eTWAD{ANO=Z2~f} zn#?j2k6L1I&-5O9Lih7bzG)RTB=4Wg)YKx~K%61+arf|6SbPh=-iRnT7@hWgS$#e; zc7c(kq4h-fC2J1|w%Z&W_yx7^LMA_!Ivm-Pyqx?_r!@1BRitm zfnvHkdX|rufVjfM*}(s^sZpW^M*mk+Bl)>Yhw+4c3ha8zRe87W9ZSm4Y3QcD$vi*5 zL7t+n)JzE`RjPV};{HQqd?s}GMkoBi^$hLU6VT$n8=WCQcWcpN9Z z*sW>CrGGR}ffr}c>}QeNzG}9*uNu!EZRBm5Py}8$gInzL+%gKE+%CtjJKaP)V!4uo zn?EDoJGAa)jcR_O_s-Cgxk7ZQIFIia#Amp92wU`IX5Qd{9np7=(q8B{`iI(pJN`>; zIR8s+Y&WJ2wY=$!A2yvDVq7D<2Z`iZB*H6wd-)qL)!pYpNbtLgu_rA3sclw8ZLZ&q zi}#4T7&MAAoPIajQu9tX0+(hIcYF4$Aj}`d(bxJcx?sgJZ2kb?1kg3TGucUw!_B7q zfpS2;h)|In+(4}6a*lS&q~MUS$1YfvPchb&;&{E_RJ-y!#`+Z z8Rvm-32cQ`rDnh&{(l*e!bA)YVIkLGzZ#%vH>lvazRz#E$GSOf(-Y>Mv0xir!(sFg zS%kA-*Q{|!Havaz4Tg&fQ1;@{_ztoj0Zse7HuXo@l#(La!MDcos(l)-rT~OtxsFzg zMGY^9J~qY~nkRM!-8hf7SP1A-d#WOsb+SknUN3z5OsOD9`|QKXExpbq@r`Y4&FB3> zVoh9B;IWEt$a%gP@)~_pR>F~A<)@E1ziAXj{!!{DHxX(z^rZ!J;+ET!6V7(&5Q~`q zHR^xxXB0c~Mg6yZz-jmIqj|Zw2OvMsk+N%F?EKFI4B^Ds7u-R>U9=VDqx>5D(jnZD zhsjiOp_mLP=VV{)^zo{gdkdV$827h+m4fZSs4d#>)_xAqECC6*3DN&|Qy?awAH-wV z#2j~T?h$)vDACr{&O>kL&~Lo&p0@Bza$?&JoVJrUpgVY3eC=IA))>MKAhJtWv$;TYyPbH|K=Xp{*QYgKdjtv^?AjiWpgaj zrC~D{=+DKt)l&}Ua;rMfHaM@Gn)EFclX7`P#~MH2nIX`8w^?6gvlDz==#!hC4n}Cc zXrv(BytbGhOD|dLy)BNlCIY zR#}H}6i0>Nw=Mo!`(`Xin)ppeu)kmXvHTTtaJw9)OIs!haC0VKPR7KY+zqoyL zHs;xzSd}7~v~X{=69{hY7vS(OC_Tqw#)I-W{4!QBzFc(lUdrb4VxbfmLJGqHiB8#j z=_1YV+Zfvq7aKhi6uowEkm0$ICQr;a4o^%Ttc&!Ye+(L6T={Mer zbox$6^1eo%#=_UC-*_pd)a)b43=7P#5wHF>=J7fgPYv4Q1gi-ipJcv^hVb;ntm)?S z@fIZD_BX*f3>NUj!u;Jo&q!+R!STl`r+bk>r}mDYzfAJ6EVFRjmP#@>rfG(xELMr?%9vDwL8-f zXr_OC;JrYrpyF-UhuW}AEayCux1TDFe$e(KylPGZ>M$f;+@BPMCMxy;a{HHk)ssG= zKef#!X z1coDaEnQ7hGY!$YTiClZNUWM-EdySxCLT9gGTz3YduEmh02jwiHtd1!1FR?mKC7c^ z#0h_hH_3FG?slooc6QZ#YOLtU*&fw!zTW(MhrazCz^jk=uiVrmVLZu{^0n_56zRrt z!X1x0GdI>i{wZ2BZ8+M4NjEEnMTIq^jcDSF?_qg4$45#_E}|tIL-tw0Ou_$;cD%w9 z#zC$k_tCOjMnhz@#@z+^G3QX=H=f(Wt2gdg#*_HGO1Gx~Y$>sZft)+#PWtwknO`?i z4rTr6`FM6KbL0H;psHx0z8R|t{o#a3<)$tT{`6eHrvp`|s^7y89_Ewwf7l8D4$BmH1i~=3gP&6R*m;<-)A)2$!32q--j}tP9G^YMcs$T_t~}lA|6%YY2!iv3XKXj z1|++HW*D9yoTBOjT7xC~X{x-OzL+vOlPDg@brcigCn{Kj)U_Q29z~>v>5;m1DV~neb}~}0 zCwL7E^e4*8zrKV*4;Wu`I==ksdXyq4vBog}1aNGhV+0iI_u@awcc}ZxBx@Xl5`^j1zK`M3zJ5lryd57YbhQh(EojHL zJHv61*L^Z07?A>%!s$P>P?}V+QNQtI^Gm) z&!;yNhErbfBgVMfuCjlYyTuPjRot0>tMKjJi$i3Ne(S6}@`~1)>{;E5f=eDWTH-g} zQdw^UJkgi<(|W=^$xP+@4*2DNpHHRh6w_pBBr4(zGSZP(B2QLedCp0xz7KWa37~rh zTj4xv!#re-Gh((RzGYrGdNsNW;-~n^dvq7%5wC-XY%lToY3QqlJ8o;9T*-C zZWiVm3u*agD9)=* z;466T{g)5A^iHu&s=Im~t@Isk%YO73c737>3ZhA%%a;z@aZ19XGAh$w4s}9=z0ins z7M+1zLTls@KG0uKkxjt~H%#Jq(dCW^mHa};5}t6Kb84_)q~cUm1c!XLJ|W6%cxO7* zm`}vr_veFUpbl@olGuKGqablCE>*8X(y2(`se=t4ZS?5%ZqIJMGV}Lr$cNoB$Q8$- zuKihL2N`Rd>}fPNVav!0=|Unss8Wb!zp2 zr21H6urk}v(Nj&e)11h=bsfmpJ&Csw32-jwj+TtW^R$NES8O>C4m^?Xe}Wgt%D~bv);}b1rNC6 z3=%Idejv#LbI?8)s}X5tyPzmz*I3i}pDowIhUEbv?kWo?Dp@Wnmd~YN4TYT+*z~C}OR~CSCRqGBMj0`fd3J8?fgmfgOMEIq z{l3LK_cz{=)3meQcld?QJH`@my~rHjV709bGOJ@6gNy{z00V zxT#=IIPsu8r$a~LB}$1JZyo2$l~H%39xp+pt#888nf*{}Pthe6qZ-TWzEeqXR*gt- zeQg_N+tfvHVGnE*5u?haz;%)b#b7%gWy_M)^~Y+Hkb*SXy%MjcUXPm^xSss-Oj9)m zDnJhE8lR@@1lk+%{EJ$&3jNZ&dTW9&xB%2dADJA#c8sfaNfNaR$)y)(tGoTz$l ztyx9awTn-CWSSSwY?kQ0_3H3FSH0wO0ywF6oae4&2TmPbU?0 zJ+(|ZPTqEBJF4arh0SD*c@8Dog>$!kvo(5yN@01*%4sQ6%UHZ4G=69mNnSqIE=W5- zMNK>vGe6Nc7ji2AM5`iJ*c+5!A;Hlmj46~g{__eOQT=&^jq2w{WCvdlBe=|=w2MC@{!&eHR<2PXqv3j9^D-e2IitZhzmq{Ed(k$4e z6FR)P&w|v`ArSk@BiAAZkIhn^w=d`$3Mac?9Qq^~#$G%Ecn0~B!E)!*Gcbc#h-1A`>4%sZQFsoo9qLF!|GV3kDfTvQNVTu|U5c>L5cT?p zNEYf9+lj9;UU><^ZdW+vW29@a%;MRR`?K|2(>WpJ$uQQ%9XxHH!q69$vJAv=h6!YQo6p9&WJzi3c6%=kl-O^*0vsZ^mgB9q&vZ?{ z@P9Xjv;_-YFuNy`?~yU56vT1DO8Vro zTg`Osctz7*V$&*S969wi#8f%M*#M78zkWUI=9yax=a^TfQBMp`-_ zMmmf))yv>kA|wIL=dSpZV8w|8)LU;foq9C52N{lFG;D74Wyq6JKyVnay1}civbBP z8)~M0Yx6dFe2=??=vdd*_28HmjL$Un8AUfRi5XZc!c=tIL&;D4!Z`{i{u|GbX6ubN zdIixV8X)Y8)HL|ICyM;+_{i4ihUAsR#;7B`NK7ZZHO-SsmggOW!-D_gF0#KZJ}Z6V z41KtsDnz7L?_H*ROaJhzZg2VX>OEyenth&PmU9LutIo#5ucA_;&x6Jq{b_RBfeRNX z;ubo~L4wn#SU#iQcrnjvP_yWBZi7JGQ;| z{Iajg`QUP2ZzOilCivFh(S3`fku|@(I*@Ub?tjdR9{y<4r&~p-A;I5t0i6!@=K5%A zPs0zD5P9IRdm|SZPKY2X4`GGh9X3-`V0!|p)6vJSw>FA;`aJj+iea`|aO+G|o4b0h z-k~+bI7qpwluIhNBWLpU6o+a0p#)(-dopm!I0x?!3f^GH#)GBES=aL~$X+X(0V~1N z4^Jq0AACGYWfxmPjk-$oqj85bKSI;qlw6J7_}&6~_Qv^Vf0o7Sk!v4NiffRHciwI5 zX`OwMyYUSCHZbBh9@or!Qomh;i%!q1VW_3*Lga1mg6Wff(ub|4%PokXmlo9*2;9`Q zdz76Y2tSIw`IY_#vaqk%r+qqfLb*v(QS)I+dLsXBr%>71+~*%M1Wo5}fIgoN18aC} z?UHe8tULS@EgpUsQ-P27W_-g0dz0z-L=>qC6X4#k5Md(-jgG_WpWT_Ar1F=Ih*}Fe zs({zJTUps&kiygv09f+LN9DdFLn2|^8u{I#&9Haev%OB6H8?pcg8fs=qAbX$Xox_?cU3E-i#^$O~X_*LPO3olAckgMqYYH1@ z%-1On0RN!q^@Xt4&*!@fs1#`aMp@Nz#1#x<2)TBy!nr|uksX(ZR>M%*R(Q&Q zzTDdtboWC!lkro=UP7EK)cTZ6A|=CHu>|*^H$~lIzPujXG{%}%!^zaa{%+{1qKoWq zcj@lga5gP?ZAZbJwc4lYHy(eE2^B<3hChN-MpF{%UysAW&DOM-TnaQVWU-LBxC=; z?mPE94;baD|C6D+@uUjfBApS(qf?s6w9b3z6f$)_?GtL!hg*J;(vG@>P8!_llz&km zQL;A;HE2ILTsddIW{?i8JC_ZQON*C6 zs;i`ZS@l}GPKrVl=HKFL*{lA|Dpybxu+SD@s#W7~SaKp`bb?S{y6V2>rs1sLPB0S8 z&bNG`>{r91u9hYaf2FFJck_^4@~YB-6a@9j0*JSq*C8S|G`Zz_2v#aTmlrvIaR}AA z4&7qNtH+YsWT4z>r<3%W)-GK*EF8pV?6StX?_imbO1Y1#R`vw#Y!&nS2Sqs9iJOJz z-po8YzV3dHoBqbImnli!m^_2sr-p196BsMqQNbf>T0EalOiXMZRbD;t=B07(#PRjl zyeUCcFxro_<_maTeRu_yqK5&;)g>tEU{yl33$;%uq1wm%+UQk=kn@`K=}uMett-Qc zGYKI@7ZjbZgN;%C17uHu)cuKP=pKI5nK>UxvLL@oyiwBtLC*H6xb@lEeyvSi?E6`< zT1+#)$*Fla6yLq2pSI?95n&B<(QoE+faPkQ+%?_ShoyjuJ;+#s!hM&B=%EOX|BJ5o z@N42*+kR0|EEGXRkkF({2Pu&NiuB&3H>oN$^d1!ir35L`i*)I|mxu_VM|uemAOWNk zdP_X>+j~FnyPtDD=MNymtXZ>Wt^2;N>#Ouj@Rz?+miJ76f{Zd(aK|itwK0-*;<4z% zyVZdvD2)1KBh_+{-%2*~TFDi=>-++D1fGM>-uR0fC4BEv^?7@lcU@iM58zB2VZ5>C zy>;$@nwfRU4L2t0#6&9WnX;HljhKPPj(D&~v6{2nVl_RfRbP3FNUG)E-7R^uIK{b+ zuGE@450ZIAR(*|^KP(yjMN&{f_illdly`He{W>h%-R=kyKYO%df;~=E?0J0nv%KV5 z)Pp{r*O}}lPyoA!=_Q#V;lkAEFB}{DK{<`z2Fc9H(`6nbEC<1>i7U(R=z4j zobo2`i^n6rI`g=eoSuib1AH+2s%B5WN%c9L{Gyv{+n%=7Yn1__N_hw zWo2f&rIT07ZDf*v;?s3qr+bf(EQi`R#1pmL7`jX452;?x@1ER6uXOW&>GFE8sxlAp%%uzstF0If?PgJ?bIKg|wIpCWz>2Sie zBx*On`-1N2pBv|KyxYF>2c4FFQ#40h9-fTGrZH$b)omXxNNUWm?-N@}1e@XGGo!<$ zou?&$pvFmzyRj)nMI|04X7Od|`rQfUW0`5|L}dXceT6?XTOEh)a**|mDl|$E(v&fi zqj0t5ZJAN)P(2>o&}x<-E1{)b<)$NQ)nGnAZm`4Er&{n!@y;4P7 zIVa#20@&4G6_!C3#P&%vv<+>IR%0~B{Bpf+`JDu+d}WB_Ey6}dvi41|jnO*%1MIX% zZkZ76?vWtRYQ~nk6=-HaFk7y30L7!C`05*Q7Y;FD~=--ED82`T9PF`8L_Mjor;m0Lsv9h_UaJUdQRlNuF3J5s?RfI{YJ?Wj94 zc%#WDwZu3sO;wqV=A3k^0f<^O3}Hn@F<+2Y0dJs=2J_%uzTShmJp$iwc_fYIDZ;kH zO0P4uM>ICT$8ik^{E~ofeEJZBk+te5?3%X2(tdf8`xU?Mc{0L3p?+J6v+EMGUb3pB zV;{`0dasTmG;n+15i#Ie)D|bHo;eQ*(6D84bV6J9iq&*L!lB zW;OS&EClcyv43!u=kFaJ$#;S)ucO{h`UElfjI9rmJr5x8JFd-I-zaic_q9Q?(z`h%XDC1U0=%pwsX`a+LZ#{zsBA)Lk+BX-K?KcYPWP zX3yr>SJF|nwaI12tTmT^I`3MK#12PVHH3H=tuSLkWY34LvfcguE_0*R7oSs$J&_#w zWUOGO)rr>Koe_BaXQQEYSM6v%TGXvY1ecid?SRx7x0=QL`u7Ek+ z2?=tNm2UHM?|ZfVMS%6?RPTmt_NH0hxQNtV-|YV%nS_6D;Xi>7jI?Xp|10p}Vg_TO zaAKLhp>y2WB$MjFApP}3I;0H{)gdnBQo7=%2Qbl*%VqqyJv<)8R9CubS8SM8`06}_ z9L(V`#QSOPP01Pp6+5Nu#$AWJHuqJnjCG!1>2xxo_uul28^oAi7J|wF%B?WBJgF}E zxzvf?K+OQe)A|D>7vg`=b$OJ|rz`bky5nAYu8e4>b+C)@FRMbp(2Y7(^06)AIXJ|9W7GnU&^3!0Kz<|1$DhHZ~w4g1P?> z8Ne_Xx&X%>VGRQ!N{_ZaZ8wZ{HC1twe(yR2%CP13t$%v%^?mvBp%@?|l;Gk=9#d+b zJ$=&n|Mu{U|CfiK+~gk*e>j+@Z9mFr0ArF$P|WfhM3VdUeA${SGFRV0gwlYPN<+n0FFiej^3b(@S5ajSKl^K;O~=|Je11hVmxVowT@C0;56X5U8^}E|i}vffTPrgAOQ28iMkvsruhY%r zwd;tKXmzRh9hLN$vvdF(g?GAmyA=rOKZ~}FwxeP>MI)~Vot$%(EVy<(aktKh zbqUz-ybeU7#ymN@hBa^t^={~j=T~MDX1rf=?av7j_7usb{6J+jV?KG?$E?Uh{Ym%9 zEwLnP8~1q@af2X2spN$3m+q9#Sn?XR_`Y$)7OMM1lU#DSZtIMdxNv&Oc(o6E1@j${ zpo5Ycn`nEXL;Oz>wSoAN*hhq2B)Xv*u4*}$n3BxnKi1gh5^qDmICMd{6`95m5 zoC&_q2(-tjHxg7{^b#{qf!_I@%_fQIg0U!y6&GGJDi%`+us^ptm2-+T5p(6Z;mjkC z`)ng&VRX>$e83sg4_}br&U(wWFlLPnsF~rJm;WfR7u@y-v4p2kS}f&4-`_q9_b zNl!Ra$AXE6dL64Nq4eT)=1E>acHvL3Z=m^?n4A0WvbnFFOr`TTd$>)XU+=;y$+@ap z?bgcr*W!e`IB{^2Fp`>zf18bhs>Of^_hW3>OW_||0#mYb!_sfN9(`w14E+g{TQ7dq zB{PSOy78||+5B^sidjcDMzua$qiEvm3)V%eAgUQgFBv0M35tpAW& zfz$gLt;MH#AMerKV8(HRgvdbaEG~>kIgzT_dTJODE}L{`2d;CW>A)y1V7vrVeo?>P z*Q2F_9*S2;^7L`%g>E!e7l2X_4od!e%tZ||wh55<539#D-XA>Ox{pi!3XB?RN5dsQ zG_|Z9olR0Ll{aAB1co6g%h)&uC3(HvJq$9kcD=k$G<3Y=`jh}H+rmh0BR@b+y%XdY zM3`Qlm0yKGtPT#&*Yn0kd*q9V6#sl0?RG4FHEKDhI!4}=zshE4Hq~q8swK4EnGR|e zb$P{K9jUS*P?(y}iLYo9aLve10H-cFu3GhWi@JEick-!(r(M zWx@TfVqgyChYbKO`VJ5|eD+#{AOE8#e(2u&6sizupVJWtim#(kGJ)HClQN z!$fkwnY~lL)|2aV0hL-ojLxGTQnvK+*C9f0d0pgDS*w-y6bt65OE;IIxT<~>LXL~- zVeiZ4F9bUDUQF!H#`Eh+Rn34am?J^1ue1xX)rG0ZReFjC` zMUea%Qw0yHH0dYdmlJK5mm89XR66)sz~G+gxO~JvG89X<#y}qb`a^!R;OOt`MPHYX z@)(xFT?$oI<}B2?z|l5*FMX}dwqOEwRdvVS9R5}roP{!;OGD{^ylNZ2nDnG;Tj6T#t;33Y?M9j2^2~bpQ{}L;GujRuBFxd% zry3)?KJfdQ%_okJa+wih@cFHw2L9CjFP+qhti8^$Ds-1Xbw~%2oSmEF={M0x+V*uh0C%>eDmjvO4)=eo?$)xM#u&2`ywFA4C_Oa1s*|mL zD^gzEM)KzYG5`Tr62o#lOBrAt*H4Zjz$~5=Wi#_yZzMwPk`2D`@aQ1cF5iON1!0$Y z98W#ywGSPyBzlxEGm{u)$M=s*DFxT&MGW9cNH%lHi6t=1O0FX6(4`PN?i(LI zm8*yDT8QR$c2q|O>?-m=W?!2v<+7X8>;gN(Z$;VTBHP?i_TeIZnX1DUexoVjNQ_~IUaly-_ZBAaYi@>^;T*CuM+5?_2Mg9VdN7oCuQ6hAcYYDmflAj&cW4r^gI`Xnnxn=s7GVl;Xv>%H z5J=c>gK}MJ6dX}1>|1>eA^(Jnlo8#it?@)15xmKS6|Q|hHQ@VW*>3x4JtTAe!u+&B z+KATie<&mezCr$z56VOC9%fv;+1bm!d}=&3mln;yqK-}4f@#NC-Rri<^ao#Ne9^^wXb66=*E0png8e$rqLV6zS~vJp73o|Fa(}w* zBb&XRmC54*wdo||xZ(a*Kf{mC5OZmn3vp|z7(BhK&}JBKxy-WvAUfBvK<}N;ko)_J zn{O6)%#hs{6jsC!SIE%tQYd9S`)6Rf+lwlQ_YT`qYFr!6Y^$Ob4X5ec*)@x?$KKtl z1F>|P%~^YD!K!!I<8IgdL>+XNXw&o*Cld1#(nX6jrVejyRU>`d7qBN#+a4wAR9o)k zokU$9A+#|gsz>gV++DY#>cg#Oi3^x-u37Q@QIGx1FuK+etEW`5F2^%N{G!KU>+eO_ zHSYv}{MJx%=C=r9F5U1K!-mT=$TY}{B<>^Q?K%_-cfJ#v#z3Y;lvgA!KK{KA)a#v<=RO}S6Xtfv48qSU9W%D^{dm>zW zN{`XS0wG$)udISM3{ zo+F=qeRj6Q62EIgj5rPC8oq}N_nccBJQy`m9^FM`H#EwqkG7kQ`JCe2+=m%`oIi+h zUQyfOkvVlj4ObSO`*f&#NHq%^rELvff9BdRRev6gC@e>QtWRKP{Y14Yi#RETG6sPg z;^}v86eNIpSj)ZU34kV8Q(1gzex*|io4`=;e1kHOYV73fplof$RD21hy+*)cx*x2~ zInLRHlecC>Lwl-wJZreeejJ=kDjm~H-s{o!1SuFX+9%XiS@LVwXG3NExk-)to39um zc1_Tq7t4Sk>KeOVOB&X@2Df~cC%*r^jZeuEB^v zkS#@wAz@{d*mu zitv>g?h)N6PGE@=xetfdglbd zB7@eqCjSNK=w>@GKV`U8!fhQTWT#EKZg4! zF+k{lB?i2xF))$r>qB2woVU6*H&XxXCpBQeExzCHpcFUZZCBV?8h{rOa06LsBIQg2 zr7)|^XT5i56e`1DD}dbHg4P9(>w*gpNx9aLDTpTAWR2%t)<}Sl$BL_y0}ZVR&pKo} z^Aez5L`cx=RR;TyA8#yM>`YzKUnrpUYkoRQlZcj;+9mF zX%#Q4)HZKghTT?nRY{=HRuzo9tFHH9B4^8g7X510UijQq!$Dy3b!dX1a;pL8gQT~S zfiLc%nKqGyjMTb|w5DHIaXsPnwFCL7DE;uGmdNOb5YSvkvuN4S_JbF!U&)HK`cO^; zFUR~XU*??lBnWkAg3C2t@(=N)%N4B?x2YhT99m#(Be!kr{*k2x=DY0-iVnrNv*N_D zHkxQfkvvpRN9S=Y8vdlf$5(r!cHr|*b+@?B?DbPIJIqhNoEQxgb~?@fyKNad z+yb^OIzugqwOACtpFO|z9baJE()sUgOE`eSKz+AIGHr>%_5+I5tG%F0QUJ^KGZW4> z!LzzA-7QMSW(eXb55BI8)0J~v$CPe2?$!?C-R9N_+&7kvnCkLC?z26ivatca;#KZYsjp;p10>* z+jZJ(x84Vd&@is6zEQawgvX|j*WYL4tou^55$Nx_Z`U10yl0=p7)mRe_oSjEFmqO| zsNU(@5zDWToK2$yLF>*qPLR&iM|Z5Vxu^cL*Y_%q_=4@z%fjikUXZwbbN*s9r+$hb zhG%d$4!~v4K1C{UQ5lf%Cm=bezwr>fjWqqbQ-@Ur#Pk?5ruSAlBR!7y-wX46fmm%8 z53m8|ZU=sy;|Y)Koi7noLoeUPgyFU6f>PJOO3;Kx*2qU!xQa>DDefoPwM&wkr{wX2 zIjLNwHoQWea2;~P!f!5sr0HmJv8AJLS6yc4U&Heiv?C=_9^Bf8y? zwO~FnXD>$mdDQ5+5lqpFMY-vZpTi-cY%ptK`4!tE)ypBD7h!dPXTY=#`cr1>KEDJpRPT zig$UqEc4=YXw|Q2C7%B3JE!n%t1(nhttY_iLT{P1nTTNrKGWMpcJp;=X6sBTu-}** zHg0?wAY(gr!kn-U_S`;^O^B6SO|(siOqcNN<9|^6>Uf)U6t{`W#m#of4)9mIab9&L zV@$d-p*nu7wo!xxL8Mqh5qEJz9Z6LBvv!!JEb#MeJ*oUz6|L)P+c0MDDlD`$)p|3u znU7;e%y9_qA`sf`BZbvC6Z!eo`ADkGdB%ENoW+wJmM#%o!QbSmRsE$J=9N=k_nSjAm|!` z>fwaJLzfX}h;B{>j`!WwAH^y1B=q9FPPG>NjMu_Z6Oz&~6@cAQp5cd?AKizZGitCFubcj> zjr2KUDu2k1E!`V$O;e9LQk%iMK_TZOT&<4byKah#w-Qp)O75r#Gjk1 zq#Fm5a3{uM46S>&Yqt&smRE>m|Kg#90 zidgGQv*n9pW=g(>+LaGHfx!wsNSBN9F6ax7@p3j zlH1uUJ-%Nd`!xV(=?VZ+vh11$ZnwDEV@y%GfMva9*YuHP*#c8K?;KMy$O|$kejF2(_fRU|?dBF= zYAj`O94rg*Rez2)=!Q$-$lu@kp|}Q@b%F;HTUg_Szs7&YMqNz2cGDV)5VoI3iqOV; zh1pA#=~e-m02H)#2n}d6)HaF#r!xBTKtC;nD&m5YrYHR}tC3~HqzXYEIm+>fOX^e5 zlgd9Z3w?g-7ibR1h&Iy^4}++J{cY@5aQRC~)~l>SZ?DA`!7?3b$4aHonyXM$8TfYM z)5jUFMugnybL|!t9!Ktbn$BKIpF)o^dm!jlDWBh|I>;_I(t4gHW+CHL8`QPCt&1JE zfl92LXGepK(4?^PE0F(wdeFto1h87)sOKwn#Ro%Ep=OPoBL|YMBefeqIR9z8oXaHx z#V^} z>AA{NF64eNx|HE}WVBiEer(JsE)AO?zX6^t_raf?4)eIEex_ zOPZx9B%TYpLYxikX~|X&O>;1d>My&kd({P@i$E5B~W>U_7@9noWYrN>CxPc znQ#pXum0;o3#}n(i5?2WNXIWP2z!q|?p+G)mK07Y{62;5t)K_r)Ajmo194kZyJaZ- z!};W5Y@&T65Cb;bmrQ!D)GE4nurGcw1Lwin$9nSGBxadwUg`Jn!`31(h9ao27{wz@7RA| z0YHtFRR@^JDe3j}=!#wM`=7r0?GF_2NY~x|?(T8i;qCpoY1T1MsCY>qe|#n$rX>97 z*XbLp-`JzytyprO{edL%-iQ@t#9LyB>;%?uv3F-L_0#WT9@7h;C%k_|KArbP1V_is zEspB6YTP`UU@vywN$gmahHt+f?B22;U|5OHb5vmG^>mRCQ18Ul!X&@%`b@uS?2Er^ z_haMTZHqQJiAK%J2WZ1B5nPtGuP4KT)FE6YYJ8$$|5!Go!^ntn@ZNma4KPwrQI%Rv zy7(w$s$$}CQbW)b`whqY30BpaEL~{l>af}YE{%8IBCqQaaLUz~flFo#tpwUi6S?zJ zG|a3Q^r3+_M3ykb)R2YA<4x@wf6n@xtW0eK!7Mc~Q$uq&cP$xYAHk6`0`h+Pynf$` z40%|ak?p*n$5F7^a-!rcBL{6-^G}n)`flIqw8#nxaoaBSJP*=DRENQ(Mx1;iqIUd% zGTeAoK(y$Z1h76#B$nw{eQZ11&SM`XxBI8iCRcDg#j*BeBHQd&H!_Q_r-T?N!r6#w z7`VCA8BeNbM3Pc>y?FfHqad>Ww;o9rS+C}_sFRFqdgs|3L7wv+fu75m&h0$UFAJdF z1lUoDr*&rvpq}x(*=>y0j^fj+H8YGpAjk?as#&c~cP8GXnd{ULeeN)8HaLp|>vag} z4M3Q{BHJ}0&&+G?8g+9!gr*}ZV&|Nz2AFIUKa@xK{q~c_FH}^fO_~$rFbnE#M5Tl|H*K!=URD=XPRX4T zx{nIa()7@#G!JxAuB*ZO>*C{LApRm@A5u!#Ri>a7QbF?v$G?Yv_ce>!Wz@QhIR0#( zpuF4m2yWh`MV<$@b9UCbG~MnXo=j*+14aP#ICD1KvHQ< zj~xdOM3#_`yZQc-bjnmf&}Dm_aNPvi!BOt(Cn0GMyve*jBSB$`f)jdY{M)@Au%SPL zV4zZeJP(y_t75xpb7%AOGCUBrQ0FTbu^-_4s^!QpkbZw?9le~Qmj1@S=-us?c`*u5 zuym}$T!_zWA+n8F+^YN5zU{p2XSiSWFRoFQG!iD&alCyH*+4lNfHtu1rdu$bdMT%I z$fDmD^=5r_(<#Ys=H7sYBXW)y?0#)(c#+|p;LuaL|A{k1rr(^BB(Ek-Vk^gT#2<63p^Fi!eoc^T_ zk^GN3q)dewn3ID}0(GB?#QF07!GqMe)GQ8g3rdJ)D7;pnymD|d_3E=I_IS&l2D^;n z$|0xsl-GI1&_}A*_p3uT8)MSMJyPaTzc4H(%J{FKiEQqF1x-Nz6EuM}y+A3(p2Kfd z->hMmRiv)?Hy{KR$~7Ad{)W|WaR*%ZL)`_+3J&?(!omMWWCT~=Szr2}h>4T22IDCXQbB;*faXN^=irDiqW%H*c1;+bOU%5L$#>V$ItkkkwzwmcaHuxERMj^g(Go~(>W9Im%W0~=nkh& zw1XQ91DOM>7a6ydBNS&Eeh4!+-y#9CuT&=0G4De59a8#YExBG^JJ|b5{a+Y>T>aJu z?oV|;Df3*8Sgkl|zo~+@n>Z&L=Ex`GWEZ@w_D;j|&R$v#PM4zO&Zp?Kl*B8B z>klvYL*uPF-?z>VdANGqEol4POS^9*$8!7fx3J7mQCFNuIs-l(mq z^nv-o!S{J%nHOD-mwR#*O)E+IZ!yZ_Hl0!AUZuD{!M48ZWO>%f1xGGR43n?p?1dE~ zqXt~4So2ovBnyfB{cguK^E)#=us>COnQIq!+R#5R-# zyzZ&&f0)sk5gtZNu20#&>PnfL|>)n!#tJQ|8rLGTGex9#NA&@YVUqAEQxvgv*WL%HeC~ zdTMDzz&7j_;J9efW7@Zo;Ir%Q9bX%E|8_Vz5fFK)sewxWgm1( zsLwvEO+tB+jqksB5EWVbx%1=P4toUE3*Ja?4jWw`tF+(yBKCR;dadeN+SvtZ6Gu$3 z#aoiblFT`cCri;`?5s^%>;^^Ln?`_97~@vTk^d{=vF)enf227fv%@;NyVb~5j|0sD zM$13f#*Uk-%(BP>Y~)3_9iv_hjl&9{&NXhrOY_RE<^}Q*kBbdJXLsUKw)2&4K4YjXJDsXJBY)y1~;#^x6+R9iVUd0ot1uV;7f&EkrH~PiR#4#nI_NozS9oKAZc2(EWm1Ha%-iuq% z^5R{&y8%g(4C~Svt^Q9$-rwTE=#I}vN-B>%CxR~7E@4H^1br@*t%ij;_HcUan0SDy znzqd-GO0Ll^S8)y_(XRd{B;+za0d5ezsJh*L@Oa=_6UkLGli5@A#d-&2%urBpE^fO zVT}y#17jy~4enj~-S%xC^$ouLuGchdw$eM4S<81RlG8yuG-NP-l8kC^#MO3bvjhhi z1|L&oF|5qHaz>G_@!F-ubbsTKG7NoB@a_*lV55bR{LIPGL%gD(nU^LzBg$1mXE!4(spKRu#B&TN!PB=|QAyMU_81+DS|VCEroI=3^Wf}4A= zGY^-})}F5pu4dHq3rBH{Qa=iL-wpKHWxefu(?iMjP7emV&tSYi^xE5I_uJZAoo=df zyJSokaSE95;5O{(1+4592wsAlV-11DeMwSXPu4v~rf3%xU58#~CZV1CjUZ7vw2Xnl z(pEw}>69nXiP#%{9BV&-Z2fxqcXj?iw!WvIxJRgLc5Gk92Iv#<*MXbQ^a&JQw)T{) zsMQ7J>pJYWdy-qFb+4zXi7usFMGNR(We1aQlZOrJ727onXNn})&tPynutycMF6<4R zAQ{ddq@UHM1cF1nO`4iY0wp^`yZDkLD;@5KGl&7NI;lM*e({~R&@>~Vj;z#0M%}iJ zvsKi^x;|QgTh*IyT}taXN%rn~@B=(Kfm!sYHIm;mQh+)YMk7tgbaHF3%sgG=8aRdN zWns8RjL5(7cRz?|?d)>|efC8#)8i^m_H0ponO-G)^PoaG?IR85^a#(^A1SpzvcL(P z?#~64uc?fgzL)za2~XfJ$q)u@8L?rR6MJLrlv@xYwM3bb+Vbd2P0O|VsgbK?Yvyak z=W>N=~NMFCG&$NMpupf;*`$MpZxD zj*VIa@_WPr|6J^9KVADg@WF8fbtREQ&Qi`Xo9XbDg^cKZxji>PPv^f6sIp@%??q2^ zPp2|jNY3Raa~~nl#_1WlzLcU$C5bt*<&(?)&zm}5$DDjyJOdKZ$p!KSeKAqQ5U*~p!(Id6e27>Nu2;3s z_$rwt2L^ zbXgr@gh11nG@QP?tz-HA3WXptgT;Eo7i8z2-78QmTJ2n0B=~gl6g4Wx9KjHrma<=W zqW0Ws*1FTV{*rLHOlss}MRCdJqE(A8wWJXDu|^Jq_S4osR{=TzZ@&mqS%q`@#08H( zUKK&g5O+BmPIInsN`iDAfeI>-kPu`9IdmGyI_wYn94|Y6#Zbmd00Nn~^z@g+IDY=9 zq+i@(#lZ%DmMYVsWh>|wyOPr=rR0~_1W<{(Ib-C1ySU%g^1#0U!dJ37S`N9Nwf>4kOu`7jmfN_r=W|f?})5MW9)P_N^P134bo4;m+Y2 zVW}uO;XKyrN1&1qF}dtR9vjX#0gA!yj5?iE*ixjO-8&G^lq+igH#*ftV)Q9g{T8`k zkd8ka?-;*h!iR`%&$k<=gUSE$LL3tV8qDJp^~)bDl?kSQmD>xD%W;!=XyzOU(-dAx zNTkOhpp-D-13`tQ3KUa)ar3-w_dVY%KM|W0+r%P9`lz$!z4q{ng|;jzd3{lL=j+sG zU}AvF@fw$h*qq}~Tnsg18v5CFw41G&?aHXGlRX%wm9-XwSrnR*TzH@D#hhM0_v+=X zF*pPL_k3OHPcK=t_)Jb~Z0-5|iWqZVy<#O!l?1o{5?P$C?p5+8l4i{dH(Nj-@1tJ2I`!9u{f^Al=N65Vp zV8t!eUS@CZw~02VS7hfTT`jGzWPhpbE9ZOKh-X=5n$)Z;(&nPbNkPiY8Yz3xZn4%c zM4tUdRtCft1Y}55MK>w{>KfaLr$X%(LP!>wqx?LMV!>gfzJWI-Q&mbVYGOOkt(=%{ zVaw0z=qmz&dqv@8%dH!2eoGP5AY*(FXS>KZ#Mt)HlxC=X!g#LZ5mRDAYtt+)9jFaj za5AMIbUS56;~kDDdib?heoj2+^qGgEBbR3@b{`Id#V;5PwG0*M3!KSD+vTAA?JPf! z98|urdOx8p@Zmwsowqv{66q!VYPF#oXw5pj{z#l(!Oor$)uV&b_B(hMAh+``i6YuI z>lWXWn_5N(V$Dd$lL^W$r}Q_;!P$Zu{-R=8v!rh*i}Ki=uwSGE2h654xFg;AwP81! zPxY`uJ6BzUfshqrQTw@VjZ+WXGU*I`bDLa=kT&tl%sc7egjb=owYV$|u|yT<82z{D zv&OUF=9xpc?q;8b7=h?&WoR~O2DhFX6NyaefobJvr|F~;4vvM?aEPdBjz3a@oB|SG zC&=*K5L-{vCb4$=93G9;*SVf53udRj)3phQFo915c{Eys}nD(ipo%D|LO54o~IgP%y zi0{t!V*<_e7xz6rd~2KVG#Z%ZtGNN|l1-1gA<#2F#GgQ;lW3XpNc_HkwUle+W}fi5 zNcxC@DXQ1p(7PdQF6@$er`%2>i2QntbGzwp)x&ku3x}5kSYW$ zgpw<)Ca8~`T)6x?*ly&gR%x8==t#ubDs$T%boC8?Pjoo^V~?coPmJ0A70oz}HI~|E zf?7{2n(`Xdr1{IXJuS~7Z-V|*vMq12e}87GU(m9oTQ9}4Yty}c!Q;Z-_?b(F?QMd8 ztJwkm{B^bmDf`W5iEo~o-9CTNN2rfVI)txvW4j(EuC`40*zHIvt&I!g(9Xf%MXYV3 zCxs(}4n{4$K{Y2D%L#=L7$GJg{2s&DXQb53%)>WlMb9c9jIz8HA|eXYENl5yYa|(- zTE_N0T)KZ%2CX-{_F>Zu`*u6a%hYr9_j*4`guou*FUh9*3ta!wELb2*QRVhcjNKNi z4Dm_*SCiajoZ9Txs5`@_LO#1Hjz4kD*hDqH zGE%m&-0c^F&?0w^J$GUQ{O4OhK!SW05PF(-o5go8OZqE#`w@#-@Ne7 zlHx$=#~qQURx8O^IZCW{w}!c^MAxwPdu3iD=qwU8S2CuLM+eBK=bE7-Bo%3;0%rb( zh-4RSUx4%r^c~KpVP`uLz<;I~n4S$elKkp-cOL)$fOsn4+ zuNSePVg5V~+X+{CYV@&L=RH!$DUi;y&k~0 z3Cx7tLlF_fw*nG;ES#nugdhjqYlij>^h*Pu=3b?i z!(fW3cgtYLDFHq+eNngK^z?p`?kio~8cM`C)n-2&M-fq&XT(RqU2y*;>E45#g;)Jt z&#Ig(#4bH2%Xc_pu>OI3f_KSX8HjYY@z#`T6;S7`7(k}^X2rH;y=fBemlEkT_G4w` zTiC7?dSJ0xaR>~nK{f0s_Q-oU>y-c#zv~cWe;Mx4B2EO zhCI!E2=&6zix=@aOAtb#G9ooT+kgcSK_9j7eScZS=+hmyixa9Wq9f4hF*8C;e zbvzoVP@X^#-u40MzRJLhAwI%=JlQI1^tC_URm7l!TIMfFE^b<0zx!&ShR$OPo4sq( zEi1W6A1!W;L@`g@u<`^Ie03G+X!F|Z0H43!LJ$goc}qN_+Snum1V4koP3{7Pt^;r3 z*hDi2tBI9*?v~4kFKCSleb;Ky*bBov{^~!s_))AG&;z3q5!gY*^e7St;6$8Howp1< zH2^NBHD-!Jm1ZC5=NYfyyM(!#Gd!TN*kp`078{spUJA?bMBe;N&Y4A14i^IW&yaJV;I(aSEJ^Jq#y)*?OxMoe59BvHB$sPj??Tr3NkOy15PH`V&OT6#O`DWu0^FBfD5^H|rQKcW4WBvu7g$77>^fi$cBSvV4AD(zcuy(B8rEheGJMV8!zlVrPza-|H#)@iXpVY_mk9~>hP16otfeg0m1*FBl|DUR^#2@PZ z+fzv^B|^4-N{I3xS;9P}jD3<6F;qi#QzJVwl~NHCBeEwk*+X`j$}){@VnSmZnixiz z>}GX8p6A}zz4srO`7GzW&v}2o=Y7sVEiFNs?IrO_1L!PP+6B@8PYSh~H-rkCa&rZB zZb)Ut1E$q6>C?oT3zmnEc2dAMZFdHLneSWhSYBdM06v01<`Z85(ET{?UAWnK_PkuU z1Oe0XD!`uU`|F*d-!k1i3iv=HjGJE)O+9*>JlpKk)kE~WjoVp%>S5f4$pbyKCB3HE z-1xc?sBF&mIr&?bDU{OJ^)Hr)@>rS^(u-9DV!%q{%c&F-`&*0jAIVOmjtYUSg%0CL zZ@Ckh#%)MuYpnVmqplEZ}B{wt(6nbfF+S}0r6s%7l@CX%vFrnP7=z?&p`M-z+PL7@T1A* z4jiN+D(KGe@wS%*VU$BC_!HjPhLEiiZm_QDL(Yj>e@apu{9>nrS`kc3;y_&ZZ_-cI z>a}mZzyOdu|UA`%XLjhqdD%dgA@{$f|dHhttG`;F&(vyyDi1r5# zKoeDX-2O03?j5)icaluMbYuciLJ%D#3O2L zyv1b4_X)X$@2fB!)l&jc*s!u7$XE$uyIG8whGv7oh(6E|_s2mQx}7f2vqhGh+E4i>$(3sbzr$^{r^uL zkn++WNeBVu;I|kXLgkS1>Sz$n-_$`E0`sbrOZvp55Ay4qqXtwX%Ex$^!3Q+qhON1s zp^Q2Tw6z(-p|S672VR+-22`a=v!!%ubEA5!B?2WE}3{mZYr3E zF3i`-EX4lj7NX{9SF-CaQ8o7j=SmPV?nDD;J|wy!Gy;|^*;SG+_pYbcf)XoH|9So@ z+yR=&@s{8A%;V=ar@zz39g;#wi!M+s4JIx~YhT0U;XyF&Pls?A9H#R&n=v#+F6&V; zTolXxw%TM%E(*-YiWz!zsTkc5ahDX;$g2zmHv+U;4shrsSwNY`@DKnHvQ7RL8AVI3 zKw#~Aa`N71X+!I#nl$3Aw`bzXV)!k8$$Wg~Jz<5ZSRN{45jgJR;PVNFM|ZPZ1Lp;= z&`MWVcLc)e%!!F9`ulU29@l;AIJ~P^*bR9x*4tyJndYsbeYG=_EWU$d1lX!I1i{JZ zT+S7wgDf@)B-7^a5>L^+LTq{sB{ixsWtaE8{xk{6U8X4LKD{W#2B4)Lg65=CxraXY z79Y11D#Hs-mw|ZOlATSxvuh$vdqfTJD(?nJ!O^+)@@&yLc;cyQv(L4vewVJr)YfHp;TD(?};)gV1?E4t~O3JQUn~50y)T9eHKkwf7vIO zA+eQs702SvurBd@;$-r%l+|jFn4y%{dr|hI{F?4fNH5ICRjv+9lD>R6jao7=z$>ZE z3byDl$V|lwla{$N%y6fZ`eNr)@HHh4dn!l(gA?z7;Fgf{DFxW z=_7_QNcLcZdWl!>pRDm<8LOvN&x5T#9?bM(uW{!z&|Lv3&sQQ>-^3s6)ztjasdVp; zA5s-t`w(9^++-fIx0t=`)HeE3G<2y)N9XBS1RrCyJR*Yxc^%=8!bsU_rM zT5^GLpTYgY*t+0|BbNiGs>3d*^*wC}Juqg7@=P^2yW`0tXQcb#(^*;Wln1BL^YBuY z<3{bmgwFom?TvN%z`S7q#yTp7+2C)*XZ)OD{co2F+G1_6WS{3G6VhWvteXqre z9XFVP0LhHLZwu`e$Kqalrl&gn)nM{+#qRN<)YPArt%Uj6 ze6^qLvgec!$K7`fjbLKHy>W^fK?(|Bh2a{gCBS_G)RONv(QHgmjT7W2St}R3O7hiV zHZ{uqT(YJ)Bw9)^pFOcM?)U|rW?>uhO`u>(vRlGx7auNql1#fyk~;X7If0rY!AQaG zziy;jcDPOdeYLpX>qHSUl3|WSMc5g8{aM#fUy}&v$<*DV_xu#x!z6 zNEc}2up`bD8$xyjfevt*GE}?ss;rJN8j^Ol*-qu}tDP^nvq$5uP!cn*5D2s@W_;|3 z;4X_xUmfrtkK=&$I+}6^fXUjLZ^CzykmJQ&OA(n=?hM2c1YzfBEYtU9S)V1T<3yU- zDV=S3sk**jT<)F%%g0?=DNEZYw(~Y%&m6z4{TL45a!e)A@XBaLh02|?d2W9f5p$$V z&Qqt&`a zLrUC!4D`>ofIHdqO0{YTMsFj7K02xf(x#YTmmmiQl_dlo!x6&5aE1iI%?iV&E=*&m zg~DbRs+z}(e8qZd*JpHN6wa$pGbqbbx_C#v^k;W*%(1uY+(wK5|C?W9y1TPmn{SpR zk3N3whM)XNSD8?W(a7(X3O6yO_byVe7qAa;Vp@+`1P+bFs+%8mdx%g^wcL8o+pmrV zay~kTCCBE2j3r)P%tg%Z~wbhq{y8n1&4&I1PcbTBneJEJiE zESr|*^~90~tsVP0Jhe=oK>g0!llhb;eogp4K90qP>h#$6SXaQgpd`1-*{`H}=Wz?0 zMZY-hv0(L`@NC4@}{3W6Q5;7rZ*=MSZ7rekn9NRC@gT?>|( zlf7eV?6(d*ZfD5-HA$tGT=2GWwH^@pX7{BEn-y^ZLVPo~DJlb#ZIu^I$?8Pw~d+e$$*c)iP&vdWQORxd^?7JII-xy;t;RlVsVy}soJgLU@)qF1ifz1I_W z_PTw$)qU^1I&6fz#O{;P4Y(6w&O&NZ<xl?3y8KaDGJ~7GQzdHiRzK4vj<~yNYgceu58rxPjOK{I=V)7P+;ola4s{S z-^ay}%CPppq*e1VD-9S>;kziXu~)<2v$7i_bWvh7-cqB-_p>Pk7T}%kOeO)_lqZsO zFB^VYWB|o{`m4@RXDXz=Amf<+$q@T`V)CQi(#f&h5Mj+oD6n#>0Qm9>^bTOJy5QB- zUfRK3v>^T{;(mctD7E9)>g|yA+Vv-$x=#lVJ7mZjtGR^It>MTCEkR#I1(@!LVOYDg T?!^;=nvyy*e4tiI8`S>-{k`RF literal 0 HcmV?d00001 From 3b17448597f8c0233f6d59082629de497fbf5799 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 3 Aug 2022 23:34:56 -0700 Subject: [PATCH 209/334] update docs for autobutcher --- docs/plugins/autobutcher.rst | 175 +++++++++++++++++++++++++++++++++++ plugins/autobutcher.cpp | 86 +---------------- 2 files changed, 176 insertions(+), 85 deletions(-) create mode 100644 docs/plugins/autobutcher.rst diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst new file mode 100644 index 000000000..e77e13232 --- /dev/null +++ b/docs/plugins/autobutcher.rst @@ -0,0 +1,175 @@ +autobutcher +=========== +Tags: +:dfhack-keybind:`autobutcher` + +Automatically butcher excess livestock. This plugin monitors how many pets you +have of each gender and age and assigns excess lifestock for slaughter. Requires +that you add the target race(s) to a watch list. Units will be ignored if they +are: + +* Untamed +* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool + individually, or `zone` ``nick`` for groups) +* Caged, if and only if the cage is defined as a room (to protect zoos) +* Trained for war or hunting + +Creatures who will not reproduce (because they're not interested in the +opposite sex or have been gelded) will be butchered before those who will. +Older adults and younger children will be butchered first if the population +is above the target (defaults are: 1 male kid, 5 female kids, 1 male adult, +5 female adults). Note that you may need to set a target above 1 to have a +reliable breeding population due to asexuality etc. See `fix-ster` if this is a +problem. + +Usage: + +``enable autobutcher`` + Start processing livestock according to the configuration. Note that + no races are watched by default. You have to add the ones you want to + monitor with ``autobutcher watch``, ``autobutcher target`` or + ``autobutcher autowatch``. +``autobutcher autowatch`` + Automatically add all new races (animals you buy from merchants, tame + yourself, or get from migrants) to the watch list using the default target + counts. +``autobutcher noautowatch`` + Stop auto-adding new races to the watch list. +``autobutcher target all|new| [ ...]`` + Set target counts for the specified races: + fk = number of female kids + mk = number of male kids + fa = number of female adults + ma = number of female adults + If you specify ``all``, then this command will set the counts for all races + on your current watchlist (including the races which are currenly set to + 'unwatched') and sets the new default for future watch commands. If you + specify ``new``, then this command just sets the new default counts for + future watch commands without changing your current watchlist. Otherwise, + all space separated races listed will be modified (or added to the watchlist + if they aren't there already). +``autobutcher ticks `` + Change the number of ticks between scanning cycles when the plugin is + enabled. By default, a cycle happens every 6000 ticks (about 8 game days). +``autobutcher watch all| [ ...]`` + Start watching the listed races. If they aren't already in your watchlist, + then they will be added with the default target counts. If you specify the + keyword ``all``, then all races in your watchlist that are currently marked + as unwatched will become watched. +``autobutcher unwatch all| [ ...]`` + Stop watching the specified race(s) (or all races on your watchlist if + ``all`` is given). The current target settings will be remembered. +``autobutcher forget all| [ ...]`` + Unwatch the specified race(s) (or all races on your watchlist if ``all`` is + given) and forget target settings for it/them. +``autobutcher [list]`` + Print status and current settings, including the watchlist. This is the + default command if autobutcher is run without parameters. +``autobutcher list_export`` + Print commands required to set the current settings in another fort. + Useful to run form dfhack-run like: + ``dfhack-run autobutcher list_export > autobutcher.script`` + +To see a list of all races, run this command: + + devel/query --table df.global.world.raws.creatures.all --search ^creature_id --maxdepth 1 + +Though not all the races listed there are tameable/butcherable. + +Examples +-------- + +Keep at most 7 kids (4 female, 3 male) and at most 3 adults (2 female, 1 male) +for turkeys. Once the kids grow up, the oldest adults will get slaughtered. +Excess kids will get slaughtered starting the the youngest to allow that the +older ones grow into adults:: + + autobutcher target 4 3 2 1 BIRD_TURKEY + +Configure useful limits for dogs, cats, geese (for eggs, leather, and bones), +alpacas, sheep, and llamas (for wool), and pigs (for milk and meat). All other +unnamed tame units will be marked for slaughter as soon as they arrive in your +fortress:: + + autobutcher target 2 2 2 2 DOG + autobutcher target 1 1 2 2 CAT + autobutcher target 50 50 14 2 BIRD_GOOSE + autobutcher target 2 2 4 2 ALPACA SHEEP LLAMA + autobutcher target 5 5 6 2 PIG + autobutcher target 0 0 0 0 new + autobutcher autowatch + +Options: + +:example: Print some usage examples. +:start: Start running every X frames (df simulation ticks). + Default: X=6000, which would be every 60 seconds at 100fps. +:stop: Stop running automatically. +:sleep : Changes the timer to sleep X frames between runs. +:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, + BIRD_TURKEY, etc) or a list of ids seperated by spaces or + the keyword 'all' which affects all races on your current + watchlist. +:unwatch R: Stop watching race(s). The current target settings will be + remembered. R can be a list of ids or the keyword 'all'. +:forget R: Stop watching race(s) and forget it's/their target settings. + R can be a list of ids or the keyword 'all'. +:autowatch: Automatically adds all new races (animals you buy from merchants, + tame yourself or get from migrants) to the watch list using + default target count. +:noautowatch: Stop auto-adding new races to the watchlist. +:list: Print the current status and watchlist. +:list_export: Print the commands needed to set up status and watchlist, + which can be used to import them to another save (see notes). +:target : + Set target count for specified race(s). The first four arguments + are the number of female and male kids, and female and male adults. + R can be a list of spceies ids, or the keyword ``all`` or ``new``. + ``R = 'all'``: change target count for all races on watchlist + and set the new default for the future. ``R = 'new'``: don't touch + current settings on the watchlist, only set the new default + for future entries. +:list_export: Print the commands required to rebuild your current settings. + +.. note:: + + Settings and watchlist are stored in the savegame, so that you can have + different settings for each save. If you want to copy your watchlist to + another savegame you must export the commands required to recreate your settings. + + To export, open an external terminal in the DF directory, and run + ``dfhack-run autobutcher list_export > filename.txt``. To import, load your + new save and run ``script filename.txt`` in the DFHack terminal. + + +Examples: + +You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, +1 male) of the race alpaca. Once the kids grow up the oldest adults will get +slaughtered. Excess kids will get slaughtered starting with the youngest +to allow that the older ones grow into adults. Any unnamed cats will +be slaughtered as soon as possible. :: + + autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY + autobutcher target 0 0 0 0 CAT + autobutcher watch ALPACA BIRD_TURKEY CAT + autobutcher start + +Automatically put all new races onto the watchlist and mark unnamed tame units +for slaughter as soon as they arrive in your fort. Settings already made +for specific races will be left untouched. :: + + autobutcher target 0 0 0 0 new + autobutcher autowatch + autobutcher start + +Stop watching the races alpaca and cat, but remember the target count +settings so that you can use 'unwatch' without the need to enter the +values again. Note: 'autobutcher unwatch all' works, but only makes sense +if you want to keep the plugin running with the 'autowatch' feature or manually +add some new races with 'watch'. If you simply want to stop it completely use +'autobutcher stop' instead. :: + + autobutcher unwatch ALPACA CAT + +const string autobutcher_help = diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index ee93d30e5..32c8b8764 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -86,95 +86,11 @@ static void cleanup_autobutcher(color_ostream &out); static command_result df_autobutcher(color_ostream &out, vector ¶meters); static void autobutcher_cycle(color_ostream &out); -const string autobutcher_help = - "Automatically butcher excess livestock. This plugin monitors how many pets\n" - "you have of each gender and age and assigns excess lifestock for slaughter\n" - "once they reach a specific count. Requires\n" - "that you add the target race(s) to a watch list. Only tame units will be\n" - "processed. Named units will be completely ignored (you can give animals\n" - "nicknames with the tool 'rename unit' to protect them from getting slaughtered\n" - "automatically). Trained war or hunting pets will be ignored.\n" - "Once you have too many adults, the oldest will be butchered first.\n" - "Once you have too many kids, the youngest will be butchered first.\n" - "If you don't set a target count the following default will be used:\n" - "1 male kid, 5 female kids, 1 male adult, 5 female adults.\n" - "\n" - "Usage:\n" - "\n" - "enable autobutcher\n" - " Start processing livestock according to the configuration. Note that\n" - " no races are watched by default. You have to add the ones you want to\n" - " monitor (or use autowatch)\n" - "autobutcher autowatch\n" - " Automatically add all new races (animals you buy\n" - " from merchants, tame yourself, or get from migrants)\n" - " to the watch list using the default target counts.\n" - "autobutcher noautowatch\n" - " Stop auto-adding new races to the watch list.\n" - "autobutcher target all|new| [ ...] \n" - " Set target counts for the specified races:\n" - " fk = number of female kids\n" - " mk = number of male kids\n" - " fa = number of female adults\n" - " ma = number of female adults\n" - " If you specify 'all', then this command will set the counts for all races\n" - " on your current watchlist (including the races which are currenly set to\n" - " 'unwatched') and sets the new default for future watch commands. If you\n" - " specify 'new', then this command just sets the new default counts for\n" - " future watch commands without changing your current watchlist. Otherwise,\n" - " all space separated races listed will be modified (or added to the watchlist\n" - " if they aren't there already).\n" - "autobutcher ticks \n" - " Change the number of ticks between scanning cycles when the plugin is\n" - " enabled. By default, a cycle happens every 6000 ticks (about 8 game days).\n" - "autobutcher watch all| [ ...]\n" - " Start watching the listed races. If they aren't already in your watchlist, then\n" - " they will be added with the default target counts. If you specify the keyword 'all',\n" - " then all races in your watchlist that are currently marked as unwatched will become\n" - " watched.\n" - "autobutcher unwatch all| [ ...]\n" - " Stop watching the specified race(s) (or all races on your watchlist if 'all' is\n" - " given). The current target settings will be remembered.\n" - "autobutcher forget all| [ ...]\n" - " Unwatch the specified race(s) (or all races on your watchlist if 'all' is given)\n" - " and forget target settings for it/them.\n" - "autobutcher [list]\n" - " Print status and current settings, including the watchlist.\n" - "autobutcher list_export\n" - " Print commands required to set the current settings in another fort.\n" - " Useful to run form dfhack-run like: 'dfhack-run autobutcher list_export > autobutcher.script'\n" - "\n" - "To see a list of all races, run this command:\n" - "\n" - " devel/query --table df.global.world.raws.creatures.all --search ^creature_id --maxdepth 1'\n" - "\n" - "Though not all the races listed there are tameable/butcherable\n" - "\n" - "Examples:\n" - "\n" - "autobutcher target 4 3 2 1 BIRD_TURKEY\n" - " This means you want to have at most 7 kids (4 female, 3 male) and at most 3 adults\n" - " (2 female, 1 male) for turkeys. Once the kids grow up, the\n" - " oldest adults will get slaughtered. Excess kids will get slaughtered starting\n" - " the the youngest to allow that the older ones grow into adults.\n" - "autobutcher target 2 2 2 2 DOG\n" - "autobutcher target 1 1 2 2 CAT\n" - "autobutcher target 50 50 14 2 BIRD_GOOSE\n" - "autobutcher target 2 2 4 2 ALPACA SHEEP LLAMA\n" - "autobutcher target 5 5 6 2 PIG\n" - "autobutcher target 0 0 0 0 new\n" - "autobutcher autowatch\n" - " Configure useful limits for dogs, cats, geese (for eggs, leather, and bones), alpacas, sheep,\n" - " and llamas (for wool), and pigs (for milk and meat). All other unnamed tame units will be marked\n" - " for slaughter as soon as they arrive in your fortress.\n"; - DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( plugin_name, "Automatically butcher excess livestock.", - df_autobutcher, - false, - autobutcher_help.c_str())); + df_autobutcher)); return CR_OK; } From b2ab93b3cd97448ad5705590676c837e3d27b1a1 Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 3 Aug 2022 23:35:13 -0700 Subject: [PATCH 210/334] update docs for autonestbox --- docs/plugins/autonestbox.rst | 27 +++++++++++++++++++++++++++ plugins/autonestbox.cpp | 22 +--------------------- 2 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 docs/plugins/autonestbox.rst diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst new file mode 100644 index 000000000..6842bef5c --- /dev/null +++ b/docs/plugins/autonestbox.rst @@ -0,0 +1,27 @@ +autonestbox +=========== +Tags: +:dfhack-keybind:`autonestbox` + +Auto-assign egg-laying female pets to nestbox zones. Requires that you create +pen/pasture zones above nestboxes. If the pen is bigger than 1x1, the nestbox +must be in the top left corner. Only 1 unit will be assigned per pen, regardless +of the size. The age of the units is currently not checked since most birds grow +up quite fast. Egg layers who are also grazers will be ignored, since confining +them to a 1x1 pasture is not a good idea. Only tame and domesticated own units +are processed since pasturing half-trained wild egg layers could destroy your +neat nestbox zones when they revert to wild. + +Usage: + +``enable autonestbox`` + Start checking for unpastured egg-layers and assigning them to nestbox + zones. +``autonestbox`` + Print current status. +``autonestbox now`` + Run a scan and assignment cycle right now. Does not require that the plugin + is enabled. +``autonestbox ticks `` + Change the number of ticks between scan and assignment cycles when the + plugin is enabled. The default is 6000 (about 8 days). diff --git a/plugins/autonestbox.cpp b/plugins/autonestbox.cpp index c47f4425f..dd052f234 100644 --- a/plugins/autonestbox.cpp +++ b/plugins/autonestbox.cpp @@ -33,24 +33,6 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -static const string autonestbox_help = - "Assigns unpastured female egg-layers to nestbox zones.\n" - "Requires that you create pen/pasture zones above nestboxes.\n" - "If the pen is bigger than 1x1 the nestbox must be in the top left corner.\n" - "Only 1 unit will be assigned per pen, regardless of the size.\n" - "The age of the units is currently not checked, most birds grow up quite fast.\n" - "Usage:\n" - "\n" - "enable autonestbox\n" - " Start checking for unpastured egg-layers and assigning them to nestbox zones.\n" - "autonestbox\n" - " Print current status." - "autonestbox now\n" - " Run a scan and assignment cycle right now. Does not require that the plugin is enabled.\n" - "autonestbox ticks \n" - " Change the number of ticks between scan and assignment cycles when the plugin is enabled.\n" - " The default is 6000 (about 8 days)\n"; - namespace DFHack { // for configuration-related logging DBG_DECLARE(autonestbox, status, DebugCategory::LINFO); @@ -90,9 +72,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Wed, 3 Aug 2022 23:40:35 -0700 Subject: [PATCH 211/334] update docs for workflow --- docs/plugins/workflow.rst | 153 +++++++++++++++++++++----------------- plugins/workflow.cpp | 81 ++------------------ 2 files changed, 91 insertions(+), 143 deletions(-) diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index f6b8c4475..cb20ad806 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -1,73 +1,69 @@ workflow ======== -Manage control of repeat jobs. `gui/workflow` provides a simple +Tags: +:dfhack-keybind:`workflow` +:dfhack-keybind:`fix-job-postings` + +Manage repeat jobs according to stock levels. `gui/workflow` provides a simple front-end integrated in the game UI. -Usage: +When the plugin is enabled, it protects all repeat jobs from removal. If they do +disappear due to any cause (raw materials not available, manual removal by the +player, etc.), they are immediately re-added to their workshop and suspended. + +If any constraints on item amounts are set, repeat jobs that produce that kind +of item are automatically suspended and resumed as the item amount goes above or +below the limit. -``workflow enable [option...], workflow disable [option...]`` - If no options are specified, enables or disables the plugin. - Otherwise, enables or disables any of the following options: +There is a good amount of overlap between this plugin and the vanilla manager +workorders, and both systems have their advantages. Vanilla manager workorders +can be more expressive about when to enqueue jobs. For example, you can gate the +activation of a vanilla workorder based on availability of raw materials, which +you cannot do in ``workflow``. However, ``workflow`` is often more convenient +for quickly keeping a small stock of various items on hand without having to +configure all the vanilla manager options. Also see the `orders` plugin for +a library of manager orders that may make managing your stocks even more +convenient than ``workflow`` can. - - drybuckets: Automatically empty abandoned water buckets. - - auto-melt: Resume melt jobs when there are objects to melt. +Usage: + +``enable workflow`` + Start monitoring for and managing workshop jobs that are set to repeat. +``workflow enable|disable drybuckets`` + Enables/disables automatic emptying of abandoned water buckets. +``workflow enable|disable auto-melt`` + Enables/disables automatic resumption of repeat melt jobs when there are + objects to melt. +``workflow count [gap]`` + Set a constraint, counting every stack as 1 item. If a gap is specified, + stocks are allowed to dip that many items below the target before relevant + jobs are resumed. +``workflow amount [gap]`` + Set a constraint, counting all items within stacks. If a gap is specified, + stocks are allowed to dip that many items below the target before relevant + jobs are resumed. +``workflow unlimit `` + Delete a constraint. +``workflow unlimit-all`` + Delete all constraints. ``workflow jobs`` - List workflow-controlled jobs (if in a workshop, filtered by it). + List workflow-controlled jobs (if in a workshop, filtered by it). ``workflow list`` - List active constraints, and their job counts. + List active constraints, and their job counts. ``workflow list-commands`` - List active constraints as workflow commands that re-create them; - this list can be copied to a file, and then reloaded using the - ``script`` built-in command. -``workflow count [cnt-gap]`` - Set a constraint, counting every stack as 1 item. -``workflow amount [cnt-gap]`` - Set a constraint, counting all items within stacks. -``workflow unlimit `` - Delete a constraint. -``workflow unlimit-all`` - Delete all constraints. - -Function + List active constraints as workflow commands that re-create them; this list + can be copied to a file, and then reloaded using the `script` built-in + command. +``fix-job-postings [dry-run]`` + Fixes crashes caused the version of workflow released with DFHack + 0.40.24-r4. It will be run automatically if needed. If your save has never + been run with this version, you will never need this command. Specify the + ``dry-run`` keyword to see what this command would do without making any + changes to game state. + +Examples -------- -When the plugin is enabled, it protects all repeat jobs from removal. -If they do disappear due to any cause, they are immediately re-added to their -workshop and suspended. - -In addition, when any constraints on item amounts are set, repeat jobs that -produce that kind of item are automatically suspended and resumed as the item -amount goes above or below the limit. The gap specifies how much below the limit -the amount has to drop before jobs are resumed; this is intended to reduce -the frequency of jobs being toggled. - -Constraint format ------------------ -The constraint spec consists of 4 parts, separated with ``/`` characters:: - - ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,] - -The first part is mandatory and specifies the item type and subtype, -using the raw tokens for items (the same syntax used custom reaction inputs). -For more information, see :wiki:`this wiki page `. - -The subsequent parts are optional: - -- A generic material spec constrains the item material to one of - the hard-coded generic classes, which currently include:: - PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN - METAL STONE SAND GLASS CLAY MILK - -- A specific material spec chooses the material exactly, using the - raw syntax for reaction input materials, e.g. ``INORGANIC:IRON``, - although for convenience it also allows just ``IRON``, or ``ACACIA:WOOD`` etc. - See the link above for more details on the unabbreviated raw syntax. - -- A comma-separated list of miscellaneous flags, which currently can - be used to ignore imported items or items below a certain quality. - -Constraint examples -------------------- Keep metal bolts within 900-1000, and wood/bone within 150-200:: workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100 @@ -104,16 +100,39 @@ Make sure there are always 80-100 units of dimple dye:: .. note:: - In order for this to work, you have to set the material of the PLANT input - on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the `job item-material ` - command. Otherwise the plugin won't be able to deduce the output material. + In order for this to work, you have to set the material of the PLANT input + on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the + `job item-material ` command. Otherwise the plugin won't be able to + deduce the output material. Maintain 10-100 locally-made crafts of exceptional quality:: workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90 -fix-job-postings ----------------- -This command fixes crashes caused by previous versions of workflow, mostly in -DFHack 0.40.24-r4, and should be run automatically when loading a world (but can -also be run manually if desired). +Constraint format +----------------- + +The constraint spec consists of 4 parts, separated with ``/`` characters:: + + ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,] + +The first part is mandatory and specifies the item type and subtype, using the +raw tokens for items (the same syntax used custom reaction inputs). For more +information, see :wiki:`this wiki page `. + +The subsequent parts are optional: + +- A generic material spec constrains the item material to one of the hard-coded + generic classes, which currently include:: + + PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN + METAL STONE SAND GLASS CLAY MILK + +- A specific material spec chooses the material exactly, using the raw syntax + for reaction input materials, e.g. ``INORGANIC:IRON``, although for + convenience it also allows just ``IRON``, or ``ACACIA:WOOD`` etc. See the + link above for more details on the unabbreviated raw syntax. + +- A comma-separated list of miscellaneous flags, which currently can be used to + ignore imported items (``LOCAL``) or items below a certain quality (1-5, with + 5 being masterwork). diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 600a8bef1..83f312bfd 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -73,84 +73,13 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector [cnt-gap]\n" - " workflow amount [cnt-gap]\n" - " Set a constraint. The first form counts each stack as only 1 item.\n" - " workflow unlimit \n" - " Delete a constraint.\n" - " workflow unlimit-all\n" - " Delete all constraints.\n" - "Function:\n" - " - When the plugin is enabled, it protects all repeat jobs from removal.\n" - " If they do disappear due to any cause, they are immediately re-added\n" - " to their workshop and suspended.\n" - " - In addition, when any constraints on item amounts are set, repeat jobs\n" - " that produce that kind of item are automatically suspended and resumed\n" - " as the item amount goes above or below the limit. The gap specifies how\n" - " much below the limit the amount has to drop before jobs are resumed;\n" - " this is intended to reduce the frequency of jobs being toggled.\n" - "Constraint format:\n" - " The contstraint spec consists of 4 parts, separated with '/' characters:\n" - " ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,]\n" - " The first part is mandatory and specifies the item type and subtype,\n" - " using the raw tokens for items, in the same syntax you would e.g. use\n" - " for a custom reaction input. The subsequent parts are optional:\n" - " - A generic material spec constrains the item material to one of\n" - " the hard-coded generic classes, like WOOD, METAL, YARN or MILK.\n" - " - A specific material spec chooses the material exactly, using the\n" - " raw syntax for reaction input materials, e.g. INORGANIC:IRON,\n" - " although for convenience it also allows just IRON, or ACACIA:WOOD.\n" - " - A comma-separated list of miscellaneous flags, which currently can\n" - " be used to ignore imported items or items below a certain quality.\n" - "Constraint examples:\n" - " workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100\n" - " workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50\n" - " Keep metal bolts within 900-1000, and wood/bone within 150-200.\n" - " workflow count FOOD 120 30\n" - " workflow count DRINK 120 30\n" - " Keep the number of prepared food & drink stacks between 90 and 120\n" - " workflow count BIN 30\n" - " workflow count BARREL 30\n" - " workflow count BOX/CLOTH,SILK,YARN 30\n" - " Make sure there are always 25-30 empty bins/barrels/bags.\n" - " workflow count BAR//COAL 20\n" - " workflow count BAR//COPPER 30\n" - " Make sure there are always 15-20 coal and 25-30 copper bars.\n" - " workflow count CRAFTS//GOLD 20\n" - " Produce 15-20 gold crafts.\n" - " workflow count POWDER_MISC/SAND 20\n" - " workflow count BOULDER/CLAY 20\n" - " Collect 15-20 sand bags and clay boulders.\n" - " workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20\n" - " Make sure there are always 80-100 units of dimple dye.\n" - " In order for this to work, you have to set the material of\n" - " the PLANT input on the Mill Plants job to MUSHROOM_CUP_DIMPLE\n" - " using the 'job item-material' command.\n" - " workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90\n" - " Maintain 10-100 locally-made crafts of exceptional quality.\n" - ) - ); + "workflow", + "Manage repeat jobs according to stock levels.", + workflow_cmd)); commands.push_back(PluginCommand( "fix-job-postings", - "Fix broken job postings caused by certain versions of workflow", - fix_job_postings_cmd, false, - "fix-job-postings: Fix job postings\n" - "fix-job-postings dry|[any argument]: Dry run only (avoid making changes)\n" - )); + "Fix broken job postings caused by very old versions of workflow.", + fix_job_postings_cmd)); } init_state(out); From 26863fe4686bdfc2942ce9b7ed59cb73d1c5dc46 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 4 Aug 2022 00:18:06 -0700 Subject: [PATCH 212/334] Update zone.rst --- docs/plugins/zone.rst | 115 ------------------------------------------ 1 file changed, 115 deletions(-) diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index be827b47c..a3112c147 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -1,6 +1,3 @@ -.. _autobutcher: -.. _autonestbox: - zone ==== Helps a bit with managing activity zones (pens, pastures and pits) and cages. @@ -131,115 +128,3 @@ Examples ``zone tocages count 50 own tame male not grazer`` Stuff up to 50 owned tame male animals who are not grazers into cages built on the current default zone. - -autobutcher -=========== -Assigns lifestock for slaughter once it reaches a specific count. Requires that -you add the target race(s) to a watch list. Only tame units will be processed. - -Units will be ignored if they are: - -* Nicknamed (for custom protection; you can use the `rename` ``unit`` tool - individually, or `zone` ``nick`` for groups) -* Caged, if and only if the cage is defined as a room (to protect zoos) -* Trained for war or hunting - -Creatures who will not reproduce (because they're not interested in the -opposite sex or have been gelded) will be butchered before those who will. -Older adults and younger children will be butchered first if the population -is above the target (default 1 male, 5 female kids and adults). Note that -you may need to set a target above 1 to have a reliable breeding population -due to asexuality etc. See `fix-ster` if this is a problem. - -Options: - -:example: Print some usage examples. -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep : Changes the timer to sleep X frames between runs. -:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, - BIRD_TURKEY, etc) or a list of ids seperated by spaces or - the keyword 'all' which affects all races on your current - watchlist. -:unwatch R: Stop watching race(s). The current target settings will be - remembered. R can be a list of ids or the keyword 'all'. -:forget R: Stop watching race(s) and forget it's/their target settings. - R can be a list of ids or the keyword 'all'. -:autowatch: Automatically adds all new races (animals you buy from merchants, - tame yourself or get from migrants) to the watch list using - default target count. -:noautowatch: Stop auto-adding new races to the watchlist. -:list: Print the current status and watchlist. -:list_export: Print the commands needed to set up status and watchlist, - which can be used to import them to another save (see notes). -:target : - Set target count for specified race(s). The first four arguments - are the number of female and male kids, and female and male adults. - R can be a list of spceies ids, or the keyword ``all`` or ``new``. - ``R = 'all'``: change target count for all races on watchlist - and set the new default for the future. ``R = 'new'``: don't touch - current settings on the watchlist, only set the new default - for future entries. -:list_export: Print the commands required to rebuild your current settings. - -.. note:: - - Settings and watchlist are stored in the savegame, so that you can have - different settings for each save. If you want to copy your watchlist to - another savegame you must export the commands required to recreate your settings. - - To export, open an external terminal in the DF directory, and run - ``dfhack-run autobutcher list_export > filename.txt``. To import, load your - new save and run ``script filename.txt`` in the DFHack terminal. - - -Examples: - -You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, -1 male) of the race alpaca. Once the kids grow up the oldest adults will get -slaughtered. Excess kids will get slaughtered starting with the youngest -to allow that the older ones grow into adults. Any unnamed cats will -be slaughtered as soon as possible. :: - - autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY - autobutcher target 0 0 0 0 CAT - autobutcher watch ALPACA BIRD_TURKEY CAT - autobutcher start - -Automatically put all new races onto the watchlist and mark unnamed tame units -for slaughter as soon as they arrive in your fort. Settings already made -for specific races will be left untouched. :: - - autobutcher target 0 0 0 0 new - autobutcher autowatch - autobutcher start - -Stop watching the races alpaca and cat, but remember the target count -settings so that you can use 'unwatch' without the need to enter the -values again. Note: 'autobutcher unwatch all' works, but only makes sense -if you want to keep the plugin running with the 'autowatch' feature or manually -add some new races with 'watch'. If you simply want to stop it completely use -'autobutcher stop' instead. :: - - autobutcher unwatch ALPACA CAT - -autonestbox -=========== -Assigns unpastured female egg-layers to nestbox zones. Requires that you create -pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox -must be in the top left corner. Only 1 unit will be assigned per pen, regardless -of the size. The age of the units is currently not checked, most birds grow up -quite fast. Egglayers who are also grazers will be ignored, since confining them -to a 1x1 pasture is not a good idea. Only tame and domesticated own units are -processed since pasturing half-trained wild egglayers could destroy your neat -nestbox zones when they revert to wild. When called without options autonestbox -will instantly run once. - -Options: - -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep: Must be followed by number X. Changes the timer to sleep X - frames between runs. From 5e9dde8a61a8a409c56210106ad3617b7ccfe477 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 4 Aug 2022 00:22:07 -0700 Subject: [PATCH 213/334] Update autobutcher.rst --- docs/plugins/autobutcher.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index e77e13232..6bd9a02ce 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -37,10 +37,10 @@ Usage: Stop auto-adding new races to the watch list. ``autobutcher target all|new| [ ...]`` Set target counts for the specified races: - fk = number of female kids - mk = number of male kids - fa = number of female adults - ma = number of female adults + - fk = number of female kids + - mk = number of male kids + - fa = number of female adults + - ma = number of female adults If you specify ``all``, then this command will set the counts for all races on your current watchlist (including the races which are currenly set to 'unwatched') and sets the new default for future watch commands. If you @@ -171,5 +171,3 @@ add some new races with 'watch'. If you simply want to stop it completely use 'autobutcher stop' instead. :: autobutcher unwatch ALPACA CAT - -const string autobutcher_help = From 8f242ac5cc391fd6be141b45ea8c2640b9bc39cc Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 4 Aug 2022 00:24:36 -0700 Subject: [PATCH 214/334] Update tweak.rst --- docs/plugins/tweak.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index 56d465f78..fff1a4512 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -62,7 +62,7 @@ Commands that persist until disabled or DF quits: Adds an option to butcher units when viewing cages with :kbd:`q`. ``civ-view-agreement`` Fixes overlapping text on the "view agreement" screen. -``condition-material`` +``condition-material`` Fixes a crash in the work order contition material list (:bug:`9905`). ``craft-age-wear`` Fixes crafted items not wearing out over time (:bug:`6003`). With this From feed91d098354d7963f065195e328ed89af80549 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 07:34:32 +0000 Subject: [PATCH 215/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/plugins/petcapRemover.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index e3ce47916..3d9739ea3 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -29,4 +29,3 @@ Usage: ``petcapRemover pregtime `` Sets the pregnancy duration to the specified number of ticks. The default value is 200,000 ticks, which is the natural pet pregnancy duration. - From 46c3862a082c5d02013deed45db17902d4b24905 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 08:08:34 -0700 Subject: [PATCH 216/334] update docs for zone --- docs/plugins/zone.rst | 254 +++++++++++++++++++++++------------------- plugins/zone.cpp | 140 ++--------------------- 2 files changed, 150 insertions(+), 244 deletions(-) diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index a3112c147..5e01a12ac 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -1,130 +1,158 @@ zone ==== -Helps a bit with managing activity zones (pens, pastures and pits) and cages. - +Tags: :dfhack-keybind:`zone` -Options: - -:set: Set zone or cage under cursor as default for future assigns. -:assign: Assign unit(s) to the pen or pit marked with the 'set' command. - If no filters are set a unit must be selected in the in-game ui. - Can also be followed by a valid zone id which will be set - instead. -:unassign: Unassign selected creature from it's zone. -:nick: Mass-assign nicknames, must be followed by the name you want - to set. -:remnick: Mass-remove nicknames. -:enumnick: Assign enumerated nicknames (e.g. "Hen 1", "Hen 2"...). Must be - followed by the prefix to use in nicknames. -:tocages: Assign unit(s) to cages inside a pasture. -:uinfo: Print info about unit(s). If no filters are set a unit must - be selected in the in-game ui. -:zinfo: Print info about zone(s). If no filters are set zones under - the cursor are listed. -:verbose: Print some more info. -:filters: Print list of valid filter options. -:examples: Print some usage examples. -:not: Negates the next filter keyword. - -Filters: - -:all: Process all units (to be used with additional filters). -:count: Must be followed by a number. Process only n units (to be used - with additional filters). -:unassigned: Not assigned to zone, chain or built cage. -:minage: Minimum age. Must be followed by number. -:maxage: Maximum age. Must be followed by number. -:race: Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, - etc). Negatable. -:caged: In a built cage. Negatable. -:own: From own civilization. Negatable. -:merchant: Is a merchant / belongs to a merchant. Should only be used for - pitting, not for stealing animals (slaughter should work). -:war: Trained war creature. Negatable. -:hunting: Trained hunting creature. Negatable. -:tamed: Creature is tame. Negatable. -:trained: Creature is trained. Finds war/hunting creatures as well as - creatures who have a training level greater than 'domesticated'. - If you want to specifically search for war/hunting creatures use - 'war' or 'hunting' Negatable. -:trainablewar: Creature can be trained for war (and is not already trained for - war/hunt). Negatable. -:trainablehunt: Creature can be trained for hunting (and is not already trained - for war/hunt). Negatable. -:male: Creature is male. Negatable. -:female: Creature is female. Negatable. -:egglayer: Race lays eggs. Negatable. -:grazer: Race is a grazer. Negatable. -:milkable: Race is milkable. Negatable. +Manage activity zones, cages, and the animals therein. + +Usage: + +``enable zone`` + Add helpful filters to the pen/pasture sidebar menu (e.g. show only caged + grazers). +``zone set`` + Set zone or cage under cursor as default for future ``assign`` or ``tocages`` + commands. +``zone assign [] []`` + Assign unit(s) to the zone with the given ID, or to the most recent pen or + pit marked with the ``set`` command. If no filters are set, then a unit must + be selected in the in-game ui. +``zone unassign []`` + Unassign selected creature from its zone. +``zone nick []`` + Assign the given nickname to the selected animal or the animals matched by + the given filter. +``zone remnick []`` + Remove nicknames from the selected animal or the animals matched by the + given filter. +``zone enumnick []`` + Assign enumerated nicknames (e.g. "Hen 1", "Hen 2"...). +``zone tocages []`` + Assign unit(s) to cages that have been built inside the pasture selected + with the ``set`` command. +``zone uinfo []`` + Print info about unit(s). If no filters are set, then a unit must be + selected in the in-game ui. +``zone zinfo`` + Print info about the zone(s) and any buildings under the cursor. + +Examples +-------- + +Before any ``assign`` or ``tocages`` examples can be used, you must first move +the cursor over a pen/pasture or pit zone and run ``zone set`` to select the +zone. + +``zone assign all own ALPACA minage 3 maxage 10`` + Assign all of your alpacas who are between 3 and 10 years old to the + selected pasture. +``zone assign all own caged grazer nick ineedgrass`` + Assign all of your grazers who are sitting in cages on stockpiles (e.g. + after buying them from merchants) to the selected pasture and give them the + nickname 'ineedgrass'. +``zone assign all own not grazer not race CAT`` + Assign all of your animals who are not grazers (excluding cats) to the + selected pasture. + " zone assign all own milkable not grazer\n" +``zone assign all own female milkable not grazer`` + Assign all of your non-grazing milkable creatures to the selected pasture or + cage. +``zone assign all own race DWARF maxage 2`` + Throw all useless kids into a pit :) They'll be fine I'm sure. +``zone nick donttouchme`` + Nicknames all units in the current default zone or cage to 'donttouchme'. + This is especially useful for protecting a group of animals assigned to a + pasture or cage from being "processed" by `autobutcher`. +``zone tocages count 50 own tame male not grazer`` + Stuff up to 50 of your tame male animals who are not grazers into cages + built on the current default zone. + +Filters +------- + +:all: Process all units. +:count : Process only up to n units. +:unassigned: Not assigned to zone, chain or built cage. +:minage : Minimum age. Must be followed by number. +:maxage : Maximum age. Must be followed by number. +:not: Negates the next filter keyword. All of the keywords documented + below are negatable. +:race: Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, + etc). +:caged: In a built cage. +:own: From own civilization. You'll usually want to include this + filter. +:war: Trained war creature. +:hunting: Trained hunting creature. +:tamed: Creature is tame. +:trained: Creature is trained. Finds war/hunting creatures as well as + creatures who have a training level greater than 'domesticated'. + If you want to specifically search for war/hunting creatures + use ``war`` or ``hunting``. +:trainablewar: Creature can be trained for war (and is not already trained for + war/hunt). +:trainablehunt: Creature can be trained for hunting (and is not already trained + for war/hunt). +:male: Creature is male. +:female: Creature is female. +:egglayer: Race lays eggs. If you want units who actually lay eggs, also + specify ``female``. +:grazer: Race is a grazer. +:milkable: Race is milkable. If you want units who actually can be milked, + also specify ``female``. +:merchant: Is a merchant / belongs to a merchant. Should only be used for + pitting or slaughtering, not for stealing animals. Usage with single units ----------------------- -One convenient way to use the zone tool is to bind the command 'zone assign' to -a hotkey, maybe also the command 'zone set'. Place the in-game cursor over -a pen/pasture or pit, use 'zone set' to mark it. Then you can select units -on the map (in 'v' or 'k' mode), in the unit list or from inside cages -and use 'zone assign' to assign them to their new home. Allows pitting your -own dwarves, by the way. - -Usage with filters ------------------- -All filters can be used together with the 'assign' command. - -Restrictions: It's not possible to assign units who are inside built cages -or chained because in most cases that won't be desirable anyways. -It's not possible to cage owned pets because in that case the owner +One convenient way to use the zone tool is to bind the commands ``zone assign`` +and ``zone set`` to hotkeys. Place the in-game cursor over a pen/pasture or pit +and use the ``zone set`` hotkey to mark it. Then you can select units on the map +(in 'v' or 'k' mode), in the unit list or from inside cages and use the +``zone assign`` hotkey to assign them to their new home. Allows pitting your own +dwarves, by the way. + +Matching with filters +--------------------- +All filters can be used together with the ``assign`` and ``tocages`` commands. + +Note that it's not possible to reassign units who are inside built cages or +chained, though this likely won't matter because if you have gone to the trouble +of creating a zoo or chaining a creature, you probably wouldn't want them +reassigned anyways. Also, ``zone`` will avoid caging owned pets because the owner uncages them after a while which results in infinite hauling back and forth. -Usually you should always use the filter 'own' (which implies tame) unless you -want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless -you specify 'race DWARF' (so it's safe to use 'assign all own' to one big -pasture if you want to have all your animals at the same place). 'egglayer' and -'milkable' should be used together with 'female' unless you have a mod with -egg-laying male elves who give milk or whatever. Merchants and their animals are -ignored unless you specify 'merchant' (pitting them should be no problem, -but stealing and pasturing their animals is not a good idea since currently they -are not properly added to your own stocks; slaughtering them should work). +Most filters should include an ``own`` element (which implies ``tame``) unless +you want to use ``zone assign`` for pitting hostiles. The ``own`` filter ignores +dwarves unless you explicitly specify ``race DWARF`` (so it's safe to use +``assign all own`` to one big pasture if you want to have all your animals in +the same place). -Most filters can be negated (e.g. 'not grazer' -> race is not a grazer). +The ``egglayer`` and ``milkable`` filters should be used together with +``female`` unless you want the males of the race included. Merchants and their +animals are ignored unless you specify ``merchant`` (pitting them should be no +problem, but stealing and pasturing their animals is not a good idea since +currently they are not properly added to your own stocks; slaughtering them +should work). + +Most filters can be negated (e.g. ``not grazer`` -> race is not a grazer). Mass-renaming ------------- -Using the 'nick' command you can set the same nickname for multiple units. -If used without 'assign', 'all' or 'count' it will rename all units in the -current default target zone. Combined with 'assign', 'all' or 'count' (and -further optional filters) it will rename units matching the filter conditions. + +Using the ``nick`` command, you can set the same nickname for multiple units. +If used without ``assign``, ``all``, or ``count``, it will rename all units in +the current default target zone. Combined with ``assign``, ``all``, or ``count`` +(and likely further optional filters) it will rename units matching the filter +conditions. Cage zones ---------- -Using the 'tocages' command you can assign units to a set of cages, for example -a room next to your butcher shop(s). They will be spread evenly among available -cages to optimize hauling to and butchering from them. For this to work you need -to build cages and then place one pen/pasture activity zone above them, covering -all cages you want to use. Then use 'zone set' (like with 'assign') and use -'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it -would make no sense, but can be used together with 'nick' or 'remnick' and all -the usual filters. -Examples --------- -``zone assign all own ALPACA minage 3 maxage 10`` - Assign all own alpacas who are between 3 and 10 years old to the selected - pasture. -``zone assign all own caged grazer nick ineedgrass`` - Assign all own grazers who are sitting in cages on stockpiles (e.g. after - buying them from merchants) to the selected pasture and give them - the nickname 'ineedgrass'. -``zone assign all own not grazer not race CAT`` - Assign all own animals who are not grazers, excluding cats. -``zone assign count 5 own female milkable`` - Assign up to 5 own female milkable creatures to the selected pasture. -``zone assign all own race DWARF maxage 2`` - Throw all useless kids into a pit :) -``zone nick donttouchme`` - Nicknames all units in the current default zone or cage to 'donttouchme'. - Mostly intended to be used for special pastures or cages which are not marked - as rooms you want to protect from autobutcher. -``zone tocages count 50 own tame male not grazer`` - Stuff up to 50 owned tame male animals who are not grazers into cages built - on the current default zone. +The ``tocages`` command assigns units to a set of cages, for example a room next +to your butcher shop(s). Units will be spread evenly among available cages to +optimize hauling to and butchering from them. For this to work you need to build +cages and then place one pen/pasture activity zone above them, covering all +cages you want to use. Then use ``zone set`` (like with ``assign``) and run +``zone tocages ``. ``tocages`` can be used together with ``nick`` or +``remnick`` to adjust nicknames while assigning to cages. diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 60f1f2b61..a3a0f2431 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -66,105 +66,6 @@ REQUIRE_GLOBAL(ui_building_in_assign); REQUIRE_GLOBAL(ui_menu_width); REQUIRE_GLOBAL(world); -static command_result df_zone (color_ostream &out, vector & parameters); - -const string zone_help = - "Allows easier management of pens/pastures, pits and cages.\n" - "Commands:\n" - " help - print this help message\n" - " filters - print list of supported filters\n" - " examples - print some usage examples\n" - " set - set zone under cursor as default for future assigns\n" - " assign - assign creature(s) to a pen or pit\n" - " if no filters are used, a single unit must be selected.\n" - " can be followed by valid building id which will then be set.\n" - " building must be a pen/pasture, pit or cage.\n" - " slaughter - mark creature(s) for slaughter\n" - " if no filters are used, a single unit must be selected.\n" - " with filters named units are ignored unless specified.\n" - " unassign - unassign selected creature(s) from zone or cage\n" - " nick - give unit(s) nicknames (e.g. all units in a cage)\n" - " enumnick - give unit(s) enumerated nicknames (e.g Hen 1, Hen 2)\n" - " remnick - remove nicknames\n" - " tocages - assign to (multiple) built cages inside a pen/pasture\n" - " spreads creatures evenly among cages for faster hauling.\n" - " uinfo - print info about selected units\n" - " zinfo - print info about zone(s) under cursor\n" - "Options:\n" - " verbose - print some more info, mostly useless debug stuff\n" - ; - -const string zone_help_filters = - "Filters (to be used in combination with 'all' or 'count'):\n" - "Required (one of):\n" - " all - process all units\n" - " should be used in combination with further filters\n" - " count - must be followed by number. process X units\n" - " should be used in combination with further filters\n" - "Others (may be used with 'not' prefix):\n" - " age - exact age. must be followed by number\n" - " caged - in a built cage\n" - " egglayer - race lays eggs (use together with 'female')\n" - " female - obvious\n" - " grazer - is a grazer\n" - " male - obvious\n" - " maxage - maximum age. must be followed by number\n" - " merchant - is a merchant / belongs to a merchant\n" - " can be used to pit merchants and slaughter their animals\n" - " (could have weird effects during trading, be careful)\n" - " ('not merchant' is set by default)\n" - " milkable - race is milkable (use together with 'female')\n" - " minage - minimum age. must be followed by number\n" - " named - has name or nickname\n" - " ('not named' is set by default when using the 'slaughter' command)\n" - " own - from own civilization\n" - " race - must be followed by a race raw id (e.g. BIRD_TURKEY)\n" - " tame - tamed\n" - " trainablehunt- can be trained for hunting (and is not already trained)\n" - " trainablewar - can be trained for war (and is not already trained)\n" - " trained - obvious\n" - " unassigned - not assigned to zone, chain or built cage\n" - " war - trained war creature\n" - ; - -const string zone_help_examples = - "Example for assigning single units:\n" - " (ingame) move cursor to a pen/pasture or pit zone\n" - " (dfhack) 'zone set' to use this zone for future assignments\n" - " (dfhack) map 'zone assign' to a hotkey of your choice\n" - " (ingame) select unit with 'v', 'k' or from unit list or inside a cage\n" - " (ingame) press hotkey to assign unit to it's new home (or pit)\n" - "Examples for assigning with filters:\n" - " (this assumes you have already set up a target zone)\n" - " zone assign all own grazer maxage 10\n" - " zone assign all own milkable not grazer\n" - " zone assign count 5 own female milkable\n" - " zone assign all own race DWARF maxage 2\n" - " throw all useless kids into a pit :)\n" - "Notes:\n" - " Unassigning per filters ignores built cages and chains currently. Usually you\n" - " should always use the filter 'own' (which implies tame) unless you want to\n" - " use the zone tool for pitting hostiles. 'own' ignores own dwarves unless you\n" - " specify 'race DWARF' and it ignores merchants and their animals unless you\n" - " specify 'merchant' (so it's safe to use 'assign all own' to one big pasture\n" - " if you want to have all your animals at the same place).\n" - " 'egglayer' and 'milkable' should be used together with 'female'\n" - " well, unless you have a mod with egg-laying male elves who give milk...\n"; - - -/////////////// -// Various small tool functions -// probably many of these should be moved to Unit.h and Building.h sometime later... - -// static df::general_ref_building_civzone_assignedst * createCivzoneRef(); -// static bool unassignUnitFromBuilding(df::unit* unit); -// static command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose); -// static void unitInfo(color_ostream & out, df::unit* creature, bool verbose); -// static void zoneInfo(color_ostream & out, df::building* building, bool verbose); -// static void cageInfo(color_ostream & out, df::building* building, bool verbose); -// static void chainInfo(color_ostream & out, df::building* building, bool verbose); -static bool isInBuiltCageRoom(df::unit*); - static void doMarkForSlaughter(df::unit* unit) { unit->flags2.bits.slaughter = 1; @@ -184,6 +85,8 @@ static bool hasValidMapPos(df::unit* unit) return false; } +static bool isInBuiltCageRoom(df::unit*); + // dump some unit info static void unitInfo(color_ostream & out, df::unit* unit, bool verbose = false) { @@ -1127,8 +1030,7 @@ static struct zone_param_filters_init { zone_param_filters_init() { zone_param_filters["maxage"] = make_pair(1, createMaxAgeFilter); }} zone_param_filters_init_; -static command_result df_zone (color_ostream &out, vector & parameters) -{ +static command_result df_zone(color_ostream &out, vector & parameters) { CoreSuspender suspend; if (!Maps::IsValid()) @@ -1160,18 +1062,7 @@ static command_result df_zone (color_ostream &out, vector & parameters) if (p0 == "help" || p0 == "?") { - out << zone_help << endl; - return CR_OK; - } - if (p0 == "filters") - { - out << zone_help_filters << endl; - return CR_OK; - } - if (p0 == "examples") - { - out << zone_help_examples << endl; - return CR_OK; + return CR_WRONG_USAGE; } else if(p0 == "zinfo") { @@ -2275,13 +2166,8 @@ IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, render); //END zone filters -DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; - - if (enable != is_enabled) - { +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (enable != is_enabled) { if (!INTERPOSE_HOOK(zone_hook, feed).apply(enable) || !INTERPOSE_HOOK(zone_hook, render).apply(enable)) return CR_FAILURE; @@ -2292,18 +2178,10 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) return CR_OK; } -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( "zone", - "manage activity zones.", - df_zone, - false, - zone_help.c_str())); - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ + "Manage activity zones.", + df_zone)); return CR_OK; } From 6f48c1f4d063cef6a978b6cece81a7076e530cb7 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 08:08:47 -0700 Subject: [PATCH 217/334] remove some cruft from autobutcher docs --- docs/plugins/autobutcher.rst | 86 +++++------------------------------- 1 file changed, 11 insertions(+), 75 deletions(-) diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index 6bd9a02ce..a6b69338c 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -67,8 +67,6 @@ Usage: default command if autobutcher is run without parameters. ``autobutcher list_export`` Print commands required to set the current settings in another fort. - Useful to run form dfhack-run like: - ``dfhack-run autobutcher list_export > autobutcher.script`` To see a list of all races, run this command: @@ -76,6 +74,16 @@ To see a list of all races, run this command: Though not all the races listed there are tameable/butcherable. +.. note:: + + Settings and watchlist are stored in the savegame, so you can have different + settings for each save. If you want to copy your watchlist to another, + savegame, you can export the commands required to recreate your settings. + + To export, open an external terminal in the DF directory, and run + ``dfhack-run autobutcher list_export > filename.txt``. To import, load your + new save and run ``script filename.txt`` in the DFHack terminal. + Examples -------- @@ -91,6 +99,7 @@ alpacas, sheep, and llamas (for wool), and pigs (for milk and meat). All other unnamed tame units will be marked for slaughter as soon as they arrive in your fortress:: + enable autobutcher autobutcher target 2 2 2 2 DOG autobutcher target 1 1 2 2 CAT autobutcher target 50 50 14 2 BIRD_GOOSE @@ -98,76 +107,3 @@ fortress:: autobutcher target 5 5 6 2 PIG autobutcher target 0 0 0 0 new autobutcher autowatch - -Options: - -:example: Print some usage examples. -:start: Start running every X frames (df simulation ticks). - Default: X=6000, which would be every 60 seconds at 100fps. -:stop: Stop running automatically. -:sleep : Changes the timer to sleep X frames between runs. -:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, - BIRD_TURKEY, etc) or a list of ids seperated by spaces or - the keyword 'all' which affects all races on your current - watchlist. -:unwatch R: Stop watching race(s). The current target settings will be - remembered. R can be a list of ids or the keyword 'all'. -:forget R: Stop watching race(s) and forget it's/their target settings. - R can be a list of ids or the keyword 'all'. -:autowatch: Automatically adds all new races (animals you buy from merchants, - tame yourself or get from migrants) to the watch list using - default target count. -:noautowatch: Stop auto-adding new races to the watchlist. -:list: Print the current status and watchlist. -:list_export: Print the commands needed to set up status and watchlist, - which can be used to import them to another save (see notes). -:target : - Set target count for specified race(s). The first four arguments - are the number of female and male kids, and female and male adults. - R can be a list of spceies ids, or the keyword ``all`` or ``new``. - ``R = 'all'``: change target count for all races on watchlist - and set the new default for the future. ``R = 'new'``: don't touch - current settings on the watchlist, only set the new default - for future entries. -:list_export: Print the commands required to rebuild your current settings. - -.. note:: - - Settings and watchlist are stored in the savegame, so that you can have - different settings for each save. If you want to copy your watchlist to - another savegame you must export the commands required to recreate your settings. - - To export, open an external terminal in the DF directory, and run - ``dfhack-run autobutcher list_export > filename.txt``. To import, load your - new save and run ``script filename.txt`` in the DFHack terminal. - - -Examples: - -You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, -1 male) of the race alpaca. Once the kids grow up the oldest adults will get -slaughtered. Excess kids will get slaughtered starting with the youngest -to allow that the older ones grow into adults. Any unnamed cats will -be slaughtered as soon as possible. :: - - autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY - autobutcher target 0 0 0 0 CAT - autobutcher watch ALPACA BIRD_TURKEY CAT - autobutcher start - -Automatically put all new races onto the watchlist and mark unnamed tame units -for slaughter as soon as they arrive in your fort. Settings already made -for specific races will be left untouched. :: - - autobutcher target 0 0 0 0 new - autobutcher autowatch - autobutcher start - -Stop watching the races alpaca and cat, but remember the target count -settings so that you can use 'unwatch' without the need to enter the -values again. Note: 'autobutcher unwatch all' works, but only makes sense -if you want to keep the plugin running with the 'autowatch' feature or manually -add some new races with 'watch'. If you simply want to stop it completely use -'autobutcher stop' instead. :: - - autobutcher unwatch ALPACA CAT From ebfe00b112a69fdea0ea9a50116e5529f1e620db Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 10:08:23 -0700 Subject: [PATCH 218/334] editing pass of short descriptions and fix some short description parsing --- docs/plugins/jobutils.rst | 2 ++ docs/plugins/pathable.rst | 3 ++- docs/plugins/prospector.rst | 4 ++-- docs/plugins/resume.rst | 2 +- docs/plugins/search.rst | 7 ++++--- docs/plugins/xlsxreader.rst | 2 +- library/lua/helpdb.lua | 29 +++++++++++++++++++---------- 7 files changed, 31 insertions(+), 18 deletions(-) diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index e8434abdc..2ecf2b591 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -7,6 +7,8 @@ Tags: :dfhack-keybind:`job-duplicate` :dfhack-keybind:`job-material` +Inspect or modify details of workshop jobs. + Usage: ``job`` diff --git a/docs/plugins/pathable.rst b/docs/plugins/pathable.rst index daf7697bc..7a906ad89 100644 --- a/docs/plugins/pathable.rst +++ b/docs/plugins/pathable.rst @@ -1,6 +1,7 @@ pathable ======== -Provides a Lua API for marking tiles that are reachable from the cursor. +Marks tiles that are reachable from the cursor. This plugin provides a Lua API, +but no direct commands. See `pathable-api` for details. diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 95436abec..718f92fa6 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -5,8 +5,8 @@ prospector Tags: :dfhack-keybind:`prospect` -Shows a summary of resources that exist on the map or an estimate of resources -available in the selected embark area. +Shows a summary of resources that exist on the map. It can also calculate an +estimate of resources available in the selected embark area. Usage:: diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index d8feb0896..7afb9be0d 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -1,7 +1,7 @@ resume ====== Tags: -:dfhack-keybind:`` +:dfhack-keybind:`resume` Color planned buildings based on their suspend status. When enabled, this plugin will display a colored 'X' over suspended buildings. When run as a command, it diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index c8f5d68b3..edf6fb47c 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -4,9 +4,10 @@ search ====== Tags: -The search plugin adds search capabilities to the Stocks, Animals, Trading, -Stockpile, Noble (assignment candidates), Military (position candidates), -Burrows (unit list), Rooms, Announcements, Job List, and Unit List screens. +Adds search capabilities to the UI. The Stocks, Animals, Trading, Stockpile, +Noble (assignment candidates), Military (position candidates), Burrows (unit +list), Rooms, Announcements, Job List, and Unit List screens all get hotkeys +that allow you to dynamically filter the displayed lists. Usage:: diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst index 6429ceedc..d09b69644 100644 --- a/docs/plugins/xlsxreader.rst +++ b/docs/plugins/xlsxreader.rst @@ -1,6 +1,6 @@ xlsxreader ========== -Provides a Lua API for reading .xlsx files. +Provides a Lua API for reading xlsx files. See `xlsxreader-api` for details. diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index cf8e86af5..a23002e57 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -142,11 +142,11 @@ end -- value is the comment character.) local function update_entry(entry, iterator, opts) opts = opts or {} - local lines = {} + local lines, tags = {}, '' local first_line_is_short_help = opts.first_line_is_short_help local begin_marker_found,header_found = not opts.begin_marker,opts.no_header local tags_found, short_help_found = false, opts.skip_short_help - local in_short_help = false + local in_tags, in_keybinding, in_short_help = false, false, false for line in iterator do if not short_help_found and first_line_is_short_help then line = line:trim() @@ -179,16 +179,21 @@ local function update_entry(entry, iterator, opts) end if not header_found and line:find('%w') then header_found = true - elseif not tags_found and line:find('^[*]*Tags:[*]* [%w, ]+$') then - local _,_,tags = line:trim():find('[*]*Tags:[*]* *(.*)') - entry.tags = {} - for _,tag in ipairs(tags:split('[ ,]+')) do - entry.tags[tag] = true + elseif in_tags then + if #line == 0 then + in_tags = false + else + tags = tags .. line end - tags_found = true + elseif not tags_found and line:find('^[*]*Tags:[*]*') then + _,_,tags = line:trim():find('[*]*Tags:[*]* *(.*)') + in_tags, tags_found = true, true + elseif in_keybinding then + if #line == 0 then in_keybinding = false end + elseif line:find('^[*]*Keybinding:') then + in_keybinding = true elseif not short_help_found and - not line:find('^[*]*Keybinding:') and - line:find('%w') then + line:find('^%w') then if in_short_help then entry.short_help = entry.short_help .. ' ' .. line else @@ -205,6 +210,10 @@ local function update_entry(entry, iterator, opts) table.insert(lines, line) ::continue:: end + entry.tags = {} + for _,tag in ipairs(tags:split('[ ,]+')) do + entry.tags[tag] = true + end if #lines > 0 then entry.long_help = table.concat(lines, '\n') end From f7acc5cfc6a28e72fa4ab50e5d63042a5cf96e6c Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 17:55:33 -0700 Subject: [PATCH 219/334] sync tags spreadsheet to git spreadsheet - https://docs.google.com/spreadsheets/d/1hiDlo8M_bB_1jE-5HRs2RrrA_VZ4cRu9VXaTctX_nwk/edit#gid=170388995 sync command - for fname in *rst; do name=$(echo $fname | sed 's/[.]rst//'); tagline=$(egrep ",$name," ~/Downloads/DFHack\ taxonomy\ -\ Tool\ tags.csv | ~/Downloads/csvtotags.sh); sed -ri "s;[*]*Tags:.*;$tagline;" $fname; done contents of csvtotags.sh - fgrep . | sed -r 's/^[^,]+,([^,]+),[^.]+[.]"?,/\1,/' | awk -F, ' function tag(idx, tagname) { if ($idx == "TRUE") { if (hastag == 1) {printf(", ")} printf("`tag/%s`", tagname) hastag = 1 } } { printf("%s", "**Tags:** ") hastag = 0 tag(2, "adventure") tag(3, "fort") tag(4, "legends") tag(5, "embark") tag(6, "system") tag(7, "dev") tag(8, "auto") tag(9, "productivity") tag(10, "inspection") tag(11, "design") tag(12, "quickfort") tag(13, "interface") tag(14, "fps") tag(15, "fix") tag(16, "mod") tag(17, "armok") tag(18, "animals") tag(19, "buildings") tag(20, "items") tag(21, "jobs") tag(22, "map") tag(23, "labors") tag(24, "units") tag(25, "stockpiles") tag(26, "trees") printf("\n") } ' --- docs/Tags.rst | 29 +++++++++++++-------- docs/builtins/alias.rst | 2 +- docs/builtins/cls.rst | 2 +- docs/builtins/die.rst | 2 +- docs/builtins/disable.rst | 2 +- docs/builtins/enable.rst | 2 +- docs/builtins/fpause.rst | 2 +- docs/builtins/help.rst | 2 +- docs/builtins/hide.rst | 2 +- docs/builtins/keybinding.rst | 2 +- docs/builtins/kill-lua.rst | 2 +- docs/builtins/load.rst | 2 +- docs/builtins/ls.rst | 2 +- docs/builtins/plug.rst | 2 +- docs/builtins/reload.rst | 2 +- docs/builtins/sc-script.rst | 2 +- docs/builtins/script.rst | 2 +- docs/builtins/show.rst | 2 +- docs/builtins/tags.rst | 2 +- docs/builtins/type.rst | 2 +- docs/builtins/unload.rst | 2 +- docs/plugins/3dveins.rst | 2 +- docs/plugins/RemoteFortressReader.rst | 2 +- docs/plugins/add-spatter.rst | 2 +- docs/plugins/autobutcher.rst | 2 +- docs/plugins/autochop.rst | 2 +- docs/plugins/autoclothing.rst | 2 +- docs/plugins/autodump.rst | 2 +- docs/plugins/autofarm.rst | 2 +- docs/plugins/autogems.rst | 2 +- docs/plugins/autohauler.rst | 2 +- docs/plugins/autolabor.rst | 2 +- docs/plugins/automaterial.rst | 2 +- docs/plugins/automelt.rst | 2 +- docs/plugins/autonestbox.rst | 2 +- docs/plugins/autotrade.rst | 2 +- docs/plugins/blueprint.rst | 2 +- docs/plugins/building-hacks.rst | 1 + docs/plugins/buildingplan.rst | 2 +- docs/plugins/burrows.rst | 2 +- docs/plugins/changeitem.rst | 2 +- docs/plugins/changelayer.rst | 2 +- docs/plugins/changevein.rst | 2 +- docs/plugins/cleanconst.rst | 2 +- docs/plugins/cleaners.rst | 2 +- docs/plugins/cleanowned.rst | 2 +- docs/plugins/command-prompt.rst | 2 +- docs/plugins/confirm.rst | 2 +- docs/plugins/createitem.rst | 2 +- docs/plugins/cursecheck.rst | 2 +- docs/plugins/cxxrandom.rst | 1 + docs/plugins/debug.rst | 2 +- docs/plugins/deramp.rst | 2 +- docs/plugins/dig-now.rst | 2 +- docs/plugins/dig.rst | 2 +- docs/plugins/digFlood.rst | 2 +- docs/plugins/diggingInvaders.rst | 2 +- docs/plugins/dwarfmonitor.rst | 2 +- docs/plugins/dwarfvet.rst | 2 +- docs/plugins/embark-assistant.rst | 2 +- docs/plugins/embark-tools.rst | 2 +- docs/plugins/eventful.rst | 1 + docs/plugins/fastdwarf.rst | 2 +- docs/plugins/filltraffic.rst | 2 +- docs/plugins/fix-unit-occupancy.rst | 2 +- docs/plugins/fixveins.rst | 2 +- docs/plugins/flows.rst | 2 +- docs/plugins/follow.rst | 2 +- docs/plugins/forceequip.rst | 2 +- docs/plugins/generated-creature-renamer.rst | 2 +- docs/plugins/getplants.rst | 2 +- docs/plugins/hotkeys.rst | 2 +- docs/plugins/infiniteSky.rst | 2 +- docs/plugins/isoworldremote.rst | 2 +- docs/plugins/jobutils.rst | 2 +- docs/plugins/labormanager.rst | 2 +- docs/plugins/lair.rst | 2 +- docs/plugins/liquids.rst | 2 +- docs/plugins/luasocket.rst | 1 + docs/plugins/manipulator.rst | 2 +- docs/plugins/map-render.rst | 1 + docs/plugins/misery.rst | 2 +- docs/plugins/mode.rst | 2 +- docs/plugins/mousequery.rst | 2 +- docs/plugins/nestboxes.rst | 2 +- docs/plugins/orders.rst | 2 +- docs/plugins/pathable.rst | 1 + docs/plugins/petcapRemover.rst | 2 +- docs/plugins/plants.rst | 2 +- docs/plugins/power-meter.rst | 2 +- docs/plugins/probe.rst | 2 +- docs/plugins/prospector.rst | 2 +- docs/plugins/regrass.rst | 2 +- docs/plugins/rename.rst | 2 +- docs/plugins/rendermax.rst | 2 +- docs/plugins/resume.rst | 2 +- docs/plugins/reveal.rst | 2 +- docs/plugins/ruby.rst | 2 +- docs/plugins/search.rst | 2 +- docs/plugins/seedwatch.rst | 2 +- docs/plugins/showmood.rst | 2 +- docs/plugins/siege-engine.rst | 2 +- docs/plugins/sort.rst | 2 +- docs/plugins/spectate.rst | 2 +- docs/plugins/steam-engine.rst | 2 +- docs/plugins/stockflow.rst | 2 +- docs/plugins/stockpiles.rst | 2 +- docs/plugins/stocks.rst | 2 +- docs/plugins/stonesense.rst | 2 +- docs/plugins/strangemood.rst | 2 +- docs/plugins/tailor.rst | 2 +- docs/plugins/tiletypes.rst | 2 +- docs/plugins/title-folder.rst | 2 +- docs/plugins/title-version.rst | 2 +- docs/plugins/trackstop.rst | 2 +- docs/plugins/tubefill.rst | 2 +- docs/plugins/tweak.rst | 2 +- docs/plugins/workNow.rst | 2 +- docs/plugins/workflow.rst | 2 +- docs/plugins/xlsxreader.rst | 1 + docs/plugins/zone.rst | 2 +- 121 files changed, 138 insertions(+), 124 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 294358bd8..d0c785c3c 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,20 +1,27 @@ :orphan: -- `tag/adventure`: Tools that are useful while in adventure mode. +- `tag/adventure`: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! - `tag/fort`: Tools that are useful while in fort mode. - `tag/legends`: Tools that are useful while in legends mode. -- `tag/items`: Tools that create or modify in-game items. -- `tag/units`: Tools that create or modify units. -- `tag/jobs`: Tools that create or modify jobs. -- `tag/labors`: Tools that deal with labor assignment. -- `tag/auto`: Tools that automatically manage some aspect of your fortress. -- `tag/map`: Map modification. +- `tag/embark`: Tools that are useful while on the embark screen. - `tag/system`: Tools related to working with DFHack commands or the core DFHack library. +- `tag/dev`: Tools useful for develpers and modders. +- `tag/auto`: Tools that you turn on and then they automatically manage some aspect of your fortress. - `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. -- `tag/animals`: Tools that help you manage animals. -- `tag/fix`: Tools that fix specific bugs. - `tag/inspection`: Tools that let you inspect game data. -- `tag/buildings`: Tools that help you work wtih placing or configuring buildings and furniture. +- `tag/design`: Tools that help you design your fort. - `tag/quickfort`: Tools that are involved in creating and playing back blueprints. -- `tag/dev`: Tools useful for develpers and modders. - `tag/interface`: Tools that modify or extend the user interface. +- `tag/fps`: Tools that help you manage FPS drop. +- `tag/fix`: Tools that fix specific bugs. +- `tag/mod`: Tools that introduce new gameplay elements. +- `tag/armok`: Tools that give you complete control over various aspects of the game. +- `tag/animals`: Tools that help you manage animals. +- `tag/buildings`: Tools that help you work with placing or configuring buildings and furniture. +- `tag/items`: Tools that create or modify in-game items. +- `tag/jobs`: Tools that create or modify jobs. +- `tag/map`: Map modification. +- `tag/labors`: Tools that deal with labor assignment. +- `tag/units`: Tools that create or modify units. +- `tag/stockpiles`: Tools that interact wtih stockpiles. +- `tag/trees`: Tools that interact with trees and shrubs. diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 96f088798..6efaf0776 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,6 +1,6 @@ alias ===== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`alias` :index:`Configure helper aliases for other DFHack commands. diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index a0478c8df..d6a245081 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,6 +1,6 @@ cls === -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`cls` :index:`Clear the terminal screen. ` Can also diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index 54a134433..db487da0e 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,6 +1,6 @@ die === -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`die` :index:`Instantly exit DF without saving. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 567e9dbb1..758b6ff8f 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,6 +1,6 @@ disable ======= -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`disable` :index:`Deactivate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index b99dd194a..8705bc4ef 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,6 +1,6 @@ enable ====== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`enable` :index:`Activate a DFHack tool that has some persistent effect. diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index c43cedfab..beb783e82 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,6 +1,6 @@ fpause ====== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`fpause` :index:`Forces DF to pause. ` This is useful when diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 957a84466..4195f2eaa 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,6 +1,6 @@ help ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`help` :index:`Display help about a command or plugin. diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index de144b637..087d87726 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,6 +1,6 @@ hide ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`hide` :index:`Hide the DFHack terminal window. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index ad759b14c..fd857a42b 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,6 +1,6 @@ keybinding ========== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`keybinding` :index:`Create hotkeys that will run DFHack commands. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 222795db3..129268fa8 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,6 +1,6 @@ kill-lua ======== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`kill-lua` :index:`Gracefully stops any currently-running Lua scripts. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index b7e3db8f3..cd3aed2c0 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,6 +1,6 @@ load ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`load` :index:`Load and register a plugin library. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 3ae3fb3e1..f03e3e533 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,6 +1,6 @@ ls == -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`ls` :index:`List available DFHack commands. ` diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 963660bbb..76e31c432 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,6 +1,6 @@ plug ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`plug` :index:`List available plugins and whether they are enabled. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index 4417b5185..8449c713d 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,6 +1,6 @@ reload ====== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`reload` :index:`Reload a loaded plugin. ` Developers diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index e19af0bf9..17c0efe25 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,6 +1,6 @@ sc-script ========= -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`sc-script` :index:`Run commands when game state changes occur. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index f15e86bec..e5576ed50 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,6 +1,6 @@ script ====== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`script` :index:`Execute a batch file of DFHack commands. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index a173231d7..fde642726 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,6 +1,6 @@ show ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`show` :index:`Unhides the DFHack terminal window. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index 33ac36fe2..024677cff 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,6 +1,6 @@ tags ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`tags` :index:`List the strings that DFHack tools can be tagged with. diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 7c446bc4e..d7cf9588e 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,6 +1,6 @@ type ==== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`type` :index:`Describe how a command is implemented. diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index ec385489f..e79d624ec 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,6 +1,6 @@ unload ====== -Tags: `tag/system` +**Tags:** `tag/system` :dfhack-keybind:`unload` :index:`Unload a plugin from memory. ` diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 9fe83a4b5..680d44c6e 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,6 +1,6 @@ 3dveins ======= -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/map` :dfhack-keybind:`3dveins` :index:`Rewrite layer veins to expand in 3D space. diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index f3bf505ac..da8c3570e 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -1,6 +1,6 @@ RemoteFortressReader ==================== -Tags: +**Tags:** `tag/dev` :dfhack-keybind:`RemoteFortressReader_version` :dfhack-keybind:`load-art-image-chunk` diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 6e5a8ddb7..ed3675132 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -1,6 +1,6 @@ add-spatter =========== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/mod`, `tag/items` :index:`Make tagged reactions produce contaminants. ` Give some use to all diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index a6b69338c..7a960012e 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -1,6 +1,6 @@ autobutcher =========== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/fps`, `tag/animals` :dfhack-keybind:`autobutcher` Automatically butcher excess livestock. This plugin monitors how many pets you diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 6893644db..6b0b7125e 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -1,6 +1,6 @@ autochop ======== -Tags: +**Tags:** `tag/fort`, `tag/auto` :index:`Auto-harvest trees when low on stockpiled logs. ` This plugin can diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 2e0d03ec1..3feb22520 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -1,6 +1,6 @@ autoclothing ============ -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` :dfhack-keybind:`autoclothing` :index:`Automatically manage clothing work orders. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index b42ed9d85..53ed7f1ba 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -1,6 +1,6 @@ autodump ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/fps`, `tag/items`, `tag/stockpiles` :dfhack-keybind:`autodump` :dfhack-keybind:`autodump-destroy-here` :dfhack-keybind:`autodump-destroy-item` diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index 9a2169d65..bfe8ad717 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -1,6 +1,6 @@ autofarm ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/buildings` :dfhack-keybind:`autofarm` :index:`Automatically manage farm crop selection. diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index ee1bc8be6..bcff93ba6 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,6 +1,6 @@ autogems ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` :dfhack-keybind:`autogems-reload` :index:`Automatically cut rough gems. ` diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 82561fa2a..e0e1b2bac 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -1,6 +1,6 @@ autohauler ========== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/labors` :dfhack-keybind:`autohauler` :index:`Automatically manage hauling labors. diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index dc14a40bb..1d02fcfe8 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -1,6 +1,6 @@ autolabor ========= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/labors` :dfhack-keybind:`autolabor` :index:`Automatically manage dwarf labors. diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 3b9a68b7a..1c237f260 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -1,6 +1,6 @@ automaterial ============ -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/buildings`, `tag/map` :index:`Sorts building materials by recent usage. ` This makes building diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 8fe995d23..190bffc06 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -1,6 +1,6 @@ automelt ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/items`, `tag/stockpiles` :index:`Quickly designate items to be melted. ` When `enabled `, this diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst index 6842bef5c..2f874c452 100644 --- a/docs/plugins/autonestbox.rst +++ b/docs/plugins/autonestbox.rst @@ -1,6 +1,6 @@ autonestbox =========== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/animals` :dfhack-keybind:`autonestbox` Auto-assign egg-laying female pets to nestbox zones. Requires that you create diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 68aa2d50d..35657fc88 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -1,6 +1,6 @@ autotrade ========= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/items`, `tag/stockpiles` :index:`Quickly designate items to be traded. ` When `enabled `, diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 6a0adfe58..48ffdd883 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -1,6 +1,6 @@ blueprint ========= -Tags: +**Tags:** `tag/fort`, `tag/design`, `tag/quickfort`, `tag/map` :dfhack-keybind:`blueprint` :index:`Record a live game map in a quickfort blueprint. diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 106173c47..1f8ee1b2b 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -1,5 +1,6 @@ building-hacks ============== +**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` Provides a Lua API for creating powered workshops. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 6aba86209..be95dc6f1 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -1,6 +1,6 @@ buildingplan ============ -Tags: +**Tags:** `tag/fort`, `tag/design`, `tag/quickfort`, `tag/buildings`, `tag/map` :dfhack-keybind:`buildingplan` :index:`Plan building construction before you have materials. diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 29b81330d..4728e2cd5 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -1,6 +1,6 @@ burrows ======= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/productivity`, `tag/units` :dfhack-keybind:`burrow` :index:`Auto-expand burrows as you dig. diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index 93a10cbfa..68c53f821 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -1,6 +1,6 @@ changeitem ========== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/items` :dfhack-keybind:`changeitem` :index:`Change item material and base quality. diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 5298cf955..1267fbdac 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -1,6 +1,6 @@ changelayer =========== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`changelayer` :index:`Change the material of an entire geology layer. diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index e23c3f063..96c68de76 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -1,6 +1,6 @@ changevein ========== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`changevein` :index:`Change the material of a mineral inclusion. diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index 37416c551..c47a8c6dd 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -1,6 +1,6 @@ cleanconst ========== -Tags: +**Tags:** `tag/fort`, `tag/fps`, `tag/map` :dfhack-keybind:`cleanconst` :index:`Cleans up construction materials. diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 4a6aee1e3..20fa792db 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -3,7 +3,7 @@ cleaners ======== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/fps`, `tag/items`, `tag/map`, `tag/units` :dfhack-keybind:`clean` :dfhack-keybind:`spotclean` diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst index c90fe0569..409c39345 100644 --- a/docs/plugins/cleanowned.rst +++ b/docs/plugins/cleanowned.rst @@ -1,6 +1,6 @@ cleanowned ========== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/items` :dfhack-keybind:`cleanowned` :index:`Confiscates and dumps garbage owned by dwarves. diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 8e6c9e885..312734b97 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -1,6 +1,6 @@ command-prompt ============== -Tags: +**Tags:** `tag/system` :dfhack-keybind:`command-prompt` :index:`An in-game DFHack terminal where you can enter other commands. diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index aceb1e6d6..de2556b6f 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -1,6 +1,6 @@ confirm ======= -Tags: +**Tags:** `tag/fort`, `tag/interface` :dfhack-keybind:`confirm` :index:`Adds confirmation dialogs for destructive actions. diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index b49a85be4..38f072e62 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -1,6 +1,6 @@ createitem ========== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/items` :dfhack-keybind:`createitem` :index:`Create arbitrary items. ` You can diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index 17cdacf26..ecb4dbd2f 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -1,6 +1,6 @@ cursecheck ========== -Tags: +**Tags:** `tag/system`, `tag/interface` :dfhack-keybind:`cursecheck` :index:`Check for cursed creatures. ` diff --git a/docs/plugins/cxxrandom.rst b/docs/plugins/cxxrandom.rst index f3ed8f86d..0016fb8d0 100644 --- a/docs/plugins/cxxrandom.rst +++ b/docs/plugins/cxxrandom.rst @@ -1,5 +1,6 @@ cxxrandom ========= +**Tags:** `tag/dev` Provides a Lua API for random distributions. diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst index 2b3b20359..4cc94b217 100644 --- a/docs/plugins/debug.rst +++ b/docs/plugins/debug.rst @@ -1,6 +1,6 @@ debug ===== -Tags: +**Tags:** `tag/dev` :dfhack-keybind:`debugfilter` :index:`Configure verbosity of DFHack debug output. diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index 7f4a8ce24..6126d2828 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -1,6 +1,6 @@ deramp ====== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`deramp` :index:`Removes all ramps designated for removal from the map. diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index cdb21c01a..7d3beedc0 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -1,6 +1,6 @@ dig-now ======= -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`dig-now` :index:`Instantly complete dig designations. diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 8fbf1a910..f4e0c8acb 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -3,7 +3,7 @@ dig === -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/map` :dfhack-keybind:`digv` :dfhack-keybind:`digvx` :dfhack-keybind:`digl` diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst index cb8018145..1a17c32a7 100644 --- a/docs/plugins/digFlood.rst +++ b/docs/plugins/digFlood.rst @@ -1,6 +1,6 @@ digFlood ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/map` :dfhack-keybind:`digFlood` :index:`Digs out veins as they are discovered. diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index d2fada0d4..4891bf8d4 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -1,6 +1,6 @@ diggingInvaders =============== -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/map` :dfhack-keybind:`diggingInvaders` :index:`Invaders dig and destroy to get to your dwarves. diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index 75b71d06c..a7f42bb81 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -1,6 +1,6 @@ dwarfmonitor ============ -Tags: +**Tags:** `tag/fort`, `tag/inspection`, `tag/units` :dfhack-keybind:`dwarfmonitor` :index:`Measure fort happiness and efficiency. diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index 6a1c5a60f..2db4a9595 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -1,6 +1,6 @@ dwarfvet ======== -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/animals` :dfhack-keybind:`dwarfvet` :index:`Allows animals to be treated at animal hospitals. diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index 3a6081655..b9a7e4ee2 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -1,6 +1,6 @@ embark-assistant ================ -Tags: +**Tags:** `tag/fort`, `tag/embark`, `tag/interface` :dfhack-keybind:`embark-assistant` :index:`Embark site selection support. diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 987b76df6..52532e4f2 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -1,6 +1,6 @@ embark-tools ============ -Tags: +**Tags:** `tag/fort`, `tag/embark`, `tag/interface` :dfhack-keybind:`embark-tools` :index:`Extend the embark screen functionality. diff --git a/docs/plugins/eventful.rst b/docs/plugins/eventful.rst index 892f233bb..102287b77 100644 --- a/docs/plugins/eventful.rst +++ b/docs/plugins/eventful.rst @@ -1,5 +1,6 @@ eventful ======== +**Tags:** `tag/dev`, `tag/mod` Provides a Lua API for reacting to in-game events. diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index e06f2564c..2e09b8a4d 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -1,6 +1,6 @@ fastdwarf ========= -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/units` :dfhack-keybind:`fastdwarf` Dwarves teleport and/or finish jobs instantly. diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index 5056ec830..5a3beaab7 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -3,7 +3,7 @@ filltraffic =========== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/map` :dfhack-keybind:`` Usage: diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 48efd7255..28479791f 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -1,6 +1,6 @@ fix-unit-occupancy ================== -Tags: +**Tags:** `tag/fort`, `tag/fix`, `tag/map` :dfhack-keybind:`` Fix phantom unit occupancy issues. For example, if you see "unit blocking tile" diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index 940c13b7a..873e1770a 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -1,6 +1,6 @@ fixveins ======== -Tags: +**Tags:** `tag/fort`, `tag/fix`, `tag/map` :dfhack-keybind:`fixveins` Restore missing mineral inclusions. This tool can also remove invalid references diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index 354b0078e..a347f00e0 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -1,6 +1,6 @@ flows ===== -Tags: +**Tags:** `tag/fort`, `tag/inspection`, `tag/map` :dfhack-keybind:`flows` Counts map blocks with flowing liquids.. If you suspect that your magma sea diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst index a0c15547e..5f43e2fdd 100644 --- a/docs/plugins/follow.rst +++ b/docs/plugins/follow.rst @@ -1,6 +1,6 @@ follow ====== -Tags: +**Tags:** `tag/fort`, `tag/interface`, `tag/units` :dfhack-keybind:`follow` Make the screen follow the selected unit. Once you exit from the current menu or diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 217085fe4..51d48390c 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -1,6 +1,6 @@ forceequip ========== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/items`, `tag/units` :dfhack-keybind:`forceequip` Move items into a unit's inventory. This tool is typically used to equip diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst index 1615ba32d..39983940b 100644 --- a/docs/plugins/generated-creature-renamer.rst +++ b/docs/plugins/generated-creature-renamer.rst @@ -1,6 +1,6 @@ generated-creature-renamer ========================== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/legends`, `tag/units` :dfhack-keybind:`list-generated` :dfhack-keybind:`save-generated-raws` diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 167ac72f2..21325e8f2 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -1,6 +1,6 @@ getplants ========= -Tags: +**Tags:** `tag/fort`, `tag/productivity` :dfhack-keybind:`getplants` Designate trees for chopping and shrubs for gathering. Specify the types of diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst index 49064db0a..fbf4a9a76 100644 --- a/docs/plugins/hotkeys.rst +++ b/docs/plugins/hotkeys.rst @@ -1,6 +1,6 @@ hotkeys ======= -Tags: +**Tags:** `tag/system`, `tag/productivity`, `tag/interface` :dfhack-keybind:`hotkeys` Show all dfhack keybindings in current context. The command opens an in-game diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index 96f141de5..ceb08aa0e 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -1,6 +1,6 @@ infiniteSky =========== -Tags: +**Tags:** `tag/fort`, `tag/map` :dfhack-keybind:`infiniteSky` Automatically allocates new z-levels of sky at the top of the map as you build diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 2c359fac2..84babe66a 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -1,5 +1,5 @@ isoworldremote ============== -Tags: +**Tags:** `tag/dev`, `tag/mod` Provides a `remote API ` used by Isoworld. diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index 2ecf2b591..5dcb0c85f 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -2,7 +2,7 @@ jobutils ======== -Tags: +**Tags:** `tag/fort`, `tag/inspection`, `tag/jobs` :dfhack-keybind:`job` :dfhack-keybind:`job-duplicate` :dfhack-keybind:`job-material` diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index 103ecde2f..1a25cedae 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -1,6 +1,6 @@ labormanager ============ -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/labors` :dfhack-keybind:`labormanager` Automatically manage dwarf labors. Labormanager is derived from `autolabor` diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index b4a7ed524..43bb06072 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -1,6 +1,6 @@ lair ==== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`lair` Mark the map as a monster lair. This avoids item scatter when the fortress is diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index 61e4bcfe2..808089591 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -2,7 +2,7 @@ liquids ======= -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`liquids` :dfhack-keybind:`liquids-here` diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index 6f7c96cae..cbbf09f7e 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -1,5 +1,6 @@ luasocket ========= +**Tags:** `tag/dev`, `tag/mod` Provides a Lua API for accessing network sockets. diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 87ac4e088..07d1d4ab6 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -1,6 +1,6 @@ manipulator =========== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/interface`, `tag/labors` An in-game labor management interface. It is equivalent to the popular Dwarf Therapist utility. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index 2bcb32adf..6b7f1a5d9 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -1,5 +1,6 @@ map-render ========== +**Tags:** `tag/dev` Provides a Lua API for rerendering portions of the map. diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 9a3a632f2..5d11f98cb 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -1,6 +1,6 @@ misery ====== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/units` :dfhack-keybind:`misery` Increase the intensity of negative dwarven thoughts. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 225a49e1e..8c33d6ec3 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -1,6 +1,6 @@ mode ==== -Tags: +**Tags:** `tag/dev` :dfhack-keybind:`mode` This command lets you see and change the game mode directly. diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index 39f401bb2..7e01de6d0 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -1,6 +1,6 @@ mousequery ========== -Tags: `tag/fort`, `tag/interface` +**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` :dfhack-keybind:`mousequery` Adds mouse controls to the DF interface. For example, with ``mousequery`` you diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst index fb9df20a9..1558a7357 100644 --- a/docs/plugins/nestboxes.rst +++ b/docs/plugins/nestboxes.rst @@ -1,6 +1,6 @@ nestboxes ========= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/animals` Protect fertile eggs incubating in a nestbox. This plugin will automatically scan for and forbid fertile eggs incubating in a nestbox so that dwarves won't diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 2506bc582..3be1f82b2 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -1,6 +1,6 @@ orders ====== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/jobs` :dfhack-keybind:`orders` Manage manager orders. diff --git a/docs/plugins/pathable.rst b/docs/plugins/pathable.rst index 7a906ad89..6368d1c7e 100644 --- a/docs/plugins/pathable.rst +++ b/docs/plugins/pathable.rst @@ -1,5 +1,6 @@ pathable ======== +**Tags:** `tag/dev`, `tag/inspection`, `tag/interface`, `tag/map` Marks tiles that are reachable from the cursor. This plugin provides a Lua API, but no direct commands. diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index 3d9739ea3..95a9e60bd 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -1,6 +1,6 @@ petcapRemover ============= -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/animals` :dfhack-keybind:`petcapRemover` Modify the pet population cap. In vanilla DF, pets will not reproduce unless the diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index 4fc87f353..2021bc103 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -2,7 +2,7 @@ plants ====== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`plant` Grow shrubs or trees. diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 7186a413d..72a49b72c 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -1,6 +1,6 @@ power-meter =========== -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` Allow presure plates to measure power. If you run `gui/power-meter` while building a pressure plate, the pressure plate can be modified to detect power diff --git a/docs/plugins/probe.rst b/docs/plugins/probe.rst index 614d0bc81..0285bb821 100644 --- a/docs/plugins/probe.rst +++ b/docs/plugins/probe.rst @@ -1,6 +1,6 @@ probe ===== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/inspection`, `tag/map` :dfhack-keybind:`probe` :dfhack-keybind:`bprobe` :dfhack-keybind:`cprobe` diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 718f92fa6..7ff3b4313 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -2,7 +2,7 @@ prospector ========== -Tags: +**Tags:** `tag/fort`, `tag/embark`, `tag/inspection`, `tag/map` :dfhack-keybind:`prospect` Shows a summary of resources that exist on the map. It can also calculate an diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index d47e98d19..98590a73e 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -1,6 +1,6 @@ regrass ======= -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/animals` :dfhack-keybind:`regrass` Regrows all the grass. Use this command if your grazers have eaten everything diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 0c4bc9ddb..34695e19a 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -1,6 +1,6 @@ rename ====== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/productivity` :dfhack-keybind:`rename` Easily rename things. Use `gui/rename` for an in-game interface. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index 641a7a248..83e9ee461 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -1,6 +1,6 @@ rendermax ========= -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/mod` :dfhack-keybind:`rendermax` Modify the map lighting. This plugin provides a collection of OpenGL lighting diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index 7afb9be0d..2f28d7083 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -1,6 +1,6 @@ resume ====== -Tags: +**Tags:** `tag/fort`, `tag/productivity` :dfhack-keybind:`resume` Color planned buildings based on their suspend status. When enabled, this plugin diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index f8611d033..fb69d9a02 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -2,7 +2,7 @@ reveal ====== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/inspection`, `tag/armok`, `tag/map` :dfhack-keybind:`reveal` :dfhack-keybind:`unreveal` :dfhack-keybind:`revforget` diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst index beb9be4a1..d3b89d3a9 100644 --- a/docs/plugins/ruby.rst +++ b/docs/plugins/ruby.rst @@ -2,7 +2,7 @@ ruby ==== -Tags: +**Tags:** `tag/dev` :dfhack-keybind:`rb` :dfhack-keybind:`rb_eval` diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index edf6fb47c..41ae1c922 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -2,7 +2,7 @@ search ====== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` Adds search capabilities to the UI. The Stocks, Animals, Trading, Stockpile, Noble (assignment candidates), Military (position candidates), Burrows (unit diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index 2b04b3fa6..bd51fdd2e 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -1,6 +1,6 @@ seedwatch ========= -Tags: +**Tags:** `tag/fort`, `tag/auto` :dfhack-keybind:`seedwatch` Manages seed and plant cooking based on seed stock levels. diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst index 8afe4c629..42af0e7a8 100644 --- a/docs/plugins/showmood.rst +++ b/docs/plugins/showmood.rst @@ -1,6 +1,6 @@ showmood ======== -Tags: +**Tags:** `tag/fort`, `tag/inspection`, `tag/jobs` :dfhack-keybind:`showmood` Shows all items needed for the active strange mood. diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index a4cfed2ba..9255110bd 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -1,6 +1,6 @@ siege-engine ============ -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` Extend the functionality and usability of siege engines. Siege engines in DF haven't been updated since the game was 2D, and can only aim in four diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst index 12684175f..f3ba6e6fb 100644 --- a/docs/plugins/sort.rst +++ b/docs/plugins/sort.rst @@ -1,6 +1,6 @@ sort ==== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` :dfhack-keybind:`sort-items` :dfhack-keybind:`sort-units` diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 983086c94..08cc3f819 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -1,6 +1,6 @@ spectate ======== -Tags: +**Tags:** `tag/fort`, `tag/units` Automatically follow exciting dwarves. diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index 35cd490f5..0fe245d63 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -1,6 +1,6 @@ steam-engine ============ -Tags: +**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` Allow modded steam engine buildings to function. The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index 0e2f5a1ad..48647b869 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -1,6 +1,6 @@ stockflow ========= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs`, `tag/stockpiles` :dfhack-keybind:`stockflow` Queue manager jobs based on free space in stockpiles. With this plugin, the diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 76d52117d..0c1000456 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -2,7 +2,7 @@ stockpiles ========== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/stockpiles` :dfhack-keybind:`copystock` :dfhack-keybind:`savestock` :dfhack-keybind:`loadstock` diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index 076f2ab6f..5506b1d28 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -1,6 +1,6 @@ stocks ====== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` :dfhack-keybind:`stocks` Enhanced fortress stock management interface. When the plugin is enabled, two diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index 59212444b..3bb271477 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -1,6 +1,6 @@ stonesense ========== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/interface`, `tag/map` :dfhack-keybind:`stonesense` :dfhack-keybind:`ssense` diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index d6065f8eb..0198af136 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -1,6 +1,6 @@ strangemood =========== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/units` :dfhack-keybind:`strangemood` Triggers a strange mood. diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 19574bc3e..93072fb1b 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -1,6 +1,6 @@ tailor ====== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` :dfhack-keybind:`tailor` Automatically keep your dwarves in fresh clothing. Whenever the bookkeeper diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index cbdbd6cc5..185f283db 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -3,7 +3,7 @@ tiletypes ========= -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`tiletypes` :dfhack-keybind:`tiletypes-command` :dfhack-keybind:`tiletypes-here` diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index 3156a9e1c..beddfc9cb 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -1,6 +1,6 @@ title-folder ============= -Tags: +**Tags:** `tag/system`, `tag/interface` Displays the DF folder name in the window title bar. diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index 267179f14..d9d35a85f 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -1,6 +1,6 @@ title-version ============= -Tags: +**Tags:** `tag/system`, `tag/interface` Displays the DFHack version on DF's title screen. diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index 72466a1bf..cf5a9853f 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -1,6 +1,6 @@ trackstop ========= -Tags: +**Tags:** `tag/fort`, `tag/interface`, `tag/mod`, `tag/buildings` Adds dynamic configuration options for track stops. When enabled, this plugin adds a :kbd:`q` menu for track stops, which is completely blank in vanilla DF. diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index 15a9ba2f2..404a180b2 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -1,6 +1,6 @@ tubefill ======== -Tags: +**Tags:** `tag/fort`, `tag/armok`, `tag/map` :dfhack-keybind:`tubefill` Replentishes mined-out adamantine. Veins that were hollow will be left alone. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index fff1a4512..03489845e 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -1,6 +1,6 @@ tweak ===== -Tags: +**Tags:** `tag/adventure`, `tag/fort`, `tag/interface`, `tag/fps`, `tag/fix`, `tag/armok` :dfhack-keybind:`tweak` Contains various tweaks for minor bugs. diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index dda3fea5c..895556ce3 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -1,6 +1,6 @@ workNow ======= -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` :dfhack-keybind:`workNow` Reduce the time that dwarves idle after completing a job. After finishing a job, diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index cb20ad806..c2a9ea26a 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -1,6 +1,6 @@ workflow ======== -Tags: +**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` :dfhack-keybind:`workflow` :dfhack-keybind:`fix-job-postings` diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst index d09b69644..123d9920a 100644 --- a/docs/plugins/xlsxreader.rst +++ b/docs/plugins/xlsxreader.rst @@ -1,5 +1,6 @@ xlsxreader ========== +**Tags:** `tag/dev` Provides a Lua API for reading xlsx files. diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 5e01a12ac..9aa34d746 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -1,6 +1,6 @@ zone ==== -Tags: +**Tags:** `tag/fort`, `tag/productivity`, `tag/animals`, `tag/buildings` :dfhack-keybind:`zone` Manage activity zones, cages, and the animals therein. From b3a2a10caac918768bae3e1f41fb97bb3a375e8b Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:05:52 -0700 Subject: [PATCH 220/334] clean up generate doc dirs with ninja clean --- .gitignore | 1 + CMakeLists.txt | 11 ++++++++++- docs/changelogs/.gitignore | 2 -- docs/sphinx_extensions/dfhack/changelog.py | 2 ++ 4 files changed, 13 insertions(+), 3 deletions(-) delete mode 100644 docs/changelogs/.gitignore diff --git a/.gitignore b/.gitignore index 0711d9b17..e91dcab3b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ docs/changelogs/ docs/html/ docs/pdf/ docs/pseudoxml/ +docs/tags/ docs/text/ docs/tools/ docs/xml/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a793bcfbb..d8d90b007 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -473,7 +473,16 @@ if(BUILD_DOCS) ) set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/html/.buildinfo") - set_source_files_properties(${SPHINX_OUTPUT} PROPERTIES GENERATED TRUE) + set_property( + DIRECTORY PROPERTY ADDITIONAL_CLEAN_FILES TRUE + "${CMAKE_CURRENT_SOURCE_DIR}/docs/changelogs" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/html" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/tags" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/text" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/tools" + "${CMAKE_BINARY_DIR}/docs/html" + "${CMAKE_BINARY_DIR}/docs/text" + ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND ${SPHINX_EXECUTABLE} -E -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" diff --git a/docs/changelogs/.gitignore b/docs/changelogs/.gitignore deleted file mode 100644 index 90de5c70d..000000000 --- a/docs/changelogs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.txt -*.rst diff --git a/docs/sphinx_extensions/dfhack/changelog.py b/docs/sphinx_extensions/dfhack/changelog.py index 629d6b6de..2f27590fd 100644 --- a/docs/sphinx_extensions/dfhack/changelog.py +++ b/docs/sphinx_extensions/dfhack/changelog.py @@ -238,6 +238,8 @@ def generate_changelog(all=False): consolidate_changelog(stable_entries) consolidate_changelog(dev_entries) + os.makedirs(os.path.join(DOCS_ROOT, 'changelogs'), mode=0o755, exist_ok=True) + print_changelog(versions, stable_entries, os.path.join(DOCS_ROOT, 'changelogs/news.rst')) print_changelog(versions, dev_entries, os.path.join(DOCS_ROOT, 'changelogs/news-dev.rst')) From 1a777257b43f86f547b2f7b586e28d797eb8227f Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:06:02 -0700 Subject: [PATCH 221/334] add get_entry_types API method to helpdb --- library/lua/helpdb.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index a23002e57..bfca996eb 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -468,6 +468,10 @@ local function get_db_property(entry_name, property) textdb[entrydb[entry_name].text_entry][property] end +function ge_entry_types(entry) + return get_db_property(entry, 'entry_types') +end + -- returns the ~54 char summary blurb associated with the entry function get_entry_short_help(entry) return get_db_property(entry, 'short_help') @@ -637,9 +641,7 @@ function get_commands() end function is_builtin(command) - ensure_db() - return entrydb[command] and - get_db_property(command, 'entry_types')[ENTRY_TYPES.BUILTIN] + return is_entry(command) and get_entry_types(command)[ENTRY_TYPES.BUILTIN] end --------------------------------------------------------------------------- From 3e30b435fdb419a94b0db02f454a66235765a181 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:20:58 -0700 Subject: [PATCH 222/334] clean up docs build deps --- CMakeLists.txt | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8d90b007..129fcb16b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -459,12 +459,19 @@ if(BUILD_DOCS) "${CMAKE_CURRENT_SOURCE_DIR}/changelog.txt" ) list(FILTER SPHINX_GLOB_RECURSE_DEPS - EXCLUDE REGEX "docs/_" + EXCLUDE REGEX "docs/changelogs" ) - file(GLOB_RECURSE SPHINX_SCRIPT_DEPS - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.lua" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.rb" - "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.txt" + list(FILTER SPHINX_GLOB_RECURSE_DEPS + EXCLUDE REGEX "docs/html" + ) + list(FILTER SPHINX_GLOB_RECURSE_DEPS + EXCLUDE REGEX "docs/tags" + ) + list(FILTER SPHINX_GLOB_RECURSE_DEPS + EXCLUDE REGEX "docs/text" + ) + list(FILTER SPHINX_GLOB_RECURSE_DEPS + EXCLUDE REGEX "docs/tools" ) set(SPHINX_DEPS ${SPHINX_GLOB_DEPS} ${SPHINX_GLOB_RECURSE_DEPS} ${SPHINX_SCRIPT_DEPS} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt" From d04beb0e4a9b903fce241f15f2f1264701fc99a2 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:21:14 -0700 Subject: [PATCH 223/334] remove -E for sphinx-build. ninja clean can do it. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 129fcb16b..5a650269a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -492,13 +492,13 @@ if(BUILD_DOCS) ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND ${SPHINX_EXECUTABLE} - -E -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" + -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/html" -w "${CMAKE_BINARY_DIR}/docs/html/_sphinx-warnings.txt" -j auto COMMAND ${SPHINX_EXECUTABLE} - -E -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" + -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/text" -w "${CMAKE_BINARY_DIR}/docs/text/_sphinx-warnings.txt" From 625b4d439df84d0ad45dfc52795031e6d0ca9b2b Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:31:39 -0700 Subject: [PATCH 224/334] fix typo in API function name --- library/lua/helpdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index bfca996eb..7bc95bc75 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -468,7 +468,7 @@ local function get_db_property(entry_name, property) textdb[entrydb[entry_name].text_entry][property] end -function ge_entry_types(entry) +function get_entry_types(entry) return get_db_property(entry, 'entry_types') end From 6700a8d9222a00ff91accd9336734124f569d2c8 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 5 Aug 2022 22:31:51 -0700 Subject: [PATCH 225/334] add missing keybinds for filltraffic plugin --- docs/plugins/filltraffic.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index 5a3beaab7..acc4dabc8 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -4,7 +4,10 @@ filltraffic =========== **Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/map` -:dfhack-keybind:`` +:dfhack-keybind:`filltraffic` +:dfhack-keybind:`alltraffic` +:dfhack-keybind:`restrictice` +:dfhack-keybind:`restrictliquids` Usage: From 02a8f63ffc542b5d9f4f63b2bd7962ef31fcf095 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 6 Aug 2022 05:38:19 +0000 Subject: [PATCH 226/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a650269a..74bad7fa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,7 +489,7 @@ if(BUILD_DOCS) "${CMAKE_CURRENT_SOURCE_DIR}/docs/tools" "${CMAKE_BINARY_DIR}/docs/html" "${CMAKE_BINARY_DIR}/docs/text" - ) + ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND ${SPHINX_EXECUTABLE} -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" From 9098914ce47471d8a368e07ae46ccad2074959b5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 22:47:38 -0400 Subject: [PATCH 227/334] Add --offline option to docs/build.py to disable image downloads --- conf.py | 16 ++++++++++++++++ docs/build.py | 10 +++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/conf.py b/conf.py index 08c2e6bd8..dd7be9792 100644 --- a/conf.py +++ b/conf.py @@ -24,6 +24,22 @@ import sphinx import sys +if os.environ.get('DFHACK_DOCS_BUILD_OFFLINE'): + # block attempted image downloads, particularly for the PDF builder + def request_disabled(*args, **kwargs): + raise RuntimeError('Offline build - network request blocked') + + import urllib3.util + urllib3.util.create_connection = request_disabled + + import urllib3.connection + urllib3.connection.HTTPConnection.connect = request_disabled + + import requests + requests.request = request_disabled + requests.get = request_disabled + + # -- Support :dfhack-keybind:`command` ------------------------------------ # this is a custom directive that pulls info from default keybindings diff --git a/docs/build.py b/docs/build.py index 582279bcb..6a63ed59e 100755 --- a/docs/build.py +++ b/docs/build.py @@ -63,6 +63,8 @@ def parse_args(source_args): help='Number of Sphinx threads to run [environment variable: JOBS; default: "auto"]') parser.add_argument('--debug', action='store_true', help='Log commands that are run, etc.') + parser.add_argument('--offline', action='store_true', + help='Disable network connections') args, forward_args = _parse_known_args(parser, source_args) # work around weirdness with list args @@ -79,6 +81,11 @@ if __name__ == '__main__': exit(1) args, forward_args = parse_args(sys.argv[1:]) + + sphinx_env = os.environ.copy() + if args.offline: + sphinx_env['DFHACK_DOCS_BUILD_OFFLINE'] = '1' + for format_name in args.format: command = [args.sphinx] + OUTPUT_FORMATS[format_name].args + ['-j', args.jobs] if args.clean: @@ -88,5 +95,6 @@ if __name__ == '__main__': if args.debug: print('Building:', format_name) print('Running:', command) - subprocess.call(command) + subprocess.run(command, check=True, env=sphinx_env) + print('') From 153fef934b612d58b2e1692ae1eb315205c121c2 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 6 Aug 2022 23:32:00 -0700 Subject: [PATCH 228/334] clean up Label tests --- test/library/gui/widgets.Label.lua | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/test/library/gui/widgets.Label.lua b/test/library/gui/widgets.Label.lua index 49a75a235..4e6222e3a 100644 --- a/test/library/gui/widgets.Label.lua +++ b/test/library/gui/widgets.Label.lua @@ -1,13 +1,6 @@ --- test -dhack/scripts/devel/tests -twidgets.Label - local gui = require('gui') local widgets = require('gui.widgets') -local xtest = {} -- use to temporarily disable tests (change `function xtest.somename` to `function xxtest.somename`) -local wait = function(n) - delay(n or 30) -- enable for debugging the tests -end - local fs = defclass(fs, gui.FramedScreen) fs.ATTRS = { frame_style = gui.GREY_LINE_FRAME, @@ -18,7 +11,7 @@ fs.ATTRS = { focus_path = 'test-framed-screen', } -function test.Label_correct_frame_body_with_scroll_icons() +function test.correct_frame_body_with_scroll_icons() local t = {} for i = 1, 12 do t[#t+1] = tostring(i) @@ -31,19 +24,15 @@ function test.Label_correct_frame_body_with_scroll_icons() view_id = 'text', frame_inset = 0, text = t, - --show_scroll_icons = 'right', }, } end local o = fs{} - --o:show() - --wait() expect.eq(o.subviews.text.frame_body.width, 9, "Label's frame_body.x2 and .width should be one smaller because of show_scroll_icons.") - --o:dismiss() end -function test.Label_correct_frame_body_with_few_text_lines() +function test.correct_frame_body_with_few_text_lines() local t = {} for i = 1, 10 do t[#t+1] = tostring(i) @@ -56,19 +45,15 @@ function test.Label_correct_frame_body_with_few_text_lines() view_id = 'text', frame_inset = 0, text = t, - --show_scroll_icons = 'right', }, } end local o = fs{} - --o:show() - --wait() expect.eq(o.subviews.text.frame_body.width, 10, "Label's frame_body.x2 and .width should not change with show_scroll_icons = false.") - --o:dismiss() end -function test.Label_correct_frame_body_without_show_scroll_icons() +function test.correct_frame_body_without_show_scroll_icons() local t = {} for i = 1, 12 do t[#t+1] = tostring(i) @@ -87,8 +72,5 @@ function test.Label_correct_frame_body_without_show_scroll_icons() end local o = fs{} - --o:show() - --wait() expect.eq(o.subviews.text.frame_body.width, 10, "Label's frame_body.x2 and .width should not change with show_scroll_icons = false.") - --o:dismiss() end From a8d0cc798036901a8ccbd7ba4c0b730e94787fce Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 6 Aug 2022 23:48:25 -0700 Subject: [PATCH 229/334] support scrolling by half pages in Label --- docs/Lua API.rst | 17 ++++++++--- library/lua/gui/widgets.lua | 18 ++++++++---- test/library/gui/widgets.Label.lua | 46 ++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 94eaa1165..d436bdd5c 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3928,11 +3928,14 @@ It has the following attributes: :auto_width: Sets self.frame.w from the text width. :on_click: A callback called when the label is clicked (optional) :on_rclick: A callback called when the label is right-clicked (optional) -:scroll_keys: Specifies which keys the label should react to as a table. Default is ``STANDARDSCROLL`` (up or down arrows, page up or down). +:scroll_keys: Specifies which keys the label should react to as a table. The table should map + keys to the number of lines to scroll as positive or negative integers or one of the keywords + supported by the ``scroll`` method. The default is up/down arrows scrolling by one line and page + up/down scrolling by one page. :show_scroll_icons: Controls scroll icons' behaviour: ``false`` for no icons, ``'right'`` or ``'left'`` for - icons next to the text in an additional column (``frame_inset`` is adjusted to have ``.r`` or ``.l`` greater than ``0``), - ``nil`` same as ``'right'`` but changes ``frame_inset`` only if a scroll icon is actually necessary - (if ``getTextHeight()`` is greater than ``frame_body.height``). Default is ``nil``. + icons next to the text in an additional column (``frame_inset`` is adjusted to have ``.r`` or ``.l`` greater than ``0``), + ``nil`` same as ``'right'`` but changes ``frame_inset`` only if a scroll icon is actually necessary + (if ``getTextHeight()`` is greater than ``frame_body.height``). Default is ``nil``. :up_arrow_icon: The symbol for scroll up arrow. Default is ``string.char(24)`` (``↑``). :down_arrow_icon: The symbol for scroll down arrow. Default is ``string.char(25)`` (``↓``). :scroll_icon_pen: Specifies the pen for scroll icons. Default is ``COLOR_LIGHTCYAN``. @@ -4024,6 +4027,12 @@ The Label widget implements the following methods: Computes the width of the text. +* ``label:scroll(nlines)`` + + This method takes the number of lines to scroll as positive or negative + integers or one of the following keywords: ``+page``, ``-page``, + ``+halfpage``, or ``-halfpage``. + WrappedLabel class ------------------ diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 5765992af..3ac74bb52 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -652,6 +652,19 @@ function Label:onRenderFrame(dc, rect) end function Label:scroll(nlines) + if type(nlines) == 'string' then + if nlines == '+page' then + nlines = self.frame_body.height + elseif nlines == '-page' then + nlines = -self.frame_body.height + elseif nlines == '+halfpage' then + nlines = math.ceil(self.frame_body.height/2) + elseif nlines == '-halfpage' then + nlines = -math.ceil(self.frame_body.height/2) + else + error(('unhandled scroll keyword: "%s"'):format(nlines)) + end + end local n = self.start_line_num + nlines n = math.min(n, self:getTextHeight() - self.frame_body.height + 1) n = math.max(n, 1) @@ -668,11 +681,6 @@ function Label:onInput(keys) end for k,v in pairs(self.scroll_keys) do if keys[k] then - if v == '+page' then - v = self.frame_body.height - elseif v == '-page' then - v = -self.frame_body.height - end self:scroll(v) end end diff --git a/test/library/gui/widgets.Label.lua b/test/library/gui/widgets.Label.lua index 4e6222e3a..9a5d462fa 100644 --- a/test/library/gui/widgets.Label.lua +++ b/test/library/gui/widgets.Label.lua @@ -18,7 +18,7 @@ function test.correct_frame_body_with_scroll_icons() t[#t+1] = NEWLINE end - function fs:init(args) + function fs:init() self:addviews{ widgets.Label{ view_id = 'text', @@ -39,7 +39,7 @@ function test.correct_frame_body_with_few_text_lines() t[#t+1] = NEWLINE end - function fs:init(args) + function fs:init() self:addviews{ widgets.Label{ view_id = 'text', @@ -60,7 +60,7 @@ function test.correct_frame_body_without_show_scroll_icons() t[#t+1] = NEWLINE end - function fs:init(args) + function fs:init() self:addviews{ widgets.Label{ view_id = 'text', @@ -74,3 +74,43 @@ function test.correct_frame_body_without_show_scroll_icons() local o = fs{} expect.eq(o.subviews.text.frame_body.width, 10, "Label's frame_body.x2 and .width should not change with show_scroll_icons = false.") end + +function test.scroll() + local t = {} + for i = 1, 12 do + t[#t+1] = tostring(i) + t[#t+1] = NEWLINE + end + + function fs:init() + self:addviews{ + widgets.Label{ + view_id = 'text', + frame_inset = 0, + text = t, + }, + } + end + + local o = fs{frame_height=3} + local txt = o.subviews.text + expect.eq(1, txt.start_line_num) + + txt:scroll(1) + expect.eq(2, txt.start_line_num) + txt:scroll('+page') + expect.eq(5, txt.start_line_num) + txt:scroll('+halfpage') + expect.eq(7, txt.start_line_num) + txt:scroll('-halfpage') + expect.eq(5, txt.start_line_num) + txt:scroll('-page') + expect.eq(2, txt.start_line_num) + txt:scroll(-1) + expect.eq(1, txt.start_line_num) + + txt:scroll(-1) + expect.eq(1, txt.start_line_num) + txt:scroll(100) + expect.eq(10, txt.start_line_num) +end From f78e4276f985ca532a265f2e477ed0f4a772b7f9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 7 Aug 2022 15:27:14 +0000 Subject: [PATCH 230/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test/library/gui/widgets.Label.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/library/gui/widgets.Label.lua b/test/library/gui/widgets.Label.lua index 9a5d462fa..6b0097d1e 100644 --- a/test/library/gui/widgets.Label.lua +++ b/test/library/gui/widgets.Label.lua @@ -95,7 +95,7 @@ function test.scroll() local o = fs{frame_height=3} local txt = o.subviews.text expect.eq(1, txt.start_line_num) - + txt:scroll(1) expect.eq(2, txt.start_line_num) txt:scroll('+page') From ef56addb1476c3039757a312d11db405c159d0b5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 7 Aug 2022 22:13:46 -0700 Subject: [PATCH 231/334] prep for new format; accept pipe as tag separator --- library/lua/helpdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 7bc95bc75..a6c635ee0 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -211,7 +211,7 @@ local function update_entry(entry, iterator, opts) ::continue:: end entry.tags = {} - for _,tag in ipairs(tags:split('[ ,]+')) do + for _,tag in ipairs(tags:split('[ ,|]+')) do entry.tags[tag] = true end if #lines > 0 then From 22de8f61397eb3a2be38f291ef22cb8f4598d07e Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 7 Aug 2022 22:50:47 -0700 Subject: [PATCH 232/334] add secondary hotkey for gui/launcher --- data/init/dfhack.keybindings.init | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index f38840e06..c8785eba7 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -8,7 +8,9 @@ # Generic dwarfmode bindings # ############################## +# the GUI command launcher (two bindings since some keyboards don't have `) keybinding add ` gui/launcher +keybinding add Ctrl-Shift-D gui/launcher # show all current key bindings keybinding add Ctrl-F1 hotkeys @@ -42,7 +44,7 @@ keybinding add Ctrl-K autodump-destroy-item # quicksave, only in main dwarfmode screen and menu page keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave -# gui/quickfort script - apply pre-made blueprints to the map +# apply blueprints to the map (Alt-F for compatibility with LNP Quickfort) keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort keybinding add Alt-F@dwarfmode gui/quickfort From 7274a8cd2a08f84dec341e5906f12767f9789790 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 7 Aug 2022 23:35:00 -0700 Subject: [PATCH 233/334] use docs/build.py to do the docs build and add sphinx extension python files to the build deps --- CMakeLists.txt | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74bad7fa5..1f11dc93a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,6 +457,7 @@ if(BUILD_DOCS) file(GLOB_RECURSE SPHINX_GLOB_RECURSE_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.rst" "${CMAKE_CURRENT_SOURCE_DIR}/changelog.txt" + "${CMAKE_CURRENT_SOURCE_DIR}/docs/*py" ) list(FILTER SPHINX_GLOB_RECURSE_DEPS EXCLUDE REGEX "docs/changelogs" @@ -476,7 +477,6 @@ if(BUILD_DOCS) set(SPHINX_DEPS ${SPHINX_GLOB_DEPS} ${SPHINX_GLOB_RECURSE_DEPS} ${SPHINX_SCRIPT_DEPS} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt" "${CMAKE_CURRENT_SOURCE_DIR}/conf.py" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/gen_changelog.py" ) set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/html/.buildinfo") @@ -491,18 +491,8 @@ if(BUILD_DOCS) "${CMAKE_BINARY_DIR}/docs/text" ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} - COMMAND ${SPHINX_EXECUTABLE} - -q -b html -d "${CMAKE_BINARY_DIR}/docs/html" - "${CMAKE_CURRENT_SOURCE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/html" - -w "${CMAKE_BINARY_DIR}/docs/html/_sphinx-warnings.txt" - -j auto - COMMAND ${SPHINX_EXECUTABLE} - -q -b text -d "${CMAKE_BINARY_DIR}/docs/text" - "${CMAKE_CURRENT_SOURCE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/docs/text" - -w "${CMAKE_BINARY_DIR}/docs/text/_sphinx-warnings.txt" - -j auto + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py" + html text --sphinx="${SPHINX_EXECUTABLE}" -- -q DEPENDS ${SPHINX_DEPS} COMMENT "Building documentation with Sphinx" ) From 8f332c5925a9a4059c6868c7e96a4911f50c798f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Aug 2022 06:39:11 +0000 Subject: [PATCH 234/334] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f11dc93a..c341735c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -492,7 +492,7 @@ if(BUILD_DOCS) ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py" - html text --sphinx="${SPHINX_EXECUTABLE}" -- -q + html text --sphinx="${SPHINX_EXECUTABLE}" -- -q DEPENDS ${SPHINX_DEPS} COMMENT "Building documentation with Sphinx" ) From c44c8721c9ec2a2040edbda97c610afc405436a0 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 02:17:21 -0400 Subject: [PATCH 235/334] Initial attempt at dfhack-tool directive Doesn't appear to produce headings that can be used as link targets... --- conf.py | 1 + docs/plugins/3dveins.rst | 3 ++ docs/sphinx_extensions/dfhack/tool_docs.py | 52 ++++++++++++++++++++++ docs/sphinx_extensions/dfhack/util.py | 13 ++++++ 4 files changed, 69 insertions(+) create mode 100644 docs/sphinx_extensions/dfhack/tool_docs.py diff --git a/conf.py b/conf.py index dd7be9792..e8ff8392b 100644 --- a/conf.py +++ b/conf.py @@ -237,6 +237,7 @@ extensions = [ 'sphinx.ext.extlinks', 'dfhack.changelog', 'dfhack.lexer', + 'dfhack.tool_docs', ] sphinx_major_version = sphinx.version_info[0] diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 680d44c6e..9cd678709 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -3,6 +3,9 @@ **Tags:** `tag/fort`, `tag/mod`, `tag/map` :dfhack-keybind:`3dveins` +.. dfhack-tool:: 3dveins + :tags: foo, bar + :index:`Rewrite layer veins to expand in 3D space. <3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py new file mode 100644 index 000000000..ac06a6576 --- /dev/null +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -0,0 +1,52 @@ +# useful references: +# https://www.sphinx-doc.org/en/master/extdev/appapi.html +# https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html +# https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rst-directives + +import docutils.nodes as nodes +# import docutils.parsers.rst.directives as rst_directives +import sphinx +import sphinx.directives + +import dfhack.util + +class DFHackToolDirective(sphinx.directives.ObjectDescription): + has_content = False + required_arguments = 1 + option_spec = { + 'tags': dfhack.util.directive_arg_str_list, + } + + def run(self): + tool_name = self.get_signatures()[0] + + tag_nodes = [nodes.strong(text='Tags: ')] + for tag in self.options['tags']: + tag_nodes += [ + nodes.literal(tag, tag), + nodes.inline(text=' | '), + ] + tag_nodes.pop() + + return [ + nodes.title(text=tool_name), + nodes.paragraph('', '', *tag_nodes), + ] + + # simpler but always renders as "inline code" (and less customizable) + # def handle_signature(self, sig, signode): + # signode += addnodes.desc_name(text=sig) + # return sig + + +def register(app): + app.add_directive('dfhack-tool', DFHackToolDirective) + +def setup(app): + app.connect('builder-inited', register) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/docs/sphinx_extensions/dfhack/util.py b/docs/sphinx_extensions/dfhack/util.py index 71a432da4..61f38b043 100644 --- a/docs/sphinx_extensions/dfhack/util.py +++ b/docs/sphinx_extensions/dfhack/util.py @@ -5,3 +5,16 @@ DOCS_ROOT = os.path.join(DFHACK_ROOT, 'docs') if not os.path.isdir(DOCS_ROOT): raise ReferenceError('docs root not found: %s' % DOCS_ROOT) + +# directive argument helpers (supplementing docutils.parsers.rst.directives) +def directive_arg_str_list(argument): + """ + Converts a space- or comma-separated list of values into a Python list + of strings. + (Directive option conversion function.) + """ + if ',' in argument: + entries = argument.split(',') + else: + entries = argument.split() + return [entry.strip() for entry in entries] From d96260556eb7e92d3f0b3f61defdecbe38892a41 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 03:18:51 -0400 Subject: [PATCH 236/334] Make title visible by putting it in its own section --- docs/plugins/3dveins.rst | 6 +++--- docs/sphinx_extensions/dfhack/tool_docs.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 9cd678709..7ae2324eb 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,10 +1,10 @@ 3dveins ======= -**Tags:** `tag/fort`, `tag/mod`, `tag/map` -:dfhack-keybind:`3dveins` .. dfhack-tool:: 3dveins - :tags: foo, bar + :tags: fort, mod, map + +:dfhack-keybind:`3dveins` :index:`Rewrite layer veins to expand in 3D space. <3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index ac06a6576..dcda81d8d 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -29,7 +29,7 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tag_nodes.pop() return [ - nodes.title(text=tool_name), + nodes.section('', nodes.title(text=tool_name)), nodes.paragraph('', '', *tag_nodes), ] From 89a88e94a9794bc488114c73e670de0080e539c0 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 27 Jul 2022 22:02:08 -0400 Subject: [PATCH 237/334] Allow empty :tags:, give section a name to prevent errors --- docs/sphinx_extensions/dfhack/tool_docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index dcda81d8d..79516e0d8 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -21,7 +21,7 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tool_name = self.get_signatures()[0] tag_nodes = [nodes.strong(text='Tags: ')] - for tag in self.options['tags']: + for tag in self.options.get('tags', []): tag_nodes += [ nodes.literal(tag, tag), nodes.inline(text=' | '), @@ -29,7 +29,7 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tag_nodes.pop() return [ - nodes.section('', nodes.title(text=tool_name)), + nodes.section('', nodes.title(text=tool_name), ids=[tool_name]), nodes.paragraph('', '', *tag_nodes), ] From e47c681e9c252f0f4599f5c2f7b41ea13e4f05f3 Mon Sep 17 00:00:00 2001 From: lethosor Date: Thu, 4 Aug 2022 02:07:52 -0400 Subject: [PATCH 238/334] Use write_file_if_changed() in changelog.py Speeds up incremental builds significantly --- conf.py | 25 ++++------------------ docs/sphinx_extensions/dfhack/changelog.py | 4 ++-- docs/sphinx_extensions/dfhack/util.py | 20 +++++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/conf.py b/conf.py index e8ff8392b..49d005f46 100644 --- a/conf.py +++ b/conf.py @@ -14,9 +14,7 @@ serve to show the default. # pylint:disable=redefined-builtin -import contextlib import datetime -import io import os import re import shlex # pylint:disable=unused-import @@ -46,6 +44,10 @@ if os.environ.get('DFHACK_DOCS_BUILD_OFFLINE'): from docutils import nodes from docutils.parsers.rst import roles +sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'docs', 'sphinx_extensions')) +from dfhack.util import write_file_if_changed + + def get_keybinds(root, files, keybindings): """Add keybindings in the specified files to the given keybindings dict. @@ -145,23 +147,6 @@ def get_tags(): return tags -@contextlib.contextmanager -def write_file_if_changed(path): - with io.StringIO() as buffer: - yield buffer - new_contents = buffer.getvalue() - - try: - with open(path, 'r') as infile: - old_contents = infile.read() - except IOError: - old_contents = None - - if old_contents != new_contents: - with open(path, 'w') as outfile: - outfile.write(new_contents) - - def generate_tag_indices(): os.makedirs('docs/tags', mode=0o755, exist_ok=True) with write_file_if_changed('docs/tags/index.rst') as topidx: @@ -225,8 +210,6 @@ generate_tag_indices() # -- General configuration ------------------------------------------------ -sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'docs', 'sphinx_extensions')) - # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.8' diff --git a/docs/sphinx_extensions/dfhack/changelog.py b/docs/sphinx_extensions/dfhack/changelog.py index 2f27590fd..0cd732988 100644 --- a/docs/sphinx_extensions/dfhack/changelog.py +++ b/docs/sphinx_extensions/dfhack/changelog.py @@ -6,7 +6,7 @@ import sys from sphinx.errors import ExtensionError, SphinxError, SphinxWarning -from dfhack.util import DFHACK_ROOT, DOCS_ROOT +from dfhack.util import DFHACK_ROOT, DOCS_ROOT, write_file_if_changed CHANGELOG_PATHS = ( 'docs/changelog.txt', @@ -172,7 +172,7 @@ def consolidate_changelog(all_entries): def print_changelog(versions, all_entries, path, replace=True, prefix=''): # all_entries: version -> section -> entry - with open(path, 'w') as f: + with write_file_if_changed(path) as f: def write(line): if replace: line = replace_text(line, REPLACEMENTS) diff --git a/docs/sphinx_extensions/dfhack/util.py b/docs/sphinx_extensions/dfhack/util.py index 61f38b043..91f0accbe 100644 --- a/docs/sphinx_extensions/dfhack/util.py +++ b/docs/sphinx_extensions/dfhack/util.py @@ -1,3 +1,5 @@ +import contextlib +import io import os DFHACK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) @@ -6,6 +8,24 @@ DOCS_ROOT = os.path.join(DFHACK_ROOT, 'docs') if not os.path.isdir(DOCS_ROOT): raise ReferenceError('docs root not found: %s' % DOCS_ROOT) + +@contextlib.contextmanager +def write_file_if_changed(path): + with io.StringIO() as buffer: + yield buffer + new_contents = buffer.getvalue() + + try: + with open(path, 'r') as infile: + old_contents = infile.read() + except IOError: + old_contents = None + + if old_contents != new_contents: + with open(path, 'w') as outfile: + outfile.write(new_contents) + + # directive argument helpers (supplementing docutils.parsers.rst.directives) def directive_arg_str_list(argument): """ From de5f4d356679db170f823d806c81b2b1f3b2ef18 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 16:24:56 -0400 Subject: [PATCH 239/334] Default to document basename in dfhack-tool directive --- docs/plugins/3dveins.rst | 2 +- docs/sphinx_extensions/dfhack/tool_docs.py | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 7ae2324eb..f1149eb98 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -1,7 +1,7 @@ 3dveins ======= -.. dfhack-tool:: 3dveins +.. dfhack-tool:: :tags: fort, mod, map :dfhack-keybind:`3dveins` diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 79516e0d8..fd6ed0946 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -12,13 +12,16 @@ import dfhack.util class DFHackToolDirective(sphinx.directives.ObjectDescription): has_content = False - required_arguments = 1 + required_arguments = 0 option_spec = { 'tags': dfhack.util.directive_arg_str_list, } def run(self): - tool_name = self.get_signatures()[0] + if self.arguments: + tool_name = self.arguments[0] + else: + tool_name = self.env.docname.split('/')[-1] tag_nodes = [nodes.strong(text='Tags: ')] for tag in self.options.get('tags', []): @@ -33,11 +36,6 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): nodes.paragraph('', '', *tag_nodes), ] - # simpler but always renders as "inline code" (and less customizable) - # def handle_signature(self, sig, signode): - # signode += addnodes.desc_name(text=sig) - # return sig - def register(app): app.add_directive('dfhack-tool', DFHackToolDirective) From bb2ca0cc1666c8a33ef3dcfb4f11073c4830b746 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 16:59:45 -0400 Subject: [PATCH 240/334] Render dfhack-tool as admonition Getting a section header integrated is complicated, so might as well emulate Mediawiki with a box-like element instead --- docs/sphinx_extensions/dfhack/tool_docs.py | 9 +++++++-- docs/styles/dfhack.css | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index fd6ed0946..809bd48b8 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -32,8 +32,13 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tag_nodes.pop() return [ - nodes.section('', nodes.title(text=tool_name), ids=[tool_name]), - nodes.paragraph('', '', *tag_nodes), + nodes.admonition('', *[ + nodes.paragraph('', '', *[ + nodes.strong('', 'Tool: '), + nodes.inline('', tool_name), + ]), + nodes.paragraph('', '', *tag_nodes), + ], classes=['dfhack-tool-summary']), ] diff --git a/docs/styles/dfhack.css b/docs/styles/dfhack.css index 9b6e523ef..e24ce1c67 100644 --- a/docs/styles/dfhack.css +++ b/docs/styles/dfhack.css @@ -60,3 +60,8 @@ div.body { span.pre { overflow-wrap: break-word; } + +.dfhack-tool-summary p { + margin-top: 0; + margin-bottom: 0.5em; +} From 12b3363b2ca1c8417c2a1fb20f5806709bc58f76 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 17:26:33 -0400 Subject: [PATCH 241/334] Make dfhack-tool tags link to tag descriptions --- docs/sphinx_extensions/dfhack/tool_docs.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 809bd48b8..65da95181 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -6,6 +6,7 @@ import docutils.nodes as nodes # import docutils.parsers.rst.directives as rst_directives import sphinx +import sphinx.addnodes as addnodes import sphinx.directives import dfhack.util @@ -26,7 +27,13 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tag_nodes = [nodes.strong(text='Tags: ')] for tag in self.options.get('tags', []): tag_nodes += [ - nodes.literal(tag, tag), + addnodes.pending_xref(tag, nodes.inline(text=tag), **{ + 'reftype': 'ref', + 'refdomain': 'std', + 'reftarget': 'tag/' + tag, + 'refexplicit': False, + 'refwarn': True, + }), nodes.inline(text=' | '), ] tag_nodes.pop() From d19ffa18060807dd58ede04e12cb54867d156950 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 23:08:51 -0400 Subject: [PATCH 242/334] Add stub dfhack-command directive, refactor to support --- docs/plugins/3dveins.rst | 2 + docs/sphinx_extensions/dfhack/tool_docs.py | 49 +++++++++++++++------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index f1149eb98..86a00b51c 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -4,6 +4,8 @@ .. dfhack-tool:: :tags: fort, mod, map +.. dfhack-command:: + :dfhack-keybind:`3dveins` :index:`Rewrite layer veins to expand in 3D space. diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 65da95181..9100375b3 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -11,19 +11,37 @@ import sphinx.directives import dfhack.util -class DFHackToolDirective(sphinx.directives.ObjectDescription): +class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): has_content = False required_arguments = 0 - option_spec = { - 'tags': dfhack.util.directive_arg_str_list, - } - def run(self): + def get_name_or_docname(self): if self.arguments: - tool_name = self.arguments[0] + return self.arguments[0] else: - tool_name = self.env.docname.split('/')[-1] + return self.env.docname.split('/')[-1] + + def make_labeled_paragraph(self, label, content): + return nodes.paragraph('', '', *[ + nodes.strong('', '{}: '.format(label)), + nodes.inline('', content), + ]) + + def make_nodes(self): + raise NotImplementedError + + def run(self): + return [ + nodes.admonition('', *self.make_nodes(), classes=['dfhack-tool-summary']), + ] + +class DFHackToolDirective(DFHackToolDirectiveBase): + option_spec = { + 'tags': dfhack.util.directive_arg_str_list, + } + + def make_nodes(self): tag_nodes = [nodes.strong(text='Tags: ')] for tag in self.options.get('tags', []): tag_nodes += [ @@ -39,18 +57,21 @@ class DFHackToolDirective(sphinx.directives.ObjectDescription): tag_nodes.pop() return [ - nodes.admonition('', *[ - nodes.paragraph('', '', *[ - nodes.strong('', 'Tool: '), - nodes.inline('', tool_name), - ]), - nodes.paragraph('', '', *tag_nodes), - ], classes=['dfhack-tool-summary']), + self.make_labeled_paragraph('Tool', self.get_name_or_docname()), + nodes.paragraph('', '', *tag_nodes), + ] + + +class DFHackCommandDirective(DFHackToolDirectiveBase): + def make_nodes(self): + return [ + self.make_labeled_paragraph('Command', self.get_name_or_docname()), ] def register(app): app.add_directive('dfhack-tool', DFHackToolDirective) + app.add_directive('dfhack-command', DFHackCommandDirective) def setup(app): app.connect('builder-inited', register) From b3d79f87cbb77e49d688400b6d588cd1f6b94f7e Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 23:12:26 -0400 Subject: [PATCH 243/334] Fix optional name override --- docs/sphinx_extensions/dfhack/tool_docs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 9100375b3..4911bd454 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -14,6 +14,7 @@ import dfhack.util class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): has_content = False required_arguments = 0 + optional_arguments = 1 def get_name_or_docname(self): if self.arguments: From 39e928845879cea0f2ed9461f22dcde1403ad081 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 6 Aug 2022 23:11:12 -0400 Subject: [PATCH 244/334] Render commands as literals --- docs/sphinx_extensions/dfhack/tool_docs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 4911bd454..35e4de700 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -22,10 +22,10 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): else: return self.env.docname.split('/')[-1] - def make_labeled_paragraph(self, label, content): + def make_labeled_paragraph(self, label, content, label_class=nodes.strong, content_class=nodes.inline): return nodes.paragraph('', '', *[ - nodes.strong('', '{}: '.format(label)), - nodes.inline('', content), + label_class('', '{}: '.format(label)), + content_class('', content), ]) def make_nodes(self): @@ -66,7 +66,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): class DFHackCommandDirective(DFHackToolDirectiveBase): def make_nodes(self): return [ - self.make_labeled_paragraph('Command', self.get_name_or_docname()), + self.make_labeled_paragraph('Command', self.get_name_or_docname(), content_class=nodes.literal), ] From 5ef36d210fae15c4715d87680421f9e1559f6f65 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 01:03:15 -0400 Subject: [PATCH 245/334] Render implicit dfhack-command alongside dfhack-tool unless :no-command: is passed --- docs/plugins/3dveins.rst | 2 -- docs/sphinx_extensions/dfhack/tool_docs.py | 28 +++++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 86a00b51c..f1149eb98 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -4,8 +4,6 @@ .. dfhack-tool:: :tags: fort, mod, map -.. dfhack-command:: - :dfhack-keybind:`3dveins` :index:`Rewrite layer veins to expand in 3D space. diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 35e4de700..28876c36a 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -3,8 +3,10 @@ # https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rst-directives +from typing import List + import docutils.nodes as nodes -# import docutils.parsers.rst.directives as rst_directives +import docutils.parsers.rst.directives as rst_directives import sphinx import sphinx.addnodes as addnodes import sphinx.directives @@ -22,27 +24,31 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): else: return self.env.docname.split('/')[-1] - def make_labeled_paragraph(self, label, content, label_class=nodes.strong, content_class=nodes.inline): + @staticmethod + def make_labeled_paragraph(label, content, label_class=nodes.strong, content_class=nodes.inline) -> nodes.paragraph: return nodes.paragraph('', '', *[ label_class('', '{}: '.format(label)), content_class('', content), ]) - def make_nodes(self): + @staticmethod + def wrap_box(*children: List[nodes.Node]) -> nodes.Admonition: + return nodes.admonition('', *children, classes=['dfhack-tool-summary']) + + def render_content(self) -> List[nodes.Node]: raise NotImplementedError def run(self): - return [ - nodes.admonition('', *self.make_nodes(), classes=['dfhack-tool-summary']), - ] + return [self.wrap_box(*self.render_content())] class DFHackToolDirective(DFHackToolDirectiveBase): option_spec = { 'tags': dfhack.util.directive_arg_str_list, + 'no-command': rst_directives.flag, } - def make_nodes(self): + def render_content(self) -> List[nodes.Node]: tag_nodes = [nodes.strong(text='Tags: ')] for tag in self.options.get('tags', []): tag_nodes += [ @@ -62,9 +68,15 @@ class DFHackToolDirective(DFHackToolDirectiveBase): nodes.paragraph('', '', *tag_nodes), ] + def run(self): + out = DFHackToolDirectiveBase.run(self) + if 'no-command' not in self.options: + out += [self.wrap_box(*DFHackCommandDirective.render_content(self))] + return out + class DFHackCommandDirective(DFHackToolDirectiveBase): - def make_nodes(self): + def render_content(self) -> List[nodes.Node]: return [ self.make_labeled_paragraph('Command', self.get_name_or_docname(), content_class=nodes.literal), ] From ed95db27f5bd7b13e556fc2ca15b4f68f38cdcb7 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 01:37:14 -0400 Subject: [PATCH 246/334] Move dfhack-keybind role to tool_docs.py and call from dfhack-command --- conf.py | 67 +--------------------- docs/sphinx_extensions/dfhack/tool_docs.py | 66 ++++++++++++++++++++- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/conf.py b/conf.py index 49d005f46..775f94b68 100644 --- a/conf.py +++ b/conf.py @@ -21,6 +21,8 @@ import shlex # pylint:disable=unused-import import sphinx import sys +sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'docs', 'sphinx_extensions')) +from dfhack.util import write_file_if_changed if os.environ.get('DFHACK_DOCS_BUILD_OFFLINE'): # block attempted image downloads, particularly for the PDF builder @@ -38,71 +40,6 @@ if os.environ.get('DFHACK_DOCS_BUILD_OFFLINE'): requests.get = request_disabled -# -- Support :dfhack-keybind:`command` ------------------------------------ -# this is a custom directive that pulls info from default keybindings - -from docutils import nodes -from docutils.parsers.rst import roles - -sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'docs', 'sphinx_extensions')) -from dfhack.util import write_file_if_changed - - -def get_keybinds(root, files, keybindings): - """Add keybindings in the specified files to the - given keybindings dict. - """ - for file in files: - with open(os.path.join(root, file)) as f: - lines = [l.replace('keybinding add', '').strip() for l in f.readlines() - if l.startswith('keybinding add')] - for k in lines: - first, command = k.split(' ', 1) - bind, context = (first.split('@') + [''])[:2] - if ' ' not in command: - command = command.replace('"', '') - tool = command.split(' ')[0].replace('"', '') - keybindings[tool] = keybindings.get(tool, []) + [ - (command, bind.split('-'), context)] - -def get_all_keybinds(root_dir): - """Get the implemented keybinds, and return a dict of - {tool: [(full_command, keybinding, context), ...]}. - """ - keybindings = dict() - for root, _, files in os.walk(root_dir): - get_keybinds(root, files, keybindings) - return keybindings - -KEYBINDS = get_all_keybinds('data/init') - - -# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments -def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, - options={}, content=[]): - """Custom role parser for DFHack default keybinds.""" - roles.set_classes(options) - if text not in KEYBINDS: - return [], [] - newnode = nodes.paragraph() - for cmd, key, ctx in KEYBINDS[text]: - n = nodes.paragraph() - newnode += n - n += nodes.strong('Keybinding:', 'Keybinding:') - n += nodes.inline(' ', ' ') - for k in key: - n += nodes.inline(k, k, classes=['kbd']) - if cmd != text: - n += nodes.inline(' -> ', ' -> ') - n += nodes.literal(cmd, cmd, classes=['guilabel']) - if ctx: - n += nodes.inline(' in ', ' in ') - n += nodes.literal(ctx, ctx) - return [newnode], [] - - -roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func) - # -- Autodoc for DFhack plugins and scripts ------------------------------- def doc_dir(dirname, files, prefix): diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 28876c36a..d1bdec8bf 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -3,6 +3,7 @@ # https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rst-directives +import os from typing import List import docutils.nodes as nodes @@ -13,6 +14,63 @@ import sphinx.directives import dfhack.util + +_KEYBINDS = {} + +def scan_keybinds(root, files, keybindings): + """Add keybindings in the specified files to the + given keybindings dict. + """ + for file in files: + with open(os.path.join(root, file)) as f: + lines = [l.replace('keybinding add', '').strip() for l in f.readlines() + if l.startswith('keybinding add')] + for k in lines: + first, command = k.split(' ', 1) + bind, context = (first.split('@') + [''])[:2] + if ' ' not in command: + command = command.replace('"', '') + tool = command.split(' ')[0].replace('"', '') + keybindings[tool] = keybindings.get(tool, []) + [ + (command, bind.split('-'), context)] + +def scan_all_keybinds(root_dir): + """Get the implemented keybinds, and return a dict of + {tool: [(full_command, keybinding, context), ...]}. + """ + keybindings = dict() + for root, _, files in os.walk(root_dir): + scan_keybinds(root, files, keybindings) + return keybindings + + +def render_dfhack_keybind(command) -> List[nodes.paragraph]: + if command not in _KEYBINDS: + return [] + newnode = nodes.paragraph() + for keycmd, key, ctx in _KEYBINDS[command]: + n = nodes.paragraph() + newnode += n + n += nodes.strong('Keybinding:', 'Keybinding:') + n += nodes.inline(' ', ' ') + for k in key: + n += nodes.inline(k, k, classes=['kbd']) + if keycmd != command: + n += nodes.inline(' -> ', ' -> ') + n += nodes.literal(keycmd, keycmd, classes=['guilabel']) + if ctx: + n += nodes.inline(' in ', ' in ') + n += nodes.literal(ctx, ctx) + return [newnode] + + +# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments +def dfhack_keybind_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """Custom role parser for DFHack default keybinds.""" + return render_dfhack_keybind(text), [] + + class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): has_content = False required_arguments = 0 @@ -77,14 +135,20 @@ class DFHackToolDirective(DFHackToolDirectiveBase): class DFHackCommandDirective(DFHackToolDirectiveBase): def render_content(self) -> List[nodes.Node]: + command = self.get_name_or_docname() return [ - self.make_labeled_paragraph('Command', self.get_name_or_docname(), content_class=nodes.literal), + self.make_labeled_paragraph('Command', command, content_class=nodes.literal), + *render_dfhack_keybind(command), ] def register(app): app.add_directive('dfhack-tool', DFHackToolDirective) app.add_directive('dfhack-command', DFHackCommandDirective) + app.add_role('dfhack-keybind', dfhack_keybind_role) + + _KEYBINDS.update(scan_all_keybinds(os.path.join(dfhack.util.DFHACK_ROOT, 'data', 'init'))) + def setup(app): app.connect('builder-inited', register) From 5a14992aca93e896c334980ae0b104128508d0bd Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 01:44:02 -0400 Subject: [PATCH 247/334] Use new directives for a few plugins --- docs/plugins/3dveins.rst | 2 -- docs/plugins/autodump.rst | 11 +++++++---- docs/plugins/autogems.rst | 8 ++++++-- docs/plugins/automelt.rst | 5 ++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index f1149eb98..b14f6145d 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -4,8 +4,6 @@ .. dfhack-tool:: :tags: fort, mod, map -:dfhack-keybind:`3dveins` - :index:`Rewrite layer veins to expand in 3D space. <3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 53ed7f1ba..dc60946b1 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -1,9 +1,12 @@ autodump ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/fps`, `tag/items`, `tag/stockpiles` -:dfhack-keybind:`autodump` -:dfhack-keybind:`autodump-destroy-here` -:dfhack-keybind:`autodump-destroy-item` + +.. dfhack-tool:: + :tags: fort, auto, fps, items, stockpiles + +.. dfhack-command:: autodump-destroy-here + +.. dfhack-command:: autodump-destroy-item :index:`Automatically set items in a stockpile to be dumped. ` When diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index bcff93ba6..3ceed0bec 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,7 +1,11 @@ autogems ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` -:dfhack-keybind:`autogems-reload` + +.. dfhack-tool:: autogems + :tags: fort, auto, jobs + :no-command: + +.. dfhack-command:: autogems-reload :index:`Automatically cut rough gems. ` This plugin periodically scans your stocks of rough gems and creates manager diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 190bffc06..0ca9f82e9 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -1,6 +1,9 @@ automelt ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/items`, `tag/stockpiles` + +.. dfhack-tool:: + :tags: fort, auto, items, stockpiles + :no-command: :index:`Quickly designate items to be melted. ` When `enabled `, this From 7651d301d244033fc392eafef2985f245e9e4dd5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 01:51:38 -0400 Subject: [PATCH 248/334] Remove extra paragraph around keybindings --- docs/sphinx_extensions/dfhack/tool_docs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index d1bdec8bf..920bef98e 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -45,12 +45,11 @@ def scan_all_keybinds(root_dir): def render_dfhack_keybind(command) -> List[nodes.paragraph]: + out = [] if command not in _KEYBINDS: - return [] - newnode = nodes.paragraph() + return out for keycmd, key, ctx in _KEYBINDS[command]: n = nodes.paragraph() - newnode += n n += nodes.strong('Keybinding:', 'Keybinding:') n += nodes.inline(' ', ' ') for k in key: @@ -61,7 +60,8 @@ def render_dfhack_keybind(command) -> List[nodes.paragraph]: if ctx: n += nodes.inline(' in ', ' in ') n += nodes.literal(ctx, ctx) - return [newnode] + out.append(n) + return out # pylint:disable=unused-argument,dangerous-default-value,too-many-arguments From 6b32e008b3bfd9eb73535ae69ea82fbf73cc14d0 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 02:16:38 -0400 Subject: [PATCH 249/334] Attempt to port keybinding documentation verification to new extension Likely requires a sphinx Domain to work with parallel builds properly --- conf.py | 15 --------------- docs/sphinx_extensions/dfhack/tool_docs.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/conf.py b/conf.py index 775f94b68..ff4cdc59d 100644 --- a/conf.py +++ b/conf.py @@ -126,24 +126,9 @@ def write_tool_docs(): outfile.write(include) -def all_keybinds_documented(): - """Check that all keybindings are documented with the :dfhack-keybind: - directive somewhere.""" - undocumented_binds = set(KEYBINDS) - tools = set(i[0] for i in DOC_ALL_DIRS) - for t in tools: - with open(('./docs/tools/{}.rst').format(t)) as f: - tool_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read())) - undocumented_binds -= tool_binds - if undocumented_binds: - raise ValueError('The following DFHack commands have undocumented ' - 'keybindings: {}'.format(sorted(undocumented_binds))) - - # Actually call the docs generator and run test write_tool_docs() generate_tag_indices() -#all_keybinds_documented() # comment out while we're transitioning # -- General configuration ------------------------------------------------ diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 920bef98e..8c843dce0 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -3,6 +3,7 @@ # https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rst-directives +import logging import os from typing import List @@ -15,7 +16,10 @@ import sphinx.directives import dfhack.util +logger = sphinx.util.logging.getLogger(__name__) + _KEYBINDS = {} +_KEYBINDS_RENDERED = set() # commands whose keybindings have been rendered def scan_keybinds(root, files, keybindings): """Add keybindings in the specified files to the @@ -45,6 +49,7 @@ def scan_all_keybinds(root_dir): def render_dfhack_keybind(command) -> List[nodes.paragraph]: + _KEYBINDS_RENDERED.add(command) out = [] if command not in _KEYBINDS: return out @@ -64,6 +69,13 @@ def render_dfhack_keybind(command) -> List[nodes.paragraph]: return out +def check_missing_keybinds(): + # FIXME: _KEYBINDS_RENDERED is empty in the parent process under parallel builds + # consider moving to a sphinx Domain to solve this properly + for missing_command in sorted(set(_KEYBINDS.keys()) - _KEYBINDS_RENDERED): + logger.warning('Undocumented keybindings for command: %s', missing_command) + + # pylint:disable=unused-argument,dangerous-default-value,too-many-arguments def dfhack_keybind_role(role, rawtext, text, lineno, inliner, options={}, content=[]): @@ -153,6 +165,9 @@ def register(app): def setup(app): app.connect('builder-inited', register) + # TODO: re-enable once detection is corrected + # app.connect('build-finished', lambda *_: check_missing_keybinds()) + return { 'version': '0.1', 'parallel_read_safe': True, From 1e7ce2602e492e8cbe8b466686f2403a24bf6f1f Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 7 Aug 2022 18:18:51 -0400 Subject: [PATCH 250/334] Shrink tool/command boxes somewhat --- docs/styles/dfhack.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/styles/dfhack.css b/docs/styles/dfhack.css index e24ce1c67..4102e6412 100644 --- a/docs/styles/dfhack.css +++ b/docs/styles/dfhack.css @@ -61,7 +61,13 @@ span.pre { overflow-wrap: break-word; } -.dfhack-tool-summary p { +div.dfhack-tool-summary { + margin: 10px 0; + padding: 10px 15px; +} + +div.dfhack-tool-summary p { margin-top: 0; margin-bottom: 0.5em; + line-height: 1em; } From 6e29ddf2d312fb157105d340ff37aade0cd1fbac Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 8 Aug 2022 02:19:07 -0400 Subject: [PATCH 251/334] Move space out of node for better text rendering --- docs/sphinx_extensions/dfhack/tool_docs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 8c843dce0..04318816b 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -97,7 +97,8 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): @staticmethod def make_labeled_paragraph(label, content, label_class=nodes.strong, content_class=nodes.inline) -> nodes.paragraph: return nodes.paragraph('', '', *[ - label_class('', '{}: '.format(label)), + label_class('', '{}:'.format(label)), + nodes.inline(text=' '), content_class('', content), ]) @@ -119,7 +120,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): } def render_content(self) -> List[nodes.Node]: - tag_nodes = [nodes.strong(text='Tags: ')] + tag_nodes = [nodes.strong(text='Tags:'), nodes.inline(text=' ')] for tag in self.options.get('tags', []): tag_nodes += [ addnodes.pending_xref(tag, nodes.inline(text=tag), **{ From daf3bc516be17e6fc953cad9e8274fc7504568d6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 8 Aug 2022 02:29:22 -0400 Subject: [PATCH 252/334] Switch to to fix line breaks in text output No visible change in HTML output; PDF looks different but still acceptable --- docs/sphinx_extensions/dfhack/tool_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 04318816b..42d995ff2 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -104,7 +104,7 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): @staticmethod def wrap_box(*children: List[nodes.Node]) -> nodes.Admonition: - return nodes.admonition('', *children, classes=['dfhack-tool-summary']) + return nodes.topic('', *children, classes=['dfhack-tool-summary']) def render_content(self) -> List[nodes.Node]: raise NotImplementedError From e6b5d5b0c1904f0a8f384aff70117a80f2f3f721 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 8 Aug 2022 17:35:58 -0400 Subject: [PATCH 253/334] Remove commas from tag lists --- docs/plugins/3dveins.rst | 2 +- docs/plugins/autodump.rst | 2 +- docs/plugins/autogems.rst | 2 +- docs/plugins/automelt.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index b14f6145d..63f6cfd8d 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -2,7 +2,7 @@ ======= .. dfhack-tool:: - :tags: fort, mod, map + :tags: fort mod map :index:`Rewrite layer veins to expand in 3D space. <3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index dc60946b1..3e8d291a0 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -2,7 +2,7 @@ autodump ======== .. dfhack-tool:: - :tags: fort, auto, fps, items, stockpiles + :tags: fort auto fps items stockpiles .. dfhack-command:: autodump-destroy-here diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 3ceed0bec..11575aa7a 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -2,7 +2,7 @@ autogems ======== .. dfhack-tool:: autogems - :tags: fort, auto, jobs + :tags: fort auto jobs :no-command: .. dfhack-command:: autogems-reload diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 0ca9f82e9..6c42cbb7e 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -2,7 +2,7 @@ automelt ======== .. dfhack-tool:: - :tags: fort, auto, items, stockpiles + :tags: fort auto items stockpiles :no-command: :index:`Quickly designate items to be melted. From 2d60c543fd867f787e891061531ca7fd4b289572 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 8 Aug 2022 21:22:55 -0400 Subject: [PATCH 254/334] Remove "Tool:" line --- docs/sphinx_extensions/dfhack/tool_docs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 42d995ff2..ff4bfc9b1 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -135,7 +135,6 @@ class DFHackToolDirective(DFHackToolDirectiveBase): tag_nodes.pop() return [ - self.make_labeled_paragraph('Tool', self.get_name_or_docname()), nodes.paragraph('', '', *tag_nodes), ] From f9930b313aec99beac4257426f0ee969fccca681 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 9 Aug 2022 22:37:24 -0700 Subject: [PATCH 255/334] migrate all docs to the new directives add a summary option for tools and commands so we can output them with their tags and keybindings at the top of the file. --- docs/builtins/alias.rst | 16 ++--- docs/builtins/cls.rst | 15 +++-- docs/builtins/devel/dump-rpc.rst | 9 +-- docs/builtins/die.rst | 13 ++-- docs/builtins/disable.rst | 10 +-- docs/builtins/enable.rst | 16 ++--- docs/builtins/fpause.rst | 9 +-- docs/builtins/help.rst | 10 +-- docs/builtins/hide.rst | 12 ++-- docs/builtins/keybinding.rst | 13 ++-- docs/builtins/kill-lua.rst | 10 +-- docs/builtins/load.rst | 10 +-- docs/builtins/ls.rst | 7 +- docs/builtins/plug.rst | 17 +++-- docs/builtins/reload.rst | 11 ++-- docs/builtins/sc-script.rst | 12 ++-- docs/builtins/script.rst | 12 ++-- docs/builtins/show.rst | 14 ++-- docs/builtins/tags.rst | 11 ++-- docs/builtins/type.rst | 13 ++-- docs/builtins/unload.rst | 7 +- docs/plugins/3dveins.rst | 9 ++- docs/plugins/RemoteFortressReader.rst | 19 ++++-- docs/plugins/add-spatter.rst | 19 +++--- docs/plugins/autobutcher.rst | 13 ++-- docs/plugins/autochop.rst | 10 +-- docs/plugins/autoclothing.rst | 11 ++-- docs/plugins/autodump.rst | 20 +++--- docs/plugins/autofarm.rst | 16 ++--- docs/plugins/autogems.rst | 15 ++--- docs/plugins/autohauler.rst | 17 ++--- docs/plugins/autolabor.rst | 12 ++-- docs/plugins/automaterial.rst | 13 ++-- docs/plugins/automelt.rst | 9 ++- docs/plugins/autonestbox.rst | 25 +++++--- docs/plugins/autotrade.rst | 14 ++-- docs/plugins/blueprint.rst | 13 ++-- docs/plugins/building-hacks.rst | 6 +- docs/plugins/buildingplan.rst | 10 +-- docs/plugins/burrows.rst | 21 +++--- docs/plugins/changeitem.rst | 20 +++--- docs/plugins/changelayer.rst | 15 +++-- docs/plugins/changevein.rst | 14 ++-- docs/plugins/cleanconst.rst | 17 ++--- docs/plugins/cleaners.rst | 32 ++++++---- docs/plugins/cleanowned.rst | 17 ++--- docs/plugins/command-prompt.rst | 7 +- docs/plugins/confirm.rst | 12 ++-- docs/plugins/createitem.rst | 13 ++-- docs/plugins/cursecheck.rst | 7 +- docs/plugins/cxxrandom.rst | 6 +- docs/plugins/debug.rst | 14 ++-- docs/plugins/deramp.rst | 10 +-- docs/plugins/dig-now.rst | 16 ++--- docs/plugins/dig.rst | 71 ++++++++++++--------- docs/plugins/digFlood.rst | 19 +++--- docs/plugins/diggingInvaders.rst | 7 +- docs/plugins/dwarfmonitor.rst | 10 +-- docs/plugins/dwarfvet.rst | 7 +- docs/plugins/embark-assistant.rst | 17 ++--- docs/plugins/embark-tools.rst | 7 +- docs/plugins/eventful.rst | 6 +- docs/plugins/fastdwarf.rst | 6 +- docs/plugins/filltraffic.rst | 40 ++++++------ docs/plugins/fix-unit-occupancy.rst | 10 +-- docs/plugins/fixveins.rst | 10 +-- docs/plugins/flows.rst | 10 +-- docs/plugins/follow.rst | 12 ++-- docs/plugins/forceequip.rst | 17 ++--- docs/plugins/generated-creature-renamer.rst | 20 ++++-- docs/plugins/getplants.rst | 10 +-- docs/plugins/hotkeys.rst | 11 ++-- docs/plugins/infiniteSky.rst | 11 ++-- docs/plugins/isoworldremote.rst | 8 ++- docs/plugins/jobutils.rst | 18 ++++-- docs/plugins/labormanager.rst | 14 ++-- docs/plugins/lair.rst | 9 +-- docs/plugins/liquids.rst | 10 ++- docs/plugins/luasocket.rst | 6 +- docs/plugins/manipulator.rst | 9 ++- docs/plugins/map-render.rst | 6 +- docs/plugins/misery.rst | 6 +- docs/plugins/mode.rst | 6 +- docs/plugins/mousequery.rst | 6 +- docs/plugins/nestboxes.rst | 13 ++-- docs/plugins/orders.rst | 6 +- docs/plugins/pathable.rst | 10 +-- docs/plugins/petcapRemover.rst | 21 +++--- docs/plugins/plants.rst | 10 ++- docs/plugins/power-meter.rst | 12 ++-- docs/plugins/probe.rst | 14 ++-- docs/plugins/prospector.rst | 14 ++-- docs/plugins/regrass.rst | 11 ++-- docs/plugins/rename.rst | 8 ++- docs/plugins/rendermax.rst | 10 +-- docs/plugins/resume.rst | 21 +++--- docs/plugins/reveal.rst | 34 ++++++---- docs/plugins/ruby.rst | 15 +++-- docs/plugins/search.rst | 14 ++-- docs/plugins/seedwatch.rst | 6 +- docs/plugins/showmood.rst | 6 +- docs/plugins/siege-engine.rst | 11 ++-- docs/plugins/sort.rst | 14 ++-- docs/plugins/spectate.rst | 6 +- docs/plugins/steam-engine.rst | 11 ++-- docs/plugins/stockflow.rst | 12 ++-- docs/plugins/stockpiles.rst | 26 +++++--- docs/plugins/stocks.rst | 9 +-- docs/plugins/stonesense.rst | 10 +-- docs/plugins/strangemood.rst | 6 +- docs/plugins/tailor.rst | 17 ++--- docs/plugins/tiletypes.rst | 26 +++++--- docs/plugins/title-folder.rst | 6 +- docs/plugins/title-version.rst | 6 +- docs/plugins/trackstop.rst | 14 ++-- docs/plugins/tubefill.rst | 8 ++- docs/plugins/tweak.rst | 6 +- docs/plugins/workNow.rst | 12 ++-- docs/plugins/workflow.rst | 12 ++-- docs/plugins/xlsxreader.rst | 6 +- docs/plugins/zone.rst | 6 +- docs/sphinx_extensions/dfhack/tool_docs.py | 11 +++- 122 files changed, 913 insertions(+), 665 deletions(-) diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 6efaf0776..84a761556 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -1,13 +1,13 @@ alias ===== -**Tags:** `tag/system` -:dfhack-keybind:`alias` - -:index:`Configure helper aliases for other DFHack commands. -` Aliases are -resolved immediately after built-in commands, which means that an alias cannot -override a built-in command, but can override a command implemented by a plugin -or script. + +.. dfhack-tool:: + :summary: Configure helper aliases for other DFHack commands. + :tags: system + +Aliases are resolved immediately after built-in commands, which means that an +alias cannot override a built-in command, but can override a command implemented +by a plugin or script. Usage: diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index d6a245081..2aad049b4 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -1,8 +1,13 @@ cls === -**Tags:** `tag/system` -:dfhack-keybind:`cls` -:index:`Clear the terminal screen. ` Can also -be invoked as ``clear``. Note that this command does not delete command history. -It just clears the text on the screen. +.. dfhack-tool:: + :summary: Clear the terminal screen. + :tags: system + +Can also be invoked as ``clear``. Note that this command does not delete command +history. It just clears the text on the screen. + +Usage:: + + cls diff --git a/docs/builtins/devel/dump-rpc.rst b/docs/builtins/devel/dump-rpc.rst index 65d4dae57..977b6e33c 100644 --- a/docs/builtins/devel/dump-rpc.rst +++ b/docs/builtins/devel/dump-rpc.rst @@ -1,10 +1,11 @@ devel/dump-rpc ============== -Tags: `tag/system` -:dfhack-keybind:`devel/dump-rpc` -:index:`Write RPC endpoint information to the specified file. -` +.. dfhack-tool:: + :summary: Dump RPC endpoint info. + :tags: dev + +Write RPC endpoint information to the specified file. Usage:: diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index db487da0e..b4873abc5 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -1,7 +1,12 @@ die === -**Tags:** `tag/system` -:dfhack-keybind:`die` -:index:`Instantly exit DF without saving. -` +.. dfhack-tool:: + :summary: Instantly exit DF without saving. + :tags: system + +Use to exit DF quickly and safely. + +Usage:: + + die diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index 758b6ff8f..e109e69e1 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -1,11 +1,11 @@ disable ======= -**Tags:** `tag/system` -:dfhack-keybind:`disable` -:index:`Deactivate a DFHack tool that has some persistent effect. -` See the -`enable` command for more info. +.. dfhack-tool:: + :summary: Deactivate a DFHack tool that has some persistent effect. + :tags: system + +See the `enable` command for more info. Usage:: diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 8705bc4ef..63b0bdc5f 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -1,14 +1,14 @@ enable ====== -**Tags:** `tag/system` -:dfhack-keybind:`enable` -:index:`Activate a DFHack tool that has some persistent effect. -` Many plugins -and scripts can be in a distinct enabled or disabled state. Some of them -activate and deactivate automatically depending on the contents of the world -raws. Others store their state in world data. However a number of them have to -be enabled globally, and the init file is the right place to do it. +.. dfhack-tool:: + :summary: Activate a DFHack tool that has some persistent effect. + :tags: system + +Many plugins and scripts can be in a distinct enabled or disabled state. Some +of them activate and deactivate automatically depending on the contents of the +world raws. Others store their state in world data. However a number of them +have to be enabled globally, and the init file is the right place to do it. Most such plugins or scripts support the built-in ``enable`` and `disable` commands. Calling them at any time without arguments prints a list of enabled diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index beb783e82..2e8f851f7 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -1,10 +1,11 @@ fpause ====== -**Tags:** `tag/system` -:dfhack-keybind:`fpause` -:index:`Forces DF to pause. ` This is useful when -your FPS drops below 1 and you lose control of the game. +.. dfhack-tool:: + :summary: Forces DF to pause. + :tags: system + +This is useful when your FPS drops below 1 and you lose control of the game. Usage:: diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index 4195f2eaa..e1e913747 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -1,11 +1,11 @@ help ==== -**Tags:** `tag/system` -:dfhack-keybind:`help` -:index:`Display help about a command or plugin. -` Can also be invoked as ``?`` -or ``man`` (short for "manual"). +.. dfhack-tool:: + :summary: Display help about a command or plugin. + :tags: system + +Can also be invoked as ``?`` or ``man`` (short for "manual"). Usage:: diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 087d87726..0ec8f36df 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -1,12 +1,12 @@ hide ==== -**Tags:** `tag/system` -:dfhack-keybind:`hide` -:index:`Hide the DFHack terminal window. -` You can show it again with the `show` -command, though you'll need to use it from a `keybinding` set beforehand or the -in-game `command-prompt`. +.. dfhack-tool:: + :summary: Hide the DFHack terminal window. + :tags: system + +You can show it again with the `show` command, though you'll need to use it from +a `keybinding` set beforehand or the in-game `command-prompt`. Only available on Windows. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index fd857a42b..5ce8145f4 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -1,12 +1,13 @@ keybinding ========== -**Tags:** `tag/system` -:dfhack-keybind:`keybinding` -:index:`Create hotkeys that will run DFHack commands. -` Like any other -command, it can be used at any time from the console, but bindings are not -remembered between runs of the game unless re-created in `dfhack.init`. +.. dfhack-tool:: + :summary: Create hotkeys that will run DFHack commands. + :tags: system + +Like any other command, it can be used at any time from the console, but +bindings are not remembered between runs of the game unless re-created in +`dfhack.init`. Hotkeys can be any combinations of Ctrl/Alt/Shift with A-Z, 0-9, F1-F12, or ``\``` (the key below the ``Esc`` key. diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 129268fa8..4de0ab7d1 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -1,11 +1,11 @@ kill-lua ======== -**Tags:** `tag/system` -:dfhack-keybind:`kill-lua` -:index:`Gracefully stops any currently-running Lua scripts. -` Use this -command to stop a misbehaving script that appears to be stuck. +.. dfhack-tool:: + :summary: Gracefully stop any currently-running Lua scripts. + :tags: system + +Use this command to stop a misbehaving script that appears to be stuck. Usage:: diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index cd3aed2c0..d253144a8 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -1,11 +1,11 @@ load ==== -**Tags:** `tag/system` -:dfhack-keybind:`load` -:index:`Load and register a plugin library. -` Also see `unload` and `reload` for -related actions. +.. dfhack-tool:: + :summary: Load and register a plugin library. + :tags: system + +Also see `unload` and `reload` for related actions. Usage:: diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index f03e3e533..702c8bae5 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -1,9 +1,10 @@ ls == -**Tags:** `tag/system` -:dfhack-keybind:`ls` -:index:`List available DFHack commands. ` +.. dfhack-tool:: + :summary: List available DFHack commands. + :tags: system + In order to group related commands, each command is associated with a list of tags. You can filter the listed commands by a tag or a substring of the command name. Can also be invoked as ``dir``. diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index 76e31c432..e10c154e5 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -1,14 +1,13 @@ plug ==== -**Tags:** `tag/system` -:dfhack-keybind:`plug` -:index:`List available plugins and whether they are enabled. -` +.. dfhack-tool:: + :summary: List available plugins and whether they are enabled. + :tags: system -Usage: +Usage:: -``plug`` - Lists available plugins and whether they are enabled. -``plug [ ...]`` - Lists only the named plugins. + plug [ [ ...]] + +If run with parameters, it lists only the named plugins. Otherwise it will list +all available plugins. diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index 8449c713d..1429e1038 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -1,11 +1,12 @@ reload ====== -**Tags:** `tag/system` -:dfhack-keybind:`reload` -:index:`Reload a loaded plugin. ` Developers -use this command to reload a plugin that they are actively modifying. Also see -`load` and `unload` for related actions. +.. dfhack-tool:: + :summary: Reload a loaded plugin. + :tags: system + +Developers use this command to reload a plugin that they are actively modifying. +Also see `load` and `unload` for related actions. Usage:: diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index 17c0efe25..b3255cb9f 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -1,12 +1,12 @@ sc-script ========= -**Tags:** `tag/system` -:dfhack-keybind:`sc-script` -:index:`Run commands when game state changes occur. -` This is similar to -the static `init-files` but is slightly more flexible since it can be set -dynamically. +.. dfhack-tool:: + :summary: Run commands when game state changes occur. + :tags: system + +This is similar to the static `init-files` but is slightly more flexible since +it can be set dynamically. Usage: diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index e5576ed50..fbcebff6c 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -1,12 +1,12 @@ script ====== -**Tags:** `tag/system` -:dfhack-keybind:`script` -:index:`Execute a batch file of DFHack commands. -` It reads a text file and -runs each line as a DFHack command as if it had been typed in by the user -- -treating the input like `an init file `. +.. dfhack-tool:: + :summary: Execute a batch file of DFHack commands. + :tags: system + +It reads a text file and runs each line as a DFHack command as if it had been +typed in by the user -- treating the input like `an init file `. Some other tools, such as `autobutcher` and `workflow`, export their settings as the commands to create them - which can later be reloaded with ``script``. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index fde642726..7002d5aff 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -1,13 +1,13 @@ show ==== -**Tags:** `tag/system` -:dfhack-keybind:`show` -:index:`Unhides the DFHack terminal window. -` Useful if you have hidden the -terminal with `hide` and you want it back. Since the terminal window won't be -available to run this command, you'll need to use it from a `keybinding` set -beforehand or the in-game `command-prompt`. +.. dfhack-tool:: + :summary: Unhides the DFHack terminal window. + :tags: system + +Useful if you have hidden the terminal with `hide` and you want it back. Since +the terminal window won't be available to run this command, you'll need to use +it from a `keybinding` set beforehand or the in-game `command-prompt`. Only available on Windows. diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index 024677cff..93a094149 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -1,11 +1,12 @@ tags ==== -**Tags:** `tag/system` -:dfhack-keybind:`tags` -:index:`List the strings that DFHack tools can be tagged with. -` You can find -groups of related tools by passing the tag name to the `ls` command. +.. dfhack-tool:: + :summary: List the strings that DFHack tools can be tagged with. + :tags: system + +You can find groups of related tools by passing the tag name to the `ls` +command. Usage:: diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index d7cf9588e..3336cae5f 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -1,12 +1,13 @@ type ==== -**Tags:** `tag/system` -:dfhack-keybind:`type` -:index:`Describe how a command is implemented. -` DFHack commands can be provided -by plugins, scripts, or by the core library itself. The ``type`` command can -tell you which is the source of a particular command. +.. dfhack-tool:: + :summary: Describe how a command is implemented. + :tags: system + +DFHack commands can be provided by plugins, scripts, or by the core library +itself. The ``type`` command can tell you which is the source of a particular +command. Usage:: diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index e79d624ec..132732a7f 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -1,9 +1,10 @@ unload ====== -**Tags:** `tag/system` -:dfhack-keybind:`unload` -:index:`Unload a plugin from memory. ` +.. dfhack-tool:: + :summary: Unload a plugin from memory. + :tags: system + Also see `load` and `reload` for related actions. Usage:: diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 63f6cfd8d..c60582310 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -2,13 +2,12 @@ ======= .. dfhack-tool:: + :summary: Rewrite layer veins to expand in 3D space. :tags: fort mod map -:index:`Rewrite layer veins to expand in 3D space. -<3dveins; Rewrite layer veins to expand in 3D space.>` Existing, flat veins -are removed and new 3D veins that naturally span z-levels are generated in -their place. The transformation preserves the mineral counts reported by -`prospect`. +Existing, flat veins are removed and new 3D veins that naturally span z-levels +are generated in their place. The transformation preserves the mineral counts +reported by `prospect`. Usage:: diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index da8c3570e..f849266c2 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -1,12 +1,19 @@ RemoteFortressReader ==================== -**Tags:** `tag/dev` -:dfhack-keybind:`RemoteFortressReader_version` -:dfhack-keybind:`load-art-image-chunk` -:index:`Backend for Armok Vision. -` Provides an API for realtime -remote fortress visualization. See :forums:`Armok Vision <146473>`. +.. dfhack-tool:: + :summary: Backend for Armok Vision. + :tags: dev + :no-command: + +.. dfhack-command:: RemoteFortressReader_version + :summary: Print the loaded RemoteFortressReader version. + +.. dfhack-command:: load-art-image-chunk + :summary: Gets an art image chunk by index. + +This plugin provides an API for realtime remote fortress visualization. See +:forums:`Armok Vision <146473>`. Usage: diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index ed3675132..06739291e 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -1,11 +1,14 @@ add-spatter =========== -**Tags:** `tag/adventure`, `tag/fort`, `tag/mod`, `tag/items` -:index:`Make tagged reactions produce contaminants. -` Give some use to all -those poisons that can be bought from caravans! The plugin automatically enables -itself when you load a world with reactions that include names starting with -``SPATTER_ADD_``. These reactions will then produce contaminants on items -instead of improvements. The contaminants are immune to being washed away by -water or destroyed by `clean`. +.. dfhack-tool:: + :summary: Make tagged reactions produce contaminants. + :tags: adventure fort mod items + :no-command: + +Give some use to all those poisons that can be bought from caravans! The plugin +automatically enables itself when you load a world with reactions that include +names starting with ``SPATTER_ADD_``, so there are no commands to run to use it. +These reactions will then produce contaminants on items instead of improvements. +The contaminants are immune to being washed away by water or destroyed by +`clean`. diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index 7a960012e..9379ae9c1 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -1,12 +1,13 @@ autobutcher =========== -**Tags:** `tag/fort`, `tag/auto`, `tag/fps`, `tag/animals` -:dfhack-keybind:`autobutcher` -Automatically butcher excess livestock. This plugin monitors how many pets you -have of each gender and age and assigns excess lifestock for slaughter. Requires -that you add the target race(s) to a watch list. Units will be ignored if they -are: +.. dfhack-tool:: + :summary: Automatically butcher excess livestock. + :tags: fort auto fps animals + +This plugin monitors how many pets you have of each gender and age and assigns +excess lifestock for slaughter. It requires that you add the target race(s) to a +watch list. Units will be ignored if they are: * Untamed * Nicknamed (for custom protection; you can use the `rename` ``unit`` tool diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 6b0b7125e..b445384fb 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -1,10 +1,12 @@ autochop ======== -**Tags:** `tag/fort`, `tag/auto` -:index:`Auto-harvest trees when low on stockpiled logs. -` This plugin can -designate trees for chopping when your stocks are low on logs. +.. dfhack-tool:: + :summary: Auto-harvest trees when low on stockpiled logs. + :tags: fort auto + :no-command: + +This plugin can designate trees for chopping when your stocks are low on logs. Usage:: diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 3feb22520..a5b57dfb1 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -1,11 +1,12 @@ autoclothing ============ -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` -:dfhack-keybind:`autoclothing` -:index:`Automatically manage clothing work orders. -` It allows you to -set how many of each clothing type every citizen should have. +.. dfhack-tool:: + :summary: Automatically manage clothing work orders. + :tags: fort auto jobs + +This command allows you to set how many of each clothing type every citizen +should have. Usage:: diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 3e8d291a0..0fd7ee23c 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -2,15 +2,20 @@ autodump ======== .. dfhack-tool:: + :summary: Automatically set items in a stockpile to be dumped. :tags: fort auto fps items stockpiles + :no-command: + +.. dfhack-command:: autodump + :summary: Teleports items marked for dumping to the cursor position. .. dfhack-command:: autodump-destroy-here + :summary: Destroy items marked for dumping under the cursor. .. dfhack-command:: autodump-destroy-item + :summary: Destroys the selected item. -:index:`Automatically set items in a stockpile to be dumped. -` When -`enabled `, this plugin adds an option to the :kbd:`q` menu for +When `enabled `, this plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``autodump`` option is selected for the stockpile, any items placed in the stockpile will automatically be designated to be dumped. @@ -44,8 +49,7 @@ Options called again with this option before the game is resumed, it cancels pending destroy actions. ``destroy-here`` - :index:`Destroy items marked for dumping under the cursor. - ` + Destroy items marked for dumping under the cursor. ``visible`` Only process items that are not hidden. ``hidden`` @@ -57,10 +61,8 @@ Examples -------- ``autodump`` - :index:`Teleports items marked for dumping to the cursor position. - ` + Teleports items marked for dumping to the cursor position. ``autodump destroy`` Destroys all unforbidden items marked for dumping ``autodump-destroy-item`` - :index:`Destroys the selected item. - ` + Destroys the selected item. diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index bfe8ad717..44077eb89 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -1,13 +1,13 @@ autofarm ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/buildings` -:dfhack-keybind:`autofarm` - -:index:`Automatically manage farm crop selection. -` This plugin periodically -scans your plant stocks and assigns crops to your farm plots based on which -plant stocks are low (as long as you have the appropriate seeds). The target -threshold for each crop type is configurable. + +.. dfhack-tool:: + :summary: Automatically manage farm crop selection. + :tags: fort auto buildings + +Periodically scan your plant stocks and assign crops to your farm plots based on +which plant stocks are low (as long as you have the appropriate seeds). The +target threshold for each crop type is configurable. Usage: diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index 11575aa7a..f95d74fea 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -1,15 +1,16 @@ autogems ======== -.. dfhack-tool:: autogems +.. dfhack-tool:: + :summary: Automatically cut rough gems. :tags: fort auto jobs :no-command: .. dfhack-command:: autogems-reload + :summary: Reloads the autogems configuration file. -:index:`Automatically cut rough gems. ` -This plugin periodically scans your stocks of rough gems and creates manager -orders for cutting them at a Jeweler's Workshop. +Automatically cut rough gems. This plugin periodically scans your stocks of +rough gems and creates manager orders for cutting them at a Jeweler's Workshop. Usage: @@ -17,10 +18,8 @@ Usage: Enables the plugin and starts autocutting gems according to its configuration. ``autogems-reload`` - :index:`Reloads the autogems configuration file. - ` You might need - to do this if you have manually modified the contents while the game is - running. + Reloads the autogems configuration file. You might need to do this if you + have manually modified the contents while the game is running. Run `gui/autogems` for a configuration UI, or access the ``Auto Cut Gems`` option from the Current Workshop Orders screen (:kbd:`o`-:kbd:`W`). diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index e0e1b2bac..1a4a96c8f 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -1,13 +1,14 @@ autohauler ========== -**Tags:** `tag/fort`, `tag/auto`, `tag/labors` -:dfhack-keybind:`autohauler` - -:index:`Automatically manage hauling labors. -` Similar to `autolabor`, but -instead of managing all labors, ``autohauler`` only addresses hauling labors, -leaving the assignment of skilled labors entirely up to you. You can use the -in-game `manipulator` UI or an external tool like Dwarf Therapist to do so. + +.. dfhack-tool:: + :summary: Automatically manage hauling labors. + :tags: fort auto labors + +Similar to `autolabor`, but instead of managing all labors, ``autohauler`` only +addresses hauling labors, leaving the assignment of skilled labors entirely up +to you. You can use the in-game `manipulator` UI or an external tool like Dwarf +Therapist to do so. Idle dwarves who are not on active military duty will be assigned the hauling labors; everyone else (including those currently hauling) will have the hauling diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 1d02fcfe8..d2fe49e87 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -1,12 +1,12 @@ autolabor ========= -**Tags:** `tag/fort`, `tag/auto`, `tag/labors` -:dfhack-keybind:`autolabor` -:index:`Automatically manage dwarf labors. -` Autolabor attempts to keep as -many dwarves as possible busy while allowing dwarves to specialize in specific -skills. +.. dfhack-tool:: + :summary: Automatically manage dwarf labors. + :tags: fort auto labors + +Autolabor attempts to keep as many dwarves as possible busy while allowing +dwarves to specialize in specific skills. Autolabor frequently checks how many jobs of each type are available and sets labors proportionally in order to get them all done quickly. Labors with diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 1c237f260..513a963d2 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -1,11 +1,14 @@ automaterial ============ -**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/buildings`, `tag/map` -:index:`Sorts building materials by recent usage. -` This makes building -constructions (walls, floors, fortifications, etc) much easier by saving you -from having to trawl through long lists of materials each time you place one. +.. dfhack-tool:: + :summary: Sorts building materials by recent usage. + :tags: fort productivity design buildings map + :no-command: + +This plugin makes building constructions (walls, floors, fortifications, etc) +much easier by saving you from having to trawl through long lists of materials +each time you place one. It moves the last used material for a given construction type to the top of the list, if there are any left. So if you build a wall with chalk blocks, the next diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 6c42cbb7e..508fdaeaf 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -2,14 +2,13 @@ automelt ======== .. dfhack-tool:: + :summary: Quickly designate items to be melted. :tags: fort auto items stockpiles :no-command: -:index:`Quickly designate items to be melted. -` When `enabled `, this -plugin adds an option to the :kbd:`q` menu for stockpiles. When the ``automelt`` -option is selected for the stockpile, any items placed in the stockpile will -automatically be designated to be melted. +When `enabled `, this plugin adds an option to the :kbd:`q` menu for +stockpiles. When the ``automelt`` option is selected for the stockpile, any +items placed in the stockpile will automatically be designated to be melted. Usage:: diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst index 2f874c452..b82a1a207 100644 --- a/docs/plugins/autonestbox.rst +++ b/docs/plugins/autonestbox.rst @@ -1,16 +1,21 @@ autonestbox =========== -**Tags:** `tag/fort`, `tag/auto`, `tag/animals` -:dfhack-keybind:`autonestbox` -Auto-assign egg-laying female pets to nestbox zones. Requires that you create -pen/pasture zones above nestboxes. If the pen is bigger than 1x1, the nestbox -must be in the top left corner. Only 1 unit will be assigned per pen, regardless -of the size. The age of the units is currently not checked since most birds grow -up quite fast. Egg layers who are also grazers will be ignored, since confining -them to a 1x1 pasture is not a good idea. Only tame and domesticated own units -are processed since pasturing half-trained wild egg layers could destroy your -neat nestbox zones when they revert to wild. +.. dfhack-tool:: + :summary: Auto-assign egg-laying female pets to nestbox zones. + :tags: fort auto animals + +To use this feature, you must create pen/pasture zones above nestboxes. If the +pen is bigger than 1x1, the nestbox must be in the top left corner. Only 1 unit +will be assigned per pen, regardless of the size. Egg layers who are also +grazers will be ignored, since confining them to a 1x1 pasture is not a good +idea. Only tame and domesticated own units are processed since pasturing +half-trained wild egg layers could destroy your neat nestbox zones when they +revert to wild. + +Note that the age of the units is not checked, so you might get some egg-laying +kids assigned to the nestbox zones. Most birds grow up quite fast, though, so +they should be adults and laying eggs soon enough. Usage: diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 35657fc88..f27fab122 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -1,12 +1,14 @@ autotrade ========= -**Tags:** `tag/fort`, `tag/auto`, `tag/items`, `tag/stockpiles` -:index:`Quickly designate items to be traded. -` When `enabled `, -this plugin adds an option to the :kbd:`q` menu for stockpiles. When the -``autotrade`` option is selected for the stockpile, any items placed in the -stockpile will automatically be designated to be traded. +.. dfhack-tool:: + :summary: Quickly designate items to be traded. + :tags: fort auto items stockpiles + :no-command: + +When `enabled `, this plugin adds an option to the :kbd:`q` menu for +stockpiles. When the ``autotrade`` option is selected for the stockpile, any +items placed in the stockpile will automatically be designated to be traded. Usage:: diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 48ffdd883..8654aa1ec 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -1,12 +1,13 @@ blueprint ========= -**Tags:** `tag/fort`, `tag/design`, `tag/quickfort`, `tag/map` -:dfhack-keybind:`blueprint` -:index:`Record a live game map in a quickfort blueprint. -` With -``blueprint``, you can export the structure of a portion of your fortress in a -blueprint file that you (or anyone else) can later play back with `quickfort`. +.. dfhack-tool:: + :summary: Record a live game map in a quickfort blueprint. + :tags: fort design quickfort map + +With ``blueprint``, you can export the structure of a portion of your fortress +in a blueprint file that you (or anyone else) can later play back with +`quickfort`. Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` subdirectory of your DF folder. The map area to turn into a blueprint is either diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 1f8ee1b2b..cd8835f37 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -1,7 +1,9 @@ building-hacks ============== -**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` -Provides a Lua API for creating powered workshops. +.. dfhack-tool:: + :summary: Provides a Lua API for creating powered workshops. + :tags: fort mod buildings + :no-command: See `building-hacks-api` for more details. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index be95dc6f1..ea9d78f64 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -1,11 +1,11 @@ buildingplan ============ -**Tags:** `tag/fort`, `tag/design`, `tag/quickfort`, `tag/buildings`, `tag/map` -:dfhack-keybind:`buildingplan` -:index:`Plan building construction before you have materials. -` This -plugin adds a planning mode for building placement. You can then place +.. dfhack-tool:: + :summary: Plan building construction before you have materials. + :tags: fort design quickfort buildings map + +This plugin adds a planning mode for building placement. You can then place furniture, constructions, and other buildings before the required materials are available, and they will be created in a suspended state. Buildingplan will periodically scan for appropriate items, and the jobs will be unsuspended when diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 4728e2cd5..92e62f08b 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -1,24 +1,23 @@ burrows ======= -**Tags:** `tag/fort`, `tag/auto`, `tag/productivity`, `tag/units` -:dfhack-keybind:`burrow` -:index:`Auto-expand burrows as you dig. -` When a wall inside a burrow -with a name ending in ``+`` is dug out, the burrow will be extended to -newly-revealed adjacent walls. Note that digging 1-wide corridors with the miner -inside the burrow is **SLOW**. +.. dfhack-tool:: + :summary: Auto-expand burrows as you dig. + :tags: fort auto productivity units + :no-command: -You can also use the ``burrow`` command to -:index:`quickly add units/tiles to burrows. -` +.. dfhack-command:: burrow + :summary: Quickly add units/tiles to burrows. + +When a wall inside a burrow with a name ending in ``+`` is dug out, the burrow +will be extended to newly-revealed adjacent walls. Usage: ``burrow enable auto-grow`` When a wall inside a burrow with a name ending in '+' is dug out, the burrow will be extended to newly-revealed adjacent walls. This final '+' may be - omitted in burrow name args of other ``burrows`` commands. Note that digging + omitted in burrow name args of other ``burrow`` commands. Note that digging 1-wide corridors with the miner inside the burrow is SLOW. ``burrow disable auto-grow`` Disables auto-grow processing. diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index 68c53f821..e49698822 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -1,15 +1,15 @@ changeitem ========== -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/items` -:dfhack-keybind:`changeitem` - -:index:`Change item material and base quality. -` By default, a change is -only allowed if the existing and desired item materials are of the same subtype -(for example wood -> wood, stone -> stone, etc). But since some transformations -work pretty well and may be desired you can override this with ``force``. Note -that forced changes can possibly result in items that crafters and haulers -refuse to touch. + +.. dfhack-tool:: + :summary: Change item material or base quality. + :tags: adventure fort armok items + +By default, a change is only allowed if the existing and desired item materials +are of the same subtype (for example wood -> wood, stone -> stone, etc). But +since some transformations work pretty well and may be desired you can override +this with ``force``. Note that forced changes can possibly result in items that +crafters and haulers refuse to touch. Usage: diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 1267fbdac..e44d6f317 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -1,13 +1,14 @@ changelayer =========== -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`changelayer` -:index:`Change the material of an entire geology layer. -` Note that one -layer can stretch across many z-levels, and changes to the geology layer will -affect all surrounding regions, not just your embark! Mineral veins and gem -clusters will not be affected. Use `changevein` if you want to modify those. +.. dfhack-tool:: + :summary: Change the material of an entire geology layer. + :tags: fort armok map + +Note that one layer can stretch across many z-levels, and changes to the geology +layer will affect all surrounding regions, not just your embark! Mineral veins +and gem clusters will not be affected. Use `changevein` if you want to modify +those. tl;dr: You will end up with changing large areas in one go, especially if you use it in lower z levels. Use this command with care! diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index 96c68de76..4811c416c 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -1,13 +1,13 @@ changevein ========== -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`changevein` -:index:`Change the material of a mineral inclusion. -` You can change it to -any incorganic material RAW id. Note that this command only affects tiles within -the current 16x16 block - for large veins and clusters, you will need to use -this command multiple times. +.. dfhack-tool:: + :summary: Change the material of a mineral inclusion. + :tags: fort armok map + +You can change a vein to any incorganic material RAW id. Note that this command +only affects tiles within the current 16x16 block - for large veins and +clusters, you will need to use this command multiple times. You can use the `probe` command to discover the material RAW ids for existing veins that you want to duplicate. diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index c47a8c6dd..9c0309105 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -1,13 +1,14 @@ cleanconst ========== -**Tags:** `tag/fort`, `tag/fps`, `tag/map` -:dfhack-keybind:`cleanconst` - -:index:`Cleans up construction materials. -` This tool alters all -constructions on the map so that they spawn their building component when they -are disassembled, allowing their actual build items to be safely deleted. This -can improve FPS when you have many constructions on the map. + +.. dfhack-tool:: + :summary: Cleans up construction materials. + :tags: fort fps map + +This tool alters all constructions on the map so that they spawn their building +component when they are disassembled, allowing their actual build items to be +safely deleted. This can improve FPS when you have many constructions on the +map. Usage:: diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 20fa792db..42e585da1 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -3,15 +3,22 @@ cleaners ======== -**Tags:** `tag/adventure`, `tag/fort`, `tag/fps`, `tag/items`, `tag/map`, `tag/units` -:dfhack-keybind:`clean` -:dfhack-keybind:`spotclean` -:index:`Removes contaminants. ` More -specifically, it cleans all the splatter that get scattered all over the map and -that clings to your items and units. In an old fortress, cleaning with this tool -can significantly reduce FPS lag. It can also spoil your !!FUN!!, so think -before you use it. +.. dfhack-tool:: + :summary: Provides commands for cleaning spatter from the map. + :tags: adventure fort fps items map units + :no-command: + +.. dfhack-command:: clean + :summary: Removes contaminants. + +.. dfhack-command:: spotclean + :summary: Remove all contaminants from the tile under the cursor. + +This plugin provides commands that clean the splatter that get scattered all +over the map and that clings to your items and units. In an old fortress, +cleaning with this tool can significantly reduce FPS lag! It can also spoil your +!!FUN!!, so think before you use it. Usage:: @@ -21,11 +28,10 @@ Usage:: By default, cleaning the map leaves mud and snow alone. Note that cleaning units includes hostiles, and that cleaning items removes poisons from weapons. -``spotclean`` works like ``clean map snow mud``, -:index:`removing all contaminants from the tile under the cursor. -` This is ideal -if you just want to clean a specific tile but don't want the `clean` command to -remove all the glorious blood from your entranceway. +``spotclean`` works like ``clean map snow mud``, removing all contaminants from +the tile under the cursor. This is ideal if you just want to clean a specific +tile but don't want the `clean` command to remove all the glorious blood from +your entranceway. Examples -------- diff --git a/docs/plugins/cleanowned.rst b/docs/plugins/cleanowned.rst index 409c39345..7271ce402 100644 --- a/docs/plugins/cleanowned.rst +++ b/docs/plugins/cleanowned.rst @@ -1,13 +1,14 @@ cleanowned ========== -**Tags:** `tag/fort`, `tag/productivity`, `tag/items` -:dfhack-keybind:`cleanowned` - -:index:`Confiscates and dumps garbage owned by dwarves. -` This tool gets -dwarves to give up ownership of scattered items and items with heavy wear and -then marks those items for dumping. Now you can finally get your dwarves to give -up their rotten food and tattered loincloths and go get new ones! + +.. dfhack-tool:: + :summary: Confiscates and dumps garbage owned by dwarves. + :tags: fort productivity items + +This tool gets dwarves to give up ownership of scattered items and items with +heavy wear and then marks those items for dumping. Now you can finally get your +dwarves to give up their rotten food and tattered loincloths and go get new +ones! Usage:: diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 312734b97..278741f36 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -1,10 +1,9 @@ command-prompt ============== -**Tags:** `tag/system` -:dfhack-keybind:`command-prompt` -:index:`An in-game DFHack terminal where you can enter other commands. -` +.. dfhack-tool:: + :summary: An in-game DFHack terminal where you can run other commands. + :tags: system Usage:: diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index de2556b6f..aef18c9c0 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -1,12 +1,12 @@ confirm ======= -**Tags:** `tag/fort`, `tag/interface` -:dfhack-keybind:`confirm` -:index:`Adds confirmation dialogs for destructive actions. -` Now you can get -the chance to avoid seizing goods from traders or deleting a hauling route in -case you hit the key accidentally. +.. dfhack-tool:: + :summary: Adds confirmation dialogs for destructive actions. + :tags: fort interface + +Now you can get the chance to avoid seizing goods from traders or deleting a +hauling route in case you hit the key accidentally. Usage: diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index 38f072e62..dfd286395 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -1,12 +1,13 @@ createitem ========== -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/items` -:dfhack-keybind:`createitem` -:index:`Create arbitrary items. ` You can -create new items of any type and made of any material. A unit must be selected -in-game to use this command. By default, items created are spawned at the feet -of the selected unit. +.. dfhack-tool:: + :summary: Create arbitrary items. + :tags: adventure fort armok items + +You can create new items of any type and made of any material. A unit must be +selected in-game to use this command. By default, items created are spawned at +the feet of the selected unit. Specify the item and material information as you would indicate them in custom reaction raws, with the following differences: diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index ecb4dbd2f..b2e2943ba 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -1,9 +1,10 @@ cursecheck ========== -**Tags:** `tag/system`, `tag/interface` -:dfhack-keybind:`cursecheck` -:index:`Check for cursed creatures. ` +.. dfhack-tool:: + :summary: Check for cursed creatures. + :tags: system interface + This command checks a single map tile (or the whole map/world) for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). diff --git a/docs/plugins/cxxrandom.rst b/docs/plugins/cxxrandom.rst index 0016fb8d0..19788e4a4 100644 --- a/docs/plugins/cxxrandom.rst +++ b/docs/plugins/cxxrandom.rst @@ -1,7 +1,9 @@ cxxrandom ========= -**Tags:** `tag/dev` -Provides a Lua API for random distributions. +.. dfhack-tool:: + :summary: Provides a Lua API for random distributions. + :tags: dev + :no-command: See `cxxrandom-api` for details. diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst index 4cc94b217..a9e601c77 100644 --- a/docs/plugins/debug.rst +++ b/docs/plugins/debug.rst @@ -1,11 +1,15 @@ debug ===== -**Tags:** `tag/dev` -:dfhack-keybind:`debugfilter` -:index:`Configure verbosity of DFHack debug output. -` Debug output is -grouped by plugin name, category name, and verbosity level. +.. dfhack-tool:: + :summary: Provides commands for controlling debug log verbosity. + :tags: dev + :no-command: + +.. dfhack-command:: debugfilter + :summary: Configure verbosity of DFHack debug output. + +Debug output is grouped by plugin name, category name, and verbosity level. The verbosity levels are: diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index 6126d2828..4bb96ef0a 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -1,11 +1,11 @@ deramp ====== -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`deramp` -:index:`Removes all ramps designated for removal from the map. -` It also -removes any "floating" down ramps that can remain after a cave-in. +.. dfhack-tool:: + :summary: Removes all ramps designated for removal from the map. + :tags: fort armok map + +It also removes any "floating" down ramps that can remain after a cave-in. Usage:: diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index 7d3beedc0..0562e000a 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -1,14 +1,14 @@ dig-now ======= -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`dig-now` -:index:`Instantly complete dig designations. -` This tool will magically -complete non-marker dig designations, modifying tile shapes and creating -boulders, ores, and gems as if a miner were doing the mining or engraving. By -default, the entire map is processed and boulder generation follows standard -game rules, but the behavior is configurable. +.. dfhack-tool:: + :summary: Instantly complete dig designations. + :tags: fort armok map + +This tool will magically complete non-marker dig designations, modifying tile +shapes and creating boulders, ores, and gems as if a miner were doing the mining +or engraving. By default, the entire map is processed and boulder generation +follows standard game rules, but the behavior is configurable. Note that no units will get mining or engraving experience for the dug/engraved tiles. diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index f4e0c8acb..1761b7085 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -3,47 +3,58 @@ dig === -**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/map` -:dfhack-keybind:`digv` -:dfhack-keybind:`digvx` -:dfhack-keybind:`digl` -:dfhack-keybind:`diglx` -:dfhack-keybind:`digcircle` -:dfhack-keybind:`digtype` -:dfhack-keybind:`digexp` -Make complicated dig patterns easy. +.. dfhack-tool:: + :summary: Provides commands for designating tiles for digging. + :tags: fort productivity design map + :no-command: + +.. dfhack-command:: digv + :summary: Designate all of the selected vein for digging. + +.. dfhack-command:: digvx + :summary: Dig a vein across z-levels, digging stairs as needed. + +.. dfhack-command:: digl + :summary: Dig all of the selected layer stone. + +.. dfhack-command:: diglx + :summary: Dig layer stone across z-levels, digging stairs as needed. + +.. dfhack-command:: digcircle + :summary: Designate circles. + +.. dfhack-command:: digtype + :summary: Designate all vein tiles of the selected type. + +.. dfhack-command:: digexp + :summary: Designate dig patterns for exploratory mining. + +This plugin provides commands to make complicated dig patterns easy. Usage: ``digv [x] [-p]`` - :index:`Designate all of the selected vein for digging. - ` + Designate all of the selected vein for digging. ``digvx [-p]`` - :index:`Dig a vein across z-levels, digging stairs as needed. - ` - This is an alias for ``digv x``. + Dig a vein across z-levels, digging stairs as needed. This is an alias for + ``digv x``. ``digl [x] [undo] [-p]`` - :index:`Dig all of the selected layer stone. - ` If ``undo`` is specified, - removes the designation instead (for if you accidentally set 50 levels at - once). + Dig all of the selected layer stone. If ``undo`` is specified, removes the + designation instead (for if you accidentally set 50 levels at once). ``diglx [-p]`` - :index:`Dig layer stone across z-levels, digging stairs as needed. - ` This - is an alias for ``digl x``. + Dig layer stone across z-levels, digging stairs as needed. This is an alias + for ``digl x``. ``digcircle [] [] [] [] [-p]`` - :index:`Designate circles. ` The diameter - is the number of tiles across the center of the circle that you want to dig. - See the `digcircle`_ section below for options. + Designate circles. The diameter is the number of tiles across the center of + the circle that you want to dig. See the `digcircle`_ section below for + options. ``digtype [] [-p]`` - :index:`Designate all vein tiles of the selected type. - ` See the `digtype`_ - section below for options. + Designate all vein tiles of the selected type. See the `digtype`_ section + below for options. ``digexp [] [] [-p]`` - :index:`Designate dig patterns for exploratory mining. - ` See the `digexp`_ - section below for options + Designate dig patterns for exploratory mining. See the `digexp`_ section + below for options. All commands support specifying the priority of the dig designations with ``-p``, where the number is from 1 to 7. If a priority is not specified, diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst index 1a17c32a7..60bbc9423 100644 --- a/docs/plugins/digFlood.rst +++ b/docs/plugins/digFlood.rst @@ -1,13 +1,16 @@ digFlood ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/map` -:dfhack-keybind:`digFlood` - -:index:`Digs out veins as they are discovered. -` It will only dig out -appropriate tiles that are adjacent to a just-finished dig job, so if you want -to autodig a vein that has already been discovered, you may need to manually -designate one tile of the tile for digging to get started. + +.. dfhack-tool:: + :summary: Digs out veins as they are discovered. + :tags: fort auto map + +Once you register specific vein types, this tool will automatically designate +tiles of those types of veins for digging as your miners complete adjacent +mining jobs. Note that it will *only* dig out tiles that are adjacent to a +just-finished dig job, so if you want to autodig a vein that has already been +discovered, you may need to manually designate one tile of the tile for digging +to get started. Usage: diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index 4891bf8d4..df54e7008 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -1,10 +1,9 @@ diggingInvaders =============== -**Tags:** `tag/fort`, `tag/mod`, `tag/map` -:dfhack-keybind:`diggingInvaders` -:index:`Invaders dig and destroy to get to your dwarves. -` +.. dfhack-tool:: + :summary: Invaders dig and destroy to get to your dwarves. + :tags: fort mod map Usage: diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index a7f42bb81..73d8a8c19 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -1,11 +1,11 @@ dwarfmonitor ============ -**Tags:** `tag/fort`, `tag/inspection`, `tag/units` -:dfhack-keybind:`dwarfmonitor` -:index:`Measure fort happiness and efficiency. -` Also show heads-up -display widgets with live fort statistics. +.. dfhack-tool:: + :summary: Measure fort happiness and efficiency. + :tags: fort inspection units + +It can also show heads-up display widgets with live fort statistics. Usage: diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index 2db4a9595..f607e85cd 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -1,10 +1,9 @@ dwarfvet ======== -**Tags:** `tag/fort`, `tag/mod`, `tag/animals` -:dfhack-keybind:`dwarfvet` -:index:`Allows animals to be treated at animal hospitals. -` +.. dfhack-tool:: + :summary: Allows animals to be treated at animal hospitals. + :tags: fort mod animals Annoyed your dragons become useless after a minor injury? Well, with dwarfvet, injured animals will be treated at an animal hospital, which is simply a hospital diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index b9a7e4ee2..22c18996a 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -1,14 +1,15 @@ embark-assistant ================ -**Tags:** `tag/fort`, `tag/embark`, `tag/interface` -:dfhack-keybind:`embark-assistant` -:index:`Embark site selection support. -` Run this command while the -pre-embark screen is displayed to show extended (and correct(?)) resource -information for the embark rectangle as well as normally undisplayed sites in -the current embark region. You will also have access to a site selection tool -with far more options than DF's vanilla search tool. +.. dfhack-tool:: + :summary: Embark site selection support. + :tags: fort embark interface + +Run this command while the pre-embark screen is displayed to show extended (and +reasonably correct) resource information for the embark rectangle as well as +normally undisplayed sites in the current embark region. You will also have +access to a site selection tool with far more options than DF's vanilla search +tool. If you enable the plugin, you'll also be able to invoke ``embark-assistant`` with the :kbd:`A` key on the pre-embark screen. diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 52532e4f2..58b02d47e 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -1,10 +1,9 @@ embark-tools ============ -**Tags:** `tag/fort`, `tag/embark`, `tag/interface` -:dfhack-keybind:`embark-tools` -:index:`Extend the embark screen functionality. -` +.. dfhack-tool:: + :summary: Extend the embark screen functionality. + :tags: fort embark interface Usage:: diff --git a/docs/plugins/eventful.rst b/docs/plugins/eventful.rst index 102287b77..d5bd55956 100644 --- a/docs/plugins/eventful.rst +++ b/docs/plugins/eventful.rst @@ -1,7 +1,9 @@ eventful ======== -**Tags:** `tag/dev`, `tag/mod` -Provides a Lua API for reacting to in-game events. +.. dfhack-tool:: + :summary: Provides a Lua API for reacting to in-game events. + :tags: dev mod + :no-command: See `eventful-api` for details. diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index 2e09b8a4d..7042a71a3 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -1,9 +1,9 @@ fastdwarf ========= -**Tags:** `tag/fort`, `tag/armok`, `tag/units` -:dfhack-keybind:`fastdwarf` -Dwarves teleport and/or finish jobs instantly. +.. dfhack-tool:: + :summary: Dwarves teleport and/or finish jobs instantly. + :tags: fort armok units Usage:: diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index acc4dabc8..ea48fd385 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -3,24 +3,28 @@ filltraffic =========== -**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/map` -:dfhack-keybind:`filltraffic` -:dfhack-keybind:`alltraffic` -:dfhack-keybind:`restrictice` -:dfhack-keybind:`restrictliquids` - -Usage: - -``filltraffic []`` - Set traffic designations using flood-fill starting at the cursor. Flood - filling stops at walls and doors. -``alltraffic `` - Set traffic designations for every single tile of the map - useful for - resetting traffic designations. -``restrictice`` - Restrict traffic on all tiles on top of visible ice. -``restrictliquids`` - Restrict traffic on all visible tiles with liquid. + +.. dfhack-tool:: + :summary: Set traffic designations using flood-fill starting at the cursor. + :tags: fort productivity design map + +.. dfhack-command:: alltraffic + :summary: Set traffic designations for every single tile of the map. + +.. dfhack-command:: restrictice + :summary: Restrict traffic on all tiles on top of visible ice. + +.. dfhack-command:: restrictliquids + :summary: Restrict traffic on all visible tiles with liquid. + +Usage:: + + filltraffic [] + alltraffic + restrictice + restrictliquids + +For ``filltraffic``, flood filling stops at walls and doors. Examples -------- diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 28479791f..117f97467 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -1,10 +1,12 @@ fix-unit-occupancy ================== -**Tags:** `tag/fort`, `tag/fix`, `tag/map` -:dfhack-keybind:`` -Fix phantom unit occupancy issues. For example, if you see "unit blocking tile" -messages that you can't account for (:bug:`3499`), this tool can help. +.. dfhack-tool:: + :summary: Fix phantom unit occupancy issues. + :tags: fort fix map + +If you see "unit blocking tile" messages that you can't account for +(:bug:`3499`), this tool can help. Usage:: diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index 873e1770a..045b3c8da 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -1,10 +1,12 @@ fixveins ======== -**Tags:** `tag/fort`, `tag/fix`, `tag/map` -:dfhack-keybind:`fixveins` -Restore missing mineral inclusions. This tool can also remove invalid references -to mineral inclusions if you broke your embark with tools like `tiletypes`. +.. dfhack-tool:: + :summary: Restore missing mineral inclusions. + :tags: fort fix map + +This tool can also remove invalid references to mineral inclusions if you broke +your embark with tools like `tiletypes`. Usage:: diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index a347f00e0..994057878 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -1,10 +1,12 @@ flows ===== -**Tags:** `tag/fort`, `tag/inspection`, `tag/map` -:dfhack-keybind:`flows` -Counts map blocks with flowing liquids.. If you suspect that your magma sea -leaks into HFS, you can use this tool to be sure without revealing the map. +.. dfhack-tool:: + :summary: Counts map blocks with flowing liquids. + :tags: fort inspection map + +If you suspect that your magma sea leaks into HFS, you can use this tool to be +sure without revealing the map. Usage:: diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst index 5f43e2fdd..b6bc610b2 100644 --- a/docs/plugins/follow.rst +++ b/docs/plugins/follow.rst @@ -1,11 +1,13 @@ follow ====== -**Tags:** `tag/fort`, `tag/interface`, `tag/units` -:dfhack-keybind:`follow` -Make the screen follow the selected unit. Once you exit from the current menu or -cursor mode, the screen will stay centered on the unit. Handy for watching -dwarves running around. Deactivated by moving the cursor manually. +.. dfhack-tool:: + :summary: Make the screen follow the selected unit. + :tags: fort interface units + +Once you exit from the current menu or cursor mode, the screen will stay +centered on the unit. Handy for watching dwarves running around. Deactivated by +moving the cursor manually. Usage:: diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 51d48390c..acfd8f462 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -1,14 +1,15 @@ forceequip ========== -**Tags:** `tag/adventure`, `tag/fort`, `tag/items`, `tag/units` -:dfhack-keybind:`forceequip` -Move items into a unit's inventory. This tool is typically used to equip -specific clothing/armor items onto a dwarf, but can also be used to put armor -onto a war animal or to add unusual items (such as crowns) to any unit. Make -sure the unit you want to equip is standing on the target items, which must be -on the ground and be unforbidden. If multiple units are standing on the same -tile, the first one will be equipped. +.. dfhack-tool:: + :summary: Move items into a unit's inventory. + :tags: adventure fort items units + +This tool is typically used to equip specific clothing/armor items onto a dwarf, +but can also be used to put armor onto a war animal or to add unusual items +(such as crowns) to any unit. Make sure the unit you want to equip is standing +on the target items, which must be on the ground and be unforbidden. If multiple +units are standing on the same tile, the first one will be equipped. The most reliable way to set up the environment for this command is to pile target items on a tile of floor with a garbage dump activity zone or the diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst index 39983940b..6af13595a 100644 --- a/docs/plugins/generated-creature-renamer.rst +++ b/docs/plugins/generated-creature-renamer.rst @@ -1,12 +1,20 @@ generated-creature-renamer ========================== -**Tags:** `tag/adventure`, `tag/fort`, `tag/legends`, `tag/units` -:dfhack-keybind:`list-generated` -:dfhack-keybind:`save-generated-raws` -Automatically renames generated creatures. Now, forgotten beasts, titans, -necromancer experiments, etc. will have raw token names that match the -description given in-game instead of unreadable generated strings. +.. dfhack-tool:: + :summary: Automatically renames generated creatures. + :tags: adventure fort legends units + :no-command: + +.. dfhack-command:: list-generated + :summary: List the token names of all generated creatures. + +.. dfhack-command:: save-generated-raws + :summary: Export a creature graphics file for modding. + +Now, forgotten beasts, titans, necromancer experiments, etc. will have raw token +names that match the description given in-game instead of unreadable generated +strings. Usage: diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 21325e8f2..502f6e9ae 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -1,10 +1,12 @@ getplants ========= -**Tags:** `tag/fort`, `tag/productivity` -:dfhack-keybind:`getplants` -Designate trees for chopping and shrubs for gathering. Specify the types of -trees to cut down and/or shrubs to gather by their plant names. +.. dfhack-tool:: + :summary: Designate trees for chopping and shrubs for gathering. + :tags: fort productivity + +Specify the types of trees to cut down and/or shrubs to gather by their plant +names. Usage: diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst index fbf4a9a76..7d30d486e 100644 --- a/docs/plugins/hotkeys.rst +++ b/docs/plugins/hotkeys.rst @@ -1,11 +1,12 @@ hotkeys ======= -**Tags:** `tag/system`, `tag/productivity`, `tag/interface` -:dfhack-keybind:`hotkeys` -Show all dfhack keybindings in current context. The command opens an in-game -screen showing which DFHack keybindings are active in the current context. -See also `hotkey-notes`. +.. dfhack-tool:: + :summary: Show all dfhack keybindings for the current context. + :tags: system productivity interface + +The command opens an in-game screen showing which DFHack keybindings are active +in the current context. See also `hotkey-notes`. Usage:: diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index ceb08aa0e..46f30c24c 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -1,10 +1,13 @@ infiniteSky =========== -**Tags:** `tag/fort`, `tag/map` -:dfhack-keybind:`infiniteSky` -Automatically allocates new z-levels of sky at the top of the map as you build -up, or on request allocates many levels all at once. +.. dfhack-tool:: + :summary: Automatically allocates new z-levels of sky + :tags: fort map + +If enabled, this plugin will automatically allocate new z-levels of sky at the +top of the map as you build up. Or it can allocate one or many additional levels +at your command. Usage: diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 84babe66a..1fa439b29 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -1,5 +1,9 @@ isoworldremote ============== -**Tags:** `tag/dev`, `tag/mod` -Provides a `remote API ` used by Isoworld. +.. dfhack-tool:: + :summary: Provides a remote API used by Isoworld. + :tags: dev mod + :no-command: + +See `remote` for related remote APIs. diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index 5dcb0c85f..46b221f89 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -2,12 +2,20 @@ jobutils ======== -**Tags:** `tag/fort`, `tag/inspection`, `tag/jobs` -:dfhack-keybind:`job` -:dfhack-keybind:`job-duplicate` -:dfhack-keybind:`job-material` -Inspect or modify details of workshop jobs. +.. dfhack-tool:: + :summary: Provides commands for interacting with jobs. + :tags: fort inspection jobs + :no-command: + +.. dfhack-command:: job + :summary: Inspect or modify details of workshop jobs. + +.. dfhack-command:: job-duplicate + :summary: Duplicates the highlighted job. + +.. dfhack-command:: job-material + :summary: Alters the material of the selected job. Usage: diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index 1a25cedae..b168a3b7a 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -1,12 +1,14 @@ labormanager ============ -**Tags:** `tag/fort`, `tag/auto`, `tag/labors` -:dfhack-keybind:`labormanager` -Automatically manage dwarf labors. Labormanager is derived from `autolabor` -but uses a completely different approach to assigning jobs to dwarves. While -autolabor tries to keep as many dwarves busy as possible, labormanager instead -strives to get jobs done as quickly as possible. +.. dfhack-tool:: + :summary: Automatically manage dwarf labors. + :tags: fort auto labors + +Labormanager is derived from `autolabor` but uses a completely different +approach to assigning jobs to dwarves. While autolabor tries to keep as many +dwarves busy as possible, labormanager instead strives to get jobs done as +quickly as possible. Labormanager frequently scans the current job list, current list of dwarves, and the map to determine how many dwarves need to be assigned to what labors in diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index 43bb06072..f42cc0777 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -1,10 +1,11 @@ lair ==== -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`lair` -Mark the map as a monster lair. This avoids item scatter when the fortress is -abandoned. +.. dfhack-tool:: + :summary: Mark the map as a monster lair. + :tags: fort armok map + +This avoids item scatter when the fortress is abandoned. Usage: diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index 808089591..a498364fc 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -2,9 +2,13 @@ liquids ======= -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`liquids` -:dfhack-keybind:`liquids-here` + +.. dfhack-tool:: + :summary: Place magma, water or obsidian. + :tags: adventure fort armok map + +.. dfhack-command:: liquids-here + :summary: Spawn liquids on the selected tile. Place magma, water or obsidian. See `gui/liquids` for an in-game interface for this functionality. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index cbbf09f7e..24b30c7a6 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -1,7 +1,9 @@ luasocket ========= -**Tags:** `tag/dev`, `tag/mod` -Provides a Lua API for accessing network sockets. +.. dfhack-tool:: + :summary: Provides a Lua API for accessing network sockets. + :tags: dev mod + :no-command: See `luasocket-api` for details. diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 07d1d4ab6..26223587d 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -1,9 +1,12 @@ manipulator =========== -**Tags:** `tag/fort`, `tag/productivity`, `tag/interface`, `tag/labors` -An in-game labor management interface. It is equivalent to the popular Dwarf -Therapist utility. +.. dfhack-tool:: + :summary: An in-game labor management interface. + :tags: fort productivity interface labors + :no-command: + +It is equivalent to the popular Dwarf Therapist utility. To activate, open the unit screen and press :kbd:`l`. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index 6b7f1a5d9..7754af8b7 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -1,7 +1,9 @@ map-render ========== -**Tags:** `tag/dev` -Provides a Lua API for rerendering portions of the map. +.. dfhack-tool:: + :summary: Provides a Lua API for rerendering portions of the map. + :tags: dev + :no-command: See `map-render-api` for details. diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 5d11f98cb..af71ae19a 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -1,9 +1,9 @@ misery ====== -**Tags:** `tag/fort`, `tag/armok`, `tag/units` -:dfhack-keybind:`misery` -Increase the intensity of negative dwarven thoughts. +.. dfhack-tool:: + :summary: Increase the intensity of negative dwarven thoughts. + :tags: fort armok units When enabled, negative thoughts that your dwarves have will multiply by the specified factor. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 8c33d6ec3..db94e6bac 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -1,9 +1,9 @@ mode ==== -**Tags:** `tag/dev` -:dfhack-keybind:`mode` -This command lets you see and change the game mode directly. +.. dfhack-tool:: + :summary: See and change the game mode. + :tags: dev .. warning:: diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index 7e01de6d0..eb6550d60 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -1,7 +1,9 @@ mousequery ========== -**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` -:dfhack-keybind:`mousequery` + +.. dfhack-tool:: + :summary: Adds mouse controls to the DF interface. + :tags: fort productivity interface Adds mouse controls to the DF interface. For example, with ``mousequery`` you can click on buildings to configure them, hold the mouse button to draw dig diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst index 1558a7357..9182eee09 100644 --- a/docs/plugins/nestboxes.rst +++ b/docs/plugins/nestboxes.rst @@ -1,11 +1,14 @@ nestboxes ========= -**Tags:** `tag/fort`, `tag/auto`, `tag/animals` -Protect fertile eggs incubating in a nestbox. This plugin will automatically -scan for and forbid fertile eggs incubating in a nestbox so that dwarves won't -come to collect them for eating. The eggs will hatch normally, even when -forbidden. +.. dfhack-tool:: + :summary: Protect fertile eggs incubating in a nestbox. + :tags: fort auto animals + :no-command: + +This plugin will automatically scan for and forbid fertile eggs incubating in a +nestbox so that dwarves won't come to collect them for eating. The eggs will +hatch normally, even when forbidden. Usage:: diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 3be1f82b2..9403c6c8e 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -1,9 +1,9 @@ orders ====== -**Tags:** `tag/fort`, `tag/productivity`, `tag/jobs` -:dfhack-keybind:`orders` -Manage manager orders. +.. dfhack-tool:: + :summary: Manage manager orders. + :tags: fort productivity jobs Usage: diff --git a/docs/plugins/pathable.rst b/docs/plugins/pathable.rst index 6368d1c7e..800580017 100644 --- a/docs/plugins/pathable.rst +++ b/docs/plugins/pathable.rst @@ -1,8 +1,10 @@ pathable ======== -**Tags:** `tag/dev`, `tag/inspection`, `tag/interface`, `tag/map` -Marks tiles that are reachable from the cursor. This plugin provides a Lua API, -but no direct commands. +.. dfhack-tool:: + :summary: Marks tiles that are reachable from the cursor. + :tags: dev inspection interface map + :no-command: -See `pathable-api` for details. +This plugin provides a Lua API, but no direct commands. See `pathable-api` for +details. diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index 95a9e60bd..f3d011308 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -1,16 +1,17 @@ petcapRemover ============= -**Tags:** `tag/fort`, `tag/armok`, `tag/animals` -:dfhack-keybind:`petcapRemover` -Modify the pet population cap. In vanilla DF, pets will not reproduce unless the -population is below 50 and the number of children of that species is below a -certain percentage. This plugin allows removing these restrictions and setting -your own thresholds. Pets still require PET or PET_EXOTIC tags in order to -reproduce. In order to make population more stable and avoid sudden population -booms as you go below the raised population cap, this plugin counts pregnancies -toward the new population cap. It can still go over, but only in the case of -multiple births. +.. dfhack-tool:: + :summary: Modify the pet population cap. + :tags: fort armok animals + +In vanilla DF, pets will not reproduce unless the population is below 50 and the +number of children of that species is below a certain percentage. This plugin +allows removing these restrictions and setting your own thresholds. Pets still +require PET or PET_EXOTIC tags in order to reproduce. In order to make +population more stable and avoid sudden population booms as you go below the +raised population cap, this plugin counts pregnancies toward the new population +cap. It can still go over, but only in the case of multiple births. Usage: diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index 2021bc103..d28119af9 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -2,10 +2,14 @@ plants ====== -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`plant` -Grow shrubs or trees. +.. dfhack-tool:: + :summary: Provides commands that interact with plants. + :tags: adventure fort armok map + :no-command: + +.. dfhack-command:: plant + :summary: Create a plant or make an existing plant grow up. Usage: diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 72a49b72c..01e75491a 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -1,7 +1,11 @@ power-meter =========== -**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` -Allow presure plates to measure power. If you run `gui/power-meter` while -building a pressure plate, the pressure plate can be modified to detect power -being supplied to gear boxes built in the four adjacent N/S/W/E tiles. +.. dfhack-tool:: + :summary: Allow presure plates to measure power. + :tags: fort mod buildings + :no-command: + +If you run `gui/power-meter` while building a pressure plate, the pressure +plate can be modified to detect power being supplied to gear boxes built in the +four adjacent N/S/W/E tiles. diff --git a/docs/plugins/probe.rst b/docs/plugins/probe.rst index 0285bb821..54436be68 100644 --- a/docs/plugins/probe.rst +++ b/docs/plugins/probe.rst @@ -1,11 +1,15 @@ probe ===== -**Tags:** `tag/adventure`, `tag/fort`, `tag/inspection`, `tag/map` -:dfhack-keybind:`probe` -:dfhack-keybind:`bprobe` -:dfhack-keybind:`cprobe` -Display low-level properties of selected objects. +.. dfhack-tool:: + :summary: Display low-level properties of the selected tile. + :tags: adventure fort inspection map + +.. dfhack-command:: bprobe + :summary: Display low-level properties of the selected building. + +.. dfhack-command:: cprobe + :summary: Display low-level properties of the selected unit. Usage: diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 7ff3b4313..dc6204c06 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -2,11 +2,17 @@ prospector ========== -**Tags:** `tag/fort`, `tag/embark`, `tag/inspection`, `tag/map` -:dfhack-keybind:`prospect` -Shows a summary of resources that exist on the map. It can also calculate an -estimate of resources available in the selected embark area. +.. dfhack-tool:: + :summary: Provides commands that help you analyze natural resources. + :tags: fort embark inspection map + :no-command: + +.. dfhack-command:: prospect + :summary: Shows a summary of resources that exist on the map. + +It can also calculate an estimate of resources available in the selected embark +area. Usage:: diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 98590a73e..72f077fcd 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -1,14 +1,15 @@ regrass ======= -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/animals` -:dfhack-keybind:`regrass` -Regrows all the grass. Use this command if your grazers have eaten everything -down to the dirt. +.. dfhack-tool:: + :summary: Regrows all the grass. + :tags: adventure fort armok animals + +Use this command if your grazers have eaten everything down to the dirt. Usage:: regrass [max] -Specify the 'max' option to pack more grass onto a tile than what the game +Specify the 'max' keyword to pack more grass onto a tile than what the game normally allows to give your grazers extra chewing time. diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 34695e19a..02bc04c2c 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -1,9 +1,11 @@ rename ====== -**Tags:** `tag/adventure`, `tag/fort`, `tag/productivity` -:dfhack-keybind:`rename` -Easily rename things. Use `gui/rename` for an in-game interface. +.. dfhack-tool:: + :summary: Easily rename things. + :tags: adventure fort productivity + +Use `gui/rename` for an in-game interface. Usage: diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index 83e9ee461..680676fa3 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -1,10 +1,12 @@ rendermax ========= -**Tags:** `tag/adventure`, `tag/fort`, `tag/mod` -:dfhack-keybind:`rendermax` -Modify the map lighting. This plugin provides a collection of OpenGL lighting -filters that affect how the map is drawn to the screen. +.. dfhack-tool:: + :summary: Modify the map lighting. + :tags: adventure fort mod + +This plugin provides a collection of OpenGL lighting filters that affect how the +map is drawn to the screen. Usage: diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index 2f28d7083..0713026e4 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -1,13 +1,18 @@ resume ====== -**Tags:** `tag/fort`, `tag/productivity` -:dfhack-keybind:`resume` - -Color planned buildings based on their suspend status. When enabled, this plugin -will display a colored 'X' over suspended buildings. When run as a command, it -can resume all suspended building jobs, allowing you to quickly recover if a -bunch of jobs were suspended due to the workers getting scared off by wildlife -or items temporarily blocking buildling sites. + +.. dfhack-tool:: + :summary: Color planned buildings based on their suspend status. + :tags: fort productivity + :no-command: + +.. dfhack-command:: resume + :summary: Resume all suspended building jobs. + +When enabled, this plugin will display a colored 'X' over suspended buildings. +When run as a command, it can resume all suspended building jobs, allowing you +to quickly recover if a bunch of jobs were suspended due to the workers getting +scared off by wildlife or items temporarily blocking buildling sites. Usage:: diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index fb69d9a02..122280a1e 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -2,17 +2,29 @@ reveal ====== -**Tags:** `tag/adventure`, `tag/fort`, `tag/inspection`, `tag/armok`, `tag/map` -:dfhack-keybind:`reveal` -:dfhack-keybind:`unreveal` -:dfhack-keybind:`revforget` -:dfhack-keybind:`revtoggle` -:dfhack-keybind:`revflood` -:dfhack-keybind:`nopause` - -Reveals the map. This reveals all z-layers in fort mode. It also works in -adventure mode, but any of its effects are negated once you move. When you use -it this way, you don't need to run ``unreveal`` to hide the map again. + +.. dfhack-tool:: + :summary: Reveals the map. + :tags: adventure fort inspection armok map + +.. dfhack-tool:: unreveal + :summary: Hides previously hidden tiles again. + +.. dfhack-tool:: revforget + :summary: Discard records about what was visible before revealing the map. + +.. dfhack-tool:: revtoggle + :summary: Switch between reveal and unreveal. + +.. dfhack-tool:: revflood + :summary: Hide everything, then reveal tiles with a path to the cursor. + +.. dfhack-tool:: nopause + :summary: Disable pausing. + +This reveals all z-layers in fort mode. It also works in adventure mode, but any +of its effects are negated once you move. When you use it this way, you don't +need to run ``unreveal`` to hide the map again. Usage: diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst index d3b89d3a9..f808735d4 100644 --- a/docs/plugins/ruby.rst +++ b/docs/plugins/ruby.rst @@ -2,12 +2,17 @@ ruby ==== -**Tags:** `tag/dev` -:dfhack-keybind:`rb` -:dfhack-keybind:`rb_eval` -Allow Ruby scripts to be executed. When invoked as a command, you can Eval() a -ruby string. +.. dfhack-tool:: + :summary: Allow Ruby scripts to be executed as DFHack commands. + :tags: dev + :no-command: + +.. dfhack-command:: rb + :summary: Eval() a ruby string. + +.. dfhack-command:: rb_eval + :summary: Eval() a ruby string. Usage:: diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index 41ae1c922..937e09885 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -2,12 +2,16 @@ search ====== -**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` -Adds search capabilities to the UI. The Stocks, Animals, Trading, Stockpile, -Noble (assignment candidates), Military (position candidates), Burrows (unit -list), Rooms, Announcements, Job List, and Unit List screens all get hotkeys -that allow you to dynamically filter the displayed lists. +.. dfhack-tool:: + :summary: Adds search capabilities to the UI. + :tags: fort productivity interface + :no-command: + +Search options are added to the Stocks, Animals, Trading, Stockpile, Noble +aassignment candidates), Military (position candidates), Burrows (unit list), +Rooms, Announcements, Job List, and Unit List screens all get hotkeys that allow +you to dynamically filter the displayed lists. Usage:: diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index bd51fdd2e..1aaa04ee3 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -1,9 +1,9 @@ seedwatch ========= -**Tags:** `tag/fort`, `tag/auto` -:dfhack-keybind:`seedwatch` -Manages seed and plant cooking based on seed stock levels. +.. dfhack-tool:: + :summary: Manages seed and plant cooking based on seed stock levels. + :tags: fort auto Each seed type can be assigned a target. If the number of seeds of that type falls below that target, then the plants and seeds of that type will be excluded diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst index 42af0e7a8..da67b5803 100644 --- a/docs/plugins/showmood.rst +++ b/docs/plugins/showmood.rst @@ -1,9 +1,9 @@ showmood ======== -**Tags:** `tag/fort`, `tag/inspection`, `tag/jobs` -:dfhack-keybind:`showmood` -Shows all items needed for the active strange mood. +.. dfhack-tool:: + :summary: Shows all items needed for the active strange mood. + :tags: fort inspection jobs Usage:: diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index 9255110bd..06ff963b7 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -1,10 +1,13 @@ siege-engine ============ -**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` -Extend the functionality and usability of siege engines. Siege engines in DF -haven't been updated since the game was 2D, and can only aim in four -directions. To make them useful above-ground, this plugin allows you to: +.. dfhack-tool:: + :summary: Extend the functionality and usability of siege engines. + :tags: fort mod buildings + :no-command: + +Siege engines in DF haven't been updated since the game was 2D, and can only aim +in four directions. To make them useful above-ground, this plugin allows you to: * link siege engines to stockpiles * restrict operator skill levels (like workshops) diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst index f3ba6e6fb..c746f1c79 100644 --- a/docs/plugins/sort.rst +++ b/docs/plugins/sort.rst @@ -1,10 +1,16 @@ sort ==== -**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` -:dfhack-keybind:`sort-items` -:dfhack-keybind:`sort-units` -Sort the visible item or unit list. +.. dfhack-tool:: + :summary: Sort lists shown in the DF interface. + :tags: fort productivity interface + :no-command: + +.. dfhack-command:: sort-items + :summary: Sort the visible item list. + +.. dfhack-command:: sort-units + :summary: Sort the visible unit list. Usage:: diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 08cc3f819..7f448ebfb 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -1,8 +1,10 @@ spectate ======== -**Tags:** `tag/fort`, `tag/units` -Automatically follow exciting dwarves. +.. dfhack-tool:: + :summary: Automatically follow exciting dwarves. + :tags: fort units + :no-command: Usage:: diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index 0fe245d63..e0f938915 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -1,10 +1,13 @@ steam-engine ============ -**Tags:** `tag/fort`, `tag/mod`, `tag/buildings` -Allow modded steam engine buildings to function. The steam-engine plugin detects -custom workshops with STEAM_ENGINE in their token, and turns them into real -steam engines! +.. dfhack-tool:: + :summary: Allow modded steam engine buildings to function. + :tags: fort mod buildings + :no-command: + +The steam-engine plugin detects custom workshops with the string +``STEAM_ENGINE`` in their token, and turns them into real steam engines! The plugin auto-enables itself when it detects the relevant tags in the world raws. It does not need to be enabled with the `enable` command. diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index 48647b869..81eab07ff 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -1,11 +1,13 @@ stockflow ========= -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs`, `tag/stockpiles` -:dfhack-keybind:`stockflow` -Queue manager jobs based on free space in stockpiles. With this plugin, the -fortress bookkeeper can tally up free space in specific stockpiles and queue -jobs through the manager to produce items to fill the free space. +.. dfhack-tool:: + :summary: Queue manager jobs based on free space in stockpiles. + :tags: fort auto jobs stockpiles + +With this plugin, the fortress bookkeeper can tally up free space in specific +stockpiles and queue jobs through the manager to produce items to fill the free +space. When the plugin is enabled, the :kbd:`q` menu of each stockpile will have two new options: diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 0c1000456..0e30b9dc7 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -2,14 +2,24 @@ stockpiles ========== -**Tags:** `tag/fort`, `tag/productivity`, `tag/design`, `tag/stockpiles` -:dfhack-keybind:`copystock` -:dfhack-keybind:`savestock` -:dfhack-keybind:`loadstock` - -Import and export stockpile settings. When the plugin is enabled, the :kbd:`q` -menu of each stockpile will have an option for saving or loading the stockpile -settings. See `gui/stockpiles` for an in-game interface. + +.. dfhack-tool:: + :summary: Import and export stockpile settings. + :tags: fort productivity design stockpiles + :no-command: + +.. dfhack-command:: copystock + :summary: Copies the configuration of the selected stockpile. + +.. dfhack-command:: savestock + :summary: Exports the configuration of the selected stockpile. + +.. dfhack-command:: loadstock + :summary: Imports the configuration of the selected stockpile. + +When the plugin is enabled, the :kbd:`q` menu of each stockpile will have an +option for saving or loading the stockpile settings. See `gui/stockpiles` for +an in-game interface. Usage: diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index 5506b1d28..e518fc1f1 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -1,10 +1,11 @@ stocks ====== -**Tags:** `tag/fort`, `tag/productivity`, `tag/interface` -:dfhack-keybind:`stocks` -Enhanced fortress stock management interface. When the plugin is enabled, two -new hotkeys become available: +.. dfhack-tool:: + :summary: Enhanced fortress stock management interface. + :tags: fort productivity interface + +When the plugin is enabled, two new hotkeys become available: * :kbd:`e` on the vanilla DF stocks screen (:kbd:`z` and then select Stocks) will launch the fortress-wide stock management screen. diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index 3bb271477..0315c92ed 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -1,10 +1,12 @@ stonesense ========== -**Tags:** `tag/adventure`, `tag/fort`, `tag/interface`, `tag/map` -:dfhack-keybind:`stonesense` -:dfhack-keybind:`ssense` -A 3D isometric visualizer that runs in a second window. +.. dfhack-tool:: + :summary: A 3D isometric visualizer. + :tags: adventure fort interface map + +.. dfhack-command:: ssense + :summary: An alias for stonesense. Usage: diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index 0198af136..8d1fc8081 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -1,9 +1,9 @@ strangemood =========== -**Tags:** `tag/fort`, `tag/armok`, `tag/units` -:dfhack-keybind:`strangemood` -Triggers a strange mood. +.. dfhack-tool:: + :summary: Trigger a strange mood. + :tags: fort armok units Usage:: diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 93072fb1b..5da293fbf 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -1,13 +1,14 @@ tailor ====== -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` -:dfhack-keybind:`tailor` - -Automatically keep your dwarves in fresh clothing. Whenever the bookkeeper -updates stockpile records, this plugin will scan the fort. If there are -fresh cloths available, dwarves who are wearing tattered clothing will have -their rags confiscated (in the same manner as the `cleanowned` tool) so that -they'll reequip with replacement clothes. + +.. dfhack-tool:: + :summary: Automatically keep your dwarves in fresh clothing. + :tags: fort auto jobs + +Whenever the bookkeeper updates stockpile records, this plugin will scan the +fort. If there are fresh cloths available, dwarves who are wearing tattered +clothing will have their rags confiscated (in the same manner as the +`cleanowned` tool) so that they'll reequip with replacement clothes. If there are not enough clothes available, manager orders will be generated to manufacture some more. ``tailor`` will intelligently create orders using diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 185f283db..ee3d4a02f 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -3,15 +3,23 @@ tiletypes ========= -**Tags:** `tag/adventure`, `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`tiletypes` -:dfhack-keybind:`tiletypes-command` -:dfhack-keybind:`tiletypes-here` -:dfhack-keybind:`tiletypes-here-point` - -Paints tiles of specified types onto the map. You can use the `probe` command -to discover properties of existing tiles that you'd like to copy. If you -accidentally paint over a vein that you want back, `fixveins` may help. + +.. dfhack-tool:: + :summary: Paints tiles of specified types onto the map. + :tags: adventure fort armok map + +.. dfhack-command:: tiletypes-command + :summary: Run tiletypes commands. + +.. dfhack-command:: tiletypes-here + :summary: Paint map tiles starting from the cursor. + +.. dfhack-command:: tiletypes-here-point + :summary: Paint the map tile under the cursor. + +You can use the `probe` command to discover properties of existing tiles that +you'd like to copy. If you accidentally paint over a vein that you want back, +`fixveins` may help. The tool works with a brush, a filter, and a paint specification. The brush determines the shape of the area to affect, the filter selects which tiles to diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index beddfc9cb..56de7d769 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -1,8 +1,10 @@ title-folder ============= -**Tags:** `tag/system`, `tag/interface` -Displays the DF folder name in the window title bar. +.. dfhack-tool:: + :summary: Displays the DF folder name in the window title bar. + :tags: system interface + :no-command: Usage:: diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index d9d35a85f..f7fc238c7 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -1,8 +1,10 @@ title-version ============= -**Tags:** `tag/system`, `tag/interface` -Displays the DFHack version on DF's title screen. +.. dfhack-tool:: + :summary: Displays the DFHack version on DF's title screen. + :tags: system interface + :no-command: Usage:: diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index cf5a9853f..65aae2b7f 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -1,11 +1,15 @@ trackstop ========= -**Tags:** `tag/fort`, `tag/interface`, `tag/mod`, `tag/buildings` -Adds dynamic configuration options for track stops. When enabled, this plugin -adds a :kbd:`q` menu for track stops, which is completely blank in vanilla DF. -This allows you to view and/or change the track stop's friction and dump -direction settings, using the keybindings from the track stop building interface. +.. dfhack-tool:: + :summary: Add dynamic configuration options for track stops. + :tags: fort interface mod buildings + :no-command: + +When enabled, this plugin adds a :kbd:`q` menu for track stops, which is +completely blank in vanilla DF. This allows you to view and/or change the track +stop's friction and dump direction settings, using the keybindings from the +track stop building interface. Usage:: diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index 404a180b2..865e4341d 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -1,9 +1,11 @@ tubefill ======== -**Tags:** `tag/fort`, `tag/armok`, `tag/map` -:dfhack-keybind:`tubefill` -Replentishes mined-out adamantine. Veins that were hollow will be left alone. +.. dfhack-tool:: + :summary: Replentishes mined-out adamantine. + :tags: fort armok map + +Veins that were originally hollow will be left alone. Usage:: diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index 03489845e..71b564434 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -1,9 +1,9 @@ tweak ===== -**Tags:** `tag/adventure`, `tag/fort`, `tag/interface`, `tag/fps`, `tag/fix`, `tag/armok` -:dfhack-keybind:`tweak` -Contains various tweaks for minor bugs. +.. dfhack-tool:: + :summary: A collection of tweaks and bugfixes. + :tags: adventure fort interface fps fix armok Usage:: diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index 895556ce3..678649487 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -1,11 +1,13 @@ workNow ======= -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` -:dfhack-keybind:`workNow` -Reduce the time that dwarves idle after completing a job. After finishing a job, -dwarves will wander away for a while before picking up a new job. This plugin -will automatically poke the game to assign dwarves to new tasks. +.. dfhack-tool:: + :summary: Reduce the time that dwarves idle after completing a job. + :tags: fort auto jobs + +After finishing a job, dwarves will wander away for a while before picking up a +new job. This plugin will automatically poke the game to assign dwarves to new +tasks. Usage: diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index c2a9ea26a..db615c876 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -1,8 +1,12 @@ workflow ======== -**Tags:** `tag/fort`, `tag/auto`, `tag/jobs` -:dfhack-keybind:`workflow` -:dfhack-keybind:`fix-job-postings` + +.. dfhack-tool:: + :summary: Manage repeat jobs according to stock levels. + :tags: fort auto jobs + +.. dfhack-command:: fix-job-postings + :summary: Fixes crashes caused by old versions of workflow. Manage repeat jobs according to stock levels. `gui/workflow` provides a simple front-end integrated in the game UI. @@ -55,7 +59,7 @@ Usage: can be copied to a file, and then reloaded using the `script` built-in command. ``fix-job-postings [dry-run]`` - Fixes crashes caused the version of workflow released with DFHack + Fixes crashes caused by the version of workflow released with DFHack 0.40.24-r4. It will be run automatically if needed. If your save has never been run with this version, you will never need this command. Specify the ``dry-run`` keyword to see what this command would do without making any diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst index 123d9920a..3d6cc0ac8 100644 --- a/docs/plugins/xlsxreader.rst +++ b/docs/plugins/xlsxreader.rst @@ -1,7 +1,9 @@ xlsxreader ========== -**Tags:** `tag/dev` -Provides a Lua API for reading xlsx files. +.. dfhack-tool:: + :summary: Provides a Lua API for reading xlsx files. + :tags: dev + :no-command: See `xlsxreader-api` for details. diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 9aa34d746..bf9428a6c 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -1,9 +1,9 @@ zone ==== -**Tags:** `tag/fort`, `tag/productivity`, `tag/animals`, `tag/buildings` -:dfhack-keybind:`zone` -Manage activity zones, cages, and the animals therein. +.. dfhack-tool:: + :summary: Manage activity zones, cages, and the animals therein. + :tags: fort productivity animals buildings Usage: diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index ff4bfc9b1..a051b76af 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -117,6 +117,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): option_spec = { 'tags': dfhack.util.directive_arg_str_list, 'no-command': rst_directives.flag, + 'summary': rst_directives.unchanged, } def render_content(self) -> List[nodes.Node]: @@ -134,9 +135,12 @@ class DFHackToolDirective(DFHackToolDirectiveBase): ] tag_nodes.pop() - return [ + ret_nodes = [ nodes.paragraph('', '', *tag_nodes), ] + if 'no-command' in self.options: + ret_nodes += [nodes.inline(text=self.options.get('summary', ''))] + return ret_nodes def run(self): out = DFHackToolDirectiveBase.run(self) @@ -146,10 +150,15 @@ class DFHackToolDirective(DFHackToolDirectiveBase): class DFHackCommandDirective(DFHackToolDirectiveBase): + option_spec = { + 'summary': rst_directives.unchanged_required, + } + def render_content(self) -> List[nodes.Node]: command = self.get_name_or_docname() return [ self.make_labeled_paragraph('Command', command, content_class=nodes.literal), + nodes.inline(text=self.options.get('summary', '')), *render_dfhack_keybind(command), ] From b21fc8aa75debfd8416eca92d0b7604b3f3fa698 Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 13 Aug 2022 13:32:45 -0700 Subject: [PATCH 256/334] remove extra space at bottom of tool summary --- docs/styles/dfhack.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/styles/dfhack.css b/docs/styles/dfhack.css index 4102e6412..0fe608dde 100644 --- a/docs/styles/dfhack.css +++ b/docs/styles/dfhack.css @@ -71,3 +71,7 @@ div.dfhack-tool-summary p { margin-bottom: 0.5em; line-height: 1em; } + +div.dfhack-tool-summary p:last-child { + margin-bottom: 0; +} From f6699c0014dd231f8d2dc205183d9f5a0098dfc4 Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 13 Aug 2022 13:40:14 -0700 Subject: [PATCH 257/334] Sync tags descriptions from spreadsheet --- docs/Tags.rst | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index d0c785c3c..96faf74e2 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -3,25 +3,27 @@ - `tag/adventure`: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! - `tag/fort`: Tools that are useful while in fort mode. - `tag/legends`: Tools that are useful while in legends mode. -- `tag/embark`: Tools that are useful while on the embark screen. -- `tag/system`: Tools related to working with DFHack commands or the core DFHack library. -- `tag/dev`: Tools useful for develpers and modders. -- `tag/auto`: Tools that you turn on and then they automatically manage some aspect of your fortress. +- `tag/embark`: Tools that are useful while on the fort embark screen or while creating an adventurer. +- `tag/dev`: Tools that are useful when developing scripts or mods. +- `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) +- `tag/auto`: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. - `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. -- `tag/inspection`: Tools that let you inspect game data. +- `tag/inspection`: Tools that let you view information that is otherwise difficult to find. - `tag/design`: Tools that help you design your fort. -- `tag/quickfort`: Tools that are involved in creating and playing back blueprints. -- `tag/interface`: Tools that modify or extend the user interface. +- `tag/gameplay`: Tools that introduce new gameplay elements. - `tag/fps`: Tools that help you manage FPS drop. -- `tag/fix`: Tools that fix specific bugs. -- `tag/mod`: Tools that introduce new gameplay elements. -- `tag/armok`: Tools that give you complete control over various aspects of the game. -- `tag/animals`: Tools that help you manage animals. -- `tag/buildings`: Tools that help you work with placing or configuring buildings and furniture. -- `tag/items`: Tools that create or modify in-game items. -- `tag/jobs`: Tools that create or modify jobs. -- `tag/map`: Map modification. -- `tag/labors`: Tools that deal with labor assignment. -- `tag/units`: Tools that create or modify units. +- `tag/bugfix`: Tools that fix specific bugs, either permanently or on-demand. +- `tag/armok`: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. +- `tag/animals`: Tools that interact with animals. +- `tag/buildings`: Tools that interact with buildings and furniture. - `tag/stockpiles`: Tools that interact wtih stockpiles. -- `tag/trees`: Tools that interact with trees and shrubs. +- `tag/items`: Tools that interact with in-game items. +- `tag/map`: Tools that interact with the game map. +- `tag/interface`: Tools that interact with or extend the DF user interface. +- `tag/workorders`: Tools that interact with workorders. +- `tag/jobs`: Tools that interact with jobs. +- `tag/labors`: Tools that deal with labor assignment. +- `tag/military`: Tools that interact with the military. +- `tag/graphics`: Tools that interact with game graphics. +- `tag/plants`: Tools that interact with trees, shrubs, and crops. +- `tag/units`: Tools that interact with units. From eb91feff7c167b4b118b753022ad06ca4df66316 Mon Sep 17 00:00:00 2001 From: myk002 Date: Thu, 11 Aug 2022 23:34:37 -0700 Subject: [PATCH 258/334] revise tag list and assignments --- docs/builtins/alias.rst | 2 +- docs/builtins/cls.rst | 2 +- docs/builtins/die.rst | 2 +- docs/builtins/disable.rst | 2 +- docs/builtins/enable.rst | 2 +- docs/builtins/fpause.rst | 2 +- docs/builtins/help.rst | 2 +- docs/builtins/hide.rst | 2 +- docs/builtins/keybinding.rst | 2 +- docs/builtins/kill-lua.rst | 2 +- docs/builtins/load.rst | 2 +- docs/builtins/ls.rst | 2 +- docs/builtins/plug.rst | 2 +- docs/builtins/reload.rst | 2 +- docs/builtins/sc-script.rst | 2 +- docs/builtins/script.rst | 2 +- docs/builtins/show.rst | 2 +- docs/builtins/tags.rst | 2 +- docs/builtins/type.rst | 2 +- docs/builtins/unload.rst | 2 +- docs/plugins/3dveins.rst | 2 +- docs/plugins/RemoteFortressReader.rst | 2 +- docs/plugins/add-spatter.rst | 2 +- docs/plugins/autochop.rst | 2 +- docs/plugins/autoclothing.rst | 2 +- docs/plugins/autodump.rst | 2 +- docs/plugins/autofarm.rst | 2 +- docs/plugins/autogems.rst | 2 +- docs/plugins/automelt.rst | 2 +- docs/plugins/autotrade.rst | 2 +- docs/plugins/blueprint.rst | 2 +- docs/plugins/building-hacks.rst | 2 +- docs/plugins/buildingplan.rst | 2 +- docs/plugins/burrows.rst | 2 +- docs/plugins/cleanconst.rst | 2 +- docs/plugins/cleaners.rst | 2 +- docs/plugins/command-prompt.rst | 2 +- docs/plugins/cursecheck.rst | 2 +- docs/plugins/diggingInvaders.rst | 2 +- docs/plugins/dwarfmonitor.rst | 2 +- docs/plugins/dwarfvet.rst | 2 +- docs/plugins/eventful.rst | 2 +- docs/plugins/fix-unit-occupancy.rst | 2 +- docs/plugins/fixveins.rst | 2 +- docs/plugins/forceequip.rst | 2 +- docs/plugins/getplants.rst | 2 +- docs/plugins/hotkeys.rst | 2 +- docs/plugins/infiniteSky.rst | 2 +- docs/plugins/isoworldremote.rst | 2 +- docs/plugins/luasocket.rst | 2 +- docs/plugins/manipulator.rst | 2 +- docs/plugins/map-render.rst | 2 +- docs/plugins/mode.rst | 2 +- docs/plugins/orders.rst | 2 +- docs/plugins/pathable.rst | 2 +- docs/plugins/petcapRemover.rst | 2 +- docs/plugins/plants.rst | 2 +- docs/plugins/power-meter.rst | 2 +- docs/plugins/probe.rst | 2 +- docs/plugins/prospector.rst | 2 +- docs/plugins/regrass.rst | 2 +- docs/plugins/rename.rst | 2 +- docs/plugins/rendermax.rst | 2 +- docs/plugins/resume.rst | 2 +- docs/plugins/seedwatch.rst | 2 +- docs/plugins/showmood.rst | 2 +- docs/plugins/siege-engine.rst | 2 +- docs/plugins/spectate.rst | 2 +- docs/plugins/steam-engine.rst | 2 +- docs/plugins/stockflow.rst | 2 +- docs/plugins/stocks.rst | 2 +- docs/plugins/stonesense.rst | 2 +- docs/plugins/tailor.rst | 2 +- docs/plugins/title-folder.rst | 2 +- docs/plugins/title-version.rst | 2 +- docs/plugins/trackstop.rst | 2 +- docs/plugins/tweak.rst | 2 +- docs/plugins/workNow.rst | 2 +- 78 files changed, 78 insertions(+), 78 deletions(-) diff --git a/docs/builtins/alias.rst b/docs/builtins/alias.rst index 84a761556..a9f44386a 100644 --- a/docs/builtins/alias.rst +++ b/docs/builtins/alias.rst @@ -3,7 +3,7 @@ alias .. dfhack-tool:: :summary: Configure helper aliases for other DFHack commands. - :tags: system + :tags: dfhack Aliases are resolved immediately after built-in commands, which means that an alias cannot override a built-in command, but can override a command implemented diff --git a/docs/builtins/cls.rst b/docs/builtins/cls.rst index 2aad049b4..515235a93 100644 --- a/docs/builtins/cls.rst +++ b/docs/builtins/cls.rst @@ -3,7 +3,7 @@ cls .. dfhack-tool:: :summary: Clear the terminal screen. - :tags: system + :tags: dfhack Can also be invoked as ``clear``. Note that this command does not delete command history. It just clears the text on the screen. diff --git a/docs/builtins/die.rst b/docs/builtins/die.rst index b4873abc5..25a34504d 100644 --- a/docs/builtins/die.rst +++ b/docs/builtins/die.rst @@ -3,7 +3,7 @@ die .. dfhack-tool:: :summary: Instantly exit DF without saving. - :tags: system + :tags: dfhack Use to exit DF quickly and safely. diff --git a/docs/builtins/disable.rst b/docs/builtins/disable.rst index e109e69e1..73d267ccf 100644 --- a/docs/builtins/disable.rst +++ b/docs/builtins/disable.rst @@ -3,7 +3,7 @@ disable .. dfhack-tool:: :summary: Deactivate a DFHack tool that has some persistent effect. - :tags: system + :tags: dfhack See the `enable` command for more info. diff --git a/docs/builtins/enable.rst b/docs/builtins/enable.rst index 63b0bdc5f..19f98a122 100644 --- a/docs/builtins/enable.rst +++ b/docs/builtins/enable.rst @@ -3,7 +3,7 @@ enable .. dfhack-tool:: :summary: Activate a DFHack tool that has some persistent effect. - :tags: system + :tags: dfhack Many plugins and scripts can be in a distinct enabled or disabled state. Some of them activate and deactivate automatically depending on the contents of the diff --git a/docs/builtins/fpause.rst b/docs/builtins/fpause.rst index 2e8f851f7..5379caec0 100644 --- a/docs/builtins/fpause.rst +++ b/docs/builtins/fpause.rst @@ -3,7 +3,7 @@ fpause .. dfhack-tool:: :summary: Forces DF to pause. - :tags: system + :tags: dfhack This is useful when your FPS drops below 1 and you lose control of the game. diff --git a/docs/builtins/help.rst b/docs/builtins/help.rst index e1e913747..95dadefd9 100644 --- a/docs/builtins/help.rst +++ b/docs/builtins/help.rst @@ -3,7 +3,7 @@ help .. dfhack-tool:: :summary: Display help about a command or plugin. - :tags: system + :tags: dfhack Can also be invoked as ``?`` or ``man`` (short for "manual"). diff --git a/docs/builtins/hide.rst b/docs/builtins/hide.rst index 0ec8f36df..b23738860 100644 --- a/docs/builtins/hide.rst +++ b/docs/builtins/hide.rst @@ -3,7 +3,7 @@ hide .. dfhack-tool:: :summary: Hide the DFHack terminal window. - :tags: system + :tags: dfhack You can show it again with the `show` command, though you'll need to use it from a `keybinding` set beforehand or the in-game `command-prompt`. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index 5ce8145f4..ae51d7a37 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -3,7 +3,7 @@ keybinding .. dfhack-tool:: :summary: Create hotkeys that will run DFHack commands. - :tags: system + :tags: dfhack Like any other command, it can be used at any time from the console, but bindings are not remembered between runs of the game unless re-created in diff --git a/docs/builtins/kill-lua.rst b/docs/builtins/kill-lua.rst index 4de0ab7d1..3334b3b2b 100644 --- a/docs/builtins/kill-lua.rst +++ b/docs/builtins/kill-lua.rst @@ -3,7 +3,7 @@ kill-lua .. dfhack-tool:: :summary: Gracefully stop any currently-running Lua scripts. - :tags: system + :tags: dfhack Use this command to stop a misbehaving script that appears to be stuck. diff --git a/docs/builtins/load.rst b/docs/builtins/load.rst index d253144a8..0c199ae3d 100644 --- a/docs/builtins/load.rst +++ b/docs/builtins/load.rst @@ -3,7 +3,7 @@ load .. dfhack-tool:: :summary: Load and register a plugin library. - :tags: system + :tags: dfhack Also see `unload` and `reload` for related actions. diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 702c8bae5..6ef09d236 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -3,7 +3,7 @@ ls .. dfhack-tool:: :summary: List available DFHack commands. - :tags: system + :tags: dfhack In order to group related commands, each command is associated with a list of tags. You can filter the listed commands by a tag or a substring of the diff --git a/docs/builtins/plug.rst b/docs/builtins/plug.rst index e10c154e5..a1b136306 100644 --- a/docs/builtins/plug.rst +++ b/docs/builtins/plug.rst @@ -3,7 +3,7 @@ plug .. dfhack-tool:: :summary: List available plugins and whether they are enabled. - :tags: system + :tags: dfhack Usage:: diff --git a/docs/builtins/reload.rst b/docs/builtins/reload.rst index 1429e1038..17efffb2c 100644 --- a/docs/builtins/reload.rst +++ b/docs/builtins/reload.rst @@ -3,7 +3,7 @@ reload .. dfhack-tool:: :summary: Reload a loaded plugin. - :tags: system + :tags: dfhack Developers use this command to reload a plugin that they are actively modifying. Also see `load` and `unload` for related actions. diff --git a/docs/builtins/sc-script.rst b/docs/builtins/sc-script.rst index b3255cb9f..5203f1aff 100644 --- a/docs/builtins/sc-script.rst +++ b/docs/builtins/sc-script.rst @@ -3,7 +3,7 @@ sc-script .. dfhack-tool:: :summary: Run commands when game state changes occur. - :tags: system + :tags: dfhack This is similar to the static `init-files` but is slightly more flexible since it can be set dynamically. diff --git a/docs/builtins/script.rst b/docs/builtins/script.rst index fbcebff6c..0f40df5df 100644 --- a/docs/builtins/script.rst +++ b/docs/builtins/script.rst @@ -3,7 +3,7 @@ script .. dfhack-tool:: :summary: Execute a batch file of DFHack commands. - :tags: system + :tags: dfhack It reads a text file and runs each line as a DFHack command as if it had been typed in by the user -- treating the input like `an init file `. diff --git a/docs/builtins/show.rst b/docs/builtins/show.rst index 7002d5aff..c59f4e519 100644 --- a/docs/builtins/show.rst +++ b/docs/builtins/show.rst @@ -3,7 +3,7 @@ show .. dfhack-tool:: :summary: Unhides the DFHack terminal window. - :tags: system + :tags: dfhack Useful if you have hidden the terminal with `hide` and you want it back. Since the terminal window won't be available to run this command, you'll need to use diff --git a/docs/builtins/tags.rst b/docs/builtins/tags.rst index 93a094149..3823a9924 100644 --- a/docs/builtins/tags.rst +++ b/docs/builtins/tags.rst @@ -3,7 +3,7 @@ tags .. dfhack-tool:: :summary: List the strings that DFHack tools can be tagged with. - :tags: system + :tags: dfhack You can find groups of related tools by passing the tag name to the `ls` command. diff --git a/docs/builtins/type.rst b/docs/builtins/type.rst index 3336cae5f..dfcb493f9 100644 --- a/docs/builtins/type.rst +++ b/docs/builtins/type.rst @@ -3,7 +3,7 @@ type .. dfhack-tool:: :summary: Describe how a command is implemented. - :tags: system + :tags: dfhack DFHack commands can be provided by plugins, scripts, or by the core library itself. The ``type`` command can tell you which is the source of a particular diff --git a/docs/builtins/unload.rst b/docs/builtins/unload.rst index 132732a7f..fd9e11a46 100644 --- a/docs/builtins/unload.rst +++ b/docs/builtins/unload.rst @@ -3,7 +3,7 @@ unload .. dfhack-tool:: :summary: Unload a plugin from memory. - :tags: system + :tags: dfhack Also see `load` and `reload` for related actions. diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index c60582310..b22ac81e5 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -3,7 +3,7 @@ .. dfhack-tool:: :summary: Rewrite layer veins to expand in 3D space. - :tags: fort mod map + :tags: fort gameplay map Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in their place. The transformation preserves the mineral counts diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index f849266c2..9a925d0fd 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -3,7 +3,7 @@ RemoteFortressReader .. dfhack-tool:: :summary: Backend for Armok Vision. - :tags: dev + :tags: dev graphics :no-command: .. dfhack-command:: RemoteFortressReader_version diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 06739291e..873bd761f 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -3,7 +3,7 @@ add-spatter .. dfhack-tool:: :summary: Make tagged reactions produce contaminants. - :tags: adventure fort mod items + :tags: adventure fort gameplay items :no-command: Give some use to all those poisons that can be bought from caravans! The plugin diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index b445384fb..f0f7932fd 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -3,7 +3,7 @@ autochop .. dfhack-tool:: :summary: Auto-harvest trees when low on stockpiled logs. - :tags: fort auto + :tags: fort auto plants :no-command: This plugin can designate trees for chopping when your stocks are low on logs. diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index a5b57dfb1..ed4f2160d 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -3,7 +3,7 @@ autoclothing .. dfhack-tool:: :summary: Automatically manage clothing work orders. - :tags: fort auto jobs + :tags: fort auto workorders This command allows you to set how many of each clothing type every citizen should have. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 0fd7ee23c..e2d812219 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -3,7 +3,7 @@ autodump .. dfhack-tool:: :summary: Automatically set items in a stockpile to be dumped. - :tags: fort auto fps items stockpiles + :tags: fort productivity fps armok stockpiles items :no-command: .. dfhack-command:: autodump diff --git a/docs/plugins/autofarm.rst b/docs/plugins/autofarm.rst index 44077eb89..026a2ad61 100644 --- a/docs/plugins/autofarm.rst +++ b/docs/plugins/autofarm.rst @@ -3,7 +3,7 @@ autofarm .. dfhack-tool:: :summary: Automatically manage farm crop selection. - :tags: fort auto buildings + :tags: fort auto plants Periodically scan your plant stocks and assign crops to your farm plots based on which plant stocks are low (as long as you have the appropriate seeds). The diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index f95d74fea..9efa33499 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -3,7 +3,7 @@ autogems .. dfhack-tool:: :summary: Automatically cut rough gems. - :tags: fort auto jobs + :tags: fort auto workorders :no-command: .. dfhack-command:: autogems-reload diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 508fdaeaf..09b35a378 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -3,7 +3,7 @@ automelt .. dfhack-tool:: :summary: Quickly designate items to be melted. - :tags: fort auto items stockpiles + :tags: fort productivity stockpiles items :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index f27fab122..946e53508 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -3,7 +3,7 @@ autotrade .. dfhack-tool:: :summary: Quickly designate items to be traded. - :tags: fort auto items stockpiles + :tags: fort productivity stockpiles items :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 8654aa1ec..ed3d0b29c 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -3,7 +3,7 @@ blueprint .. dfhack-tool:: :summary: Record a live game map in a quickfort blueprint. - :tags: fort design quickfort map + :tags: fort design buildings stockpiles map With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index cd8835f37..12f49fbbe 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,7 @@ building-hacks .. dfhack-tool:: :summary: Provides a Lua API for creating powered workshops. - :tags: fort mod buildings + :tags: fort gameplay buildings :no-command: See `building-hacks-api` for more details. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index ea9d78f64..4e96e3349 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -3,7 +3,7 @@ buildingplan .. dfhack-tool:: :summary: Plan building construction before you have materials. - :tags: fort design quickfort buildings map + :tags: fort design buildings This plugin adds a planning mode for building placement. You can then place furniture, constructions, and other buildings before the required materials are diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 92e62f08b..1bdbd959f 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -3,7 +3,7 @@ burrows .. dfhack-tool:: :summary: Auto-expand burrows as you dig. - :tags: fort auto productivity units + :tags: fort auto productivity design map units :no-command: .. dfhack-command:: burrow diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index 9c0309105..9417bd539 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -3,7 +3,7 @@ cleanconst .. dfhack-tool:: :summary: Cleans up construction materials. - :tags: fort fps map + :tags: fort fps buildings This tool alters all constructions on the map so that they spawn their building component when they are disassembled, allowing their actual build items to be diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 42e585da1..ca022c440 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -6,7 +6,7 @@ cleaners .. dfhack-tool:: :summary: Provides commands for cleaning spatter from the map. - :tags: adventure fort fps items map units + :tags: adventure fort fps armok items map units :no-command: .. dfhack-command:: clean diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 278741f36..802a1898e 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -3,7 +3,7 @@ command-prompt .. dfhack-tool:: :summary: An in-game DFHack terminal where you can run other commands. - :tags: system + :tags: dfhack Usage:: diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index b2e2943ba..afe6b537f 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -3,7 +3,7 @@ cursecheck .. dfhack-tool:: :summary: Check for cursed creatures. - :tags: system interface + :tags: dev fps stockpiles This command checks a single map tile (or the whole map/world) for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index df54e7008..bdb65b490 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -3,7 +3,7 @@ diggingInvaders .. dfhack-tool:: :summary: Invaders dig and destroy to get to your dwarves. - :tags: fort mod map + :tags: fort gameplay military units Usage: diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index 73d8a8c19..1c499639f 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -3,7 +3,7 @@ dwarfmonitor .. dfhack-tool:: :summary: Measure fort happiness and efficiency. - :tags: fort inspection units + :tags: fort inspection jobs units It can also show heads-up display widgets with live fort statistics. diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index f607e85cd..fc8f84659 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -3,7 +3,7 @@ dwarfvet .. dfhack-tool:: :summary: Allows animals to be treated at animal hospitals. - :tags: fort mod animals + :tags: fort gameplay animals Annoyed your dragons become useless after a minor injury? Well, with dwarfvet, injured animals will be treated at an animal hospital, which is simply a hospital diff --git a/docs/plugins/eventful.rst b/docs/plugins/eventful.rst index d5bd55956..e89480413 100644 --- a/docs/plugins/eventful.rst +++ b/docs/plugins/eventful.rst @@ -3,7 +3,7 @@ eventful .. dfhack-tool:: :summary: Provides a Lua API for reacting to in-game events. - :tags: dev mod + :tags: dev gameplay :no-command: See `eventful-api` for details. diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 117f97467..ba75ed389 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -3,7 +3,7 @@ fix-unit-occupancy .. dfhack-tool:: :summary: Fix phantom unit occupancy issues. - :tags: fort fix map + :tags: fort bugfix map If you see "unit blocking tile" messages that you can't account for (:bug:`3499`), this tool can help. diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index 045b3c8da..f8b9c4c36 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -3,7 +3,7 @@ fixveins .. dfhack-tool:: :summary: Restore missing mineral inclusions. - :tags: fort fix map + :tags: fort bugfix map This tool can also remove invalid references to mineral inclusions if you broke your embark with tools like `tiletypes`. diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index acfd8f462..b835865e2 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -3,7 +3,7 @@ forceequip .. dfhack-tool:: :summary: Move items into a unit's inventory. - :tags: adventure fort items units + :tags: adventure fort animals items military units This tool is typically used to equip specific clothing/armor items onto a dwarf, but can also be used to put armor onto a war animal or to add unusual items diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 502f6e9ae..90f4203b9 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -3,7 +3,7 @@ getplants .. dfhack-tool:: :summary: Designate trees for chopping and shrubs for gathering. - :tags: fort productivity + :tags: fort productivity plants Specify the types of trees to cut down and/or shrubs to gather by their plant names. diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst index 7d30d486e..6115bc90c 100644 --- a/docs/plugins/hotkeys.rst +++ b/docs/plugins/hotkeys.rst @@ -3,7 +3,7 @@ hotkeys .. dfhack-tool:: :summary: Show all dfhack keybindings for the current context. - :tags: system productivity interface + :tags: dfhack The command opens an in-game screen showing which DFHack keybindings are active in the current context. See also `hotkey-notes`. diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index 46f30c24c..144c6c6e7 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -3,7 +3,7 @@ infiniteSky .. dfhack-tool:: :summary: Automatically allocates new z-levels of sky - :tags: fort map + :tags: fort design map If enabled, this plugin will automatically allocate new z-levels of sky at the top of the map as you build up. Or it can allocate one or many additional levels diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 1fa439b29..284794f96 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -3,7 +3,7 @@ isoworldremote .. dfhack-tool:: :summary: Provides a remote API used by Isoworld. - :tags: dev mod + :tags: dev graphics :no-command: See `remote` for related remote APIs. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index 24b30c7a6..4b5b18540 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -3,7 +3,7 @@ luasocket .. dfhack-tool:: :summary: Provides a Lua API for accessing network sockets. - :tags: dev mod + :tags: dev :no-command: See `luasocket-api` for details. diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 26223587d..a0c8ec38e 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -3,7 +3,7 @@ manipulator .. dfhack-tool:: :summary: An in-game labor management interface. - :tags: fort productivity interface labors + :tags: fort productivity labors :no-command: It is equivalent to the popular Dwarf Therapist utility. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index 7754af8b7..efe3ed0c6 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -3,7 +3,7 @@ map-render .. dfhack-tool:: :summary: Provides a Lua API for rerendering portions of the map. - :tags: dev + :tags: dev graphics :no-command: See `map-render-api` for details. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index db94e6bac..94ee7cb44 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -3,7 +3,7 @@ mode .. dfhack-tool:: :summary: See and change the game mode. - :tags: dev + :tags: dev gameplay armok .. warning:: diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 9403c6c8e..036e0df11 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -3,7 +3,7 @@ orders .. dfhack-tool:: :summary: Manage manager orders. - :tags: fort productivity jobs + :tags: fort productivity workorders Usage: diff --git a/docs/plugins/pathable.rst b/docs/plugins/pathable.rst index 800580017..f4b89683b 100644 --- a/docs/plugins/pathable.rst +++ b/docs/plugins/pathable.rst @@ -3,7 +3,7 @@ pathable .. dfhack-tool:: :summary: Marks tiles that are reachable from the cursor. - :tags: dev inspection interface map + :tags: dev inspection map :no-command: This plugin provides a Lua API, but no direct commands. See `pathable-api` for diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index f3d011308..d4e1f2279 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -3,7 +3,7 @@ petcapRemover .. dfhack-tool:: :summary: Modify the pet population cap. - :tags: fort armok animals + :tags: fort animals In vanilla DF, pets will not reproduce unless the population is below 50 and the number of children of that species is below a certain percentage. This plugin diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index d28119af9..cc23c84dd 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -5,7 +5,7 @@ plants .. dfhack-tool:: :summary: Provides commands that interact with plants. - :tags: adventure fort armok map + :tags: adventure fort armok map plants :no-command: .. dfhack-command:: plant diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 01e75491a..5e608ca46 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -3,7 +3,7 @@ power-meter .. dfhack-tool:: :summary: Allow presure plates to measure power. - :tags: fort mod buildings + :tags: fort gameplay buildings :no-command: If you run `gui/power-meter` while building a pressure plate, the pressure diff --git a/docs/plugins/probe.rst b/docs/plugins/probe.rst index 54436be68..7da332ba5 100644 --- a/docs/plugins/probe.rst +++ b/docs/plugins/probe.rst @@ -3,7 +3,7 @@ probe .. dfhack-tool:: :summary: Display low-level properties of the selected tile. - :tags: adventure fort inspection map + :tags: adventure fort inspection buildings map units .. dfhack-command:: bprobe :summary: Display low-level properties of the selected building. diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index dc6204c06..7ef3d223c 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -5,7 +5,7 @@ prospector .. dfhack-tool:: :summary: Provides commands that help you analyze natural resources. - :tags: fort embark inspection map + :tags: fort embark inspection armok map :no-command: .. dfhack-command:: prospect diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 72f077fcd..a4b011a1e 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -3,7 +3,7 @@ regrass .. dfhack-tool:: :summary: Regrows all the grass. - :tags: adventure fort armok animals + :tags: adventure fort armok animals map Use this command if your grazers have eaten everything down to the dirt. diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 02bc04c2c..c3e4d5b59 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -3,7 +3,7 @@ rename .. dfhack-tool:: :summary: Easily rename things. - :tags: adventure fort productivity + :tags: adventure fort productivity buildings stockpiles units Use `gui/rename` for an in-game interface. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index 680676fa3..af3dc6c0c 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -3,7 +3,7 @@ rendermax .. dfhack-tool:: :summary: Modify the map lighting. - :tags: adventure fort mod + :tags: adventure fort gameplay graphics This plugin provides a collection of OpenGL lighting filters that affect how the map is drawn to the screen. diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index 0713026e4..b8c388738 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -3,7 +3,7 @@ resume .. dfhack-tool:: :summary: Color planned buildings based on their suspend status. - :tags: fort productivity + :tags: fort productivity interface jobs :no-command: .. dfhack-command:: resume diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index 1aaa04ee3..7d31c160c 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -3,7 +3,7 @@ seedwatch .. dfhack-tool:: :summary: Manages seed and plant cooking based on seed stock levels. - :tags: fort auto + :tags: fort auto plants Each seed type can be assigned a target. If the number of seeds of that type falls below that target, then the plants and seeds of that type will be excluded diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst index da67b5803..2a294b166 100644 --- a/docs/plugins/showmood.rst +++ b/docs/plugins/showmood.rst @@ -3,7 +3,7 @@ showmood .. dfhack-tool:: :summary: Shows all items needed for the active strange mood. - :tags: fort inspection jobs + :tags: fort inspection armok jobs units Usage:: diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index 06ff963b7..a6b3e87a8 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -3,7 +3,7 @@ siege-engine .. dfhack-tool:: :summary: Extend the functionality and usability of siege engines. - :tags: fort mod buildings + :tags: fort gameplay buildings :no-command: Siege engines in DF haven't been updated since the game was 2D, and can only aim diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 7f448ebfb..96147695a 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -3,7 +3,7 @@ spectate .. dfhack-tool:: :summary: Automatically follow exciting dwarves. - :tags: fort units + :tags: fort interface :no-command: Usage:: diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index e0f938915..6560ae714 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -3,7 +3,7 @@ steam-engine .. dfhack-tool:: :summary: Allow modded steam engine buildings to function. - :tags: fort mod buildings + :tags: fort gameplay buildings :no-command: The steam-engine plugin detects custom workshops with the string diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index 81eab07ff..358305a96 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -3,7 +3,7 @@ stockflow .. dfhack-tool:: :summary: Queue manager jobs based on free space in stockpiles. - :tags: fort auto jobs stockpiles + :tags: fort auto stockpiles workorders With this plugin, the fortress bookkeeper can tally up free space in specific stockpiles and queue jobs through the manager to produce items to fill the free diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index e518fc1f1..144ba2287 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -3,7 +3,7 @@ stocks .. dfhack-tool:: :summary: Enhanced fortress stock management interface. - :tags: fort productivity interface + :tags: fort productivity items When the plugin is enabled, two new hotkeys become available: diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index 0315c92ed..b43f95697 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -3,7 +3,7 @@ stonesense .. dfhack-tool:: :summary: A 3D isometric visualizer. - :tags: adventure fort interface map + :tags: adventure fort map graphics .. dfhack-command:: ssense :summary: An alias for stonesense. diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 5da293fbf..646e73f91 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -3,7 +3,7 @@ tailor .. dfhack-tool:: :summary: Automatically keep your dwarves in fresh clothing. - :tags: fort auto jobs + :tags: fort auto workorders Whenever the bookkeeper updates stockpile records, this plugin will scan the fort. If there are fresh cloths available, dwarves who are wearing tattered diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index 56de7d769..ca349a0ed 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -3,7 +3,7 @@ title-folder .. dfhack-tool:: :summary: Displays the DF folder name in the window title bar. - :tags: system interface + :tags: interface :no-command: Usage:: diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index f7fc238c7..239b08bc2 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -3,7 +3,7 @@ title-version .. dfhack-tool:: :summary: Displays the DFHack version on DF's title screen. - :tags: system interface + :tags: interface :no-command: Usage:: diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index 65aae2b7f..1c638dfd7 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -3,7 +3,7 @@ trackstop .. dfhack-tool:: :summary: Add dynamic configuration options for track stops. - :tags: fort interface mod buildings + :tags: fort gameplay buildings :no-command: When enabled, this plugin adds a :kbd:`q` menu for track stops, which is diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index 71b564434..c04392c62 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -3,7 +3,7 @@ tweak .. dfhack-tool:: :summary: A collection of tweaks and bugfixes. - :tags: adventure fort interface fps fix armok + :tags: adventure fort fps bugfix armok interface Usage:: diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index 678649487..27709c166 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -3,7 +3,7 @@ workNow .. dfhack-tool:: :summary: Reduce the time that dwarves idle after completing a job. - :tags: fort auto jobs + :tags: fort auto labors After finishing a job, dwarves will wander away for a while before picking up a new job. This plugin will automatically poke the game to assign dwarves to new From 2a3a812b3c7c5de15dfb7510c2a998483056d3cc Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 13 Aug 2022 21:21:48 -0700 Subject: [PATCH 259/334] update tag reference in Core.rst --- docs/Core.rst | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index 025e951e6..09ae2577d 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -185,7 +185,7 @@ where ``*`` can be any string, including the empty string. A world being loaded can mean a fortress, an adventurer, or legends mode. These files are best used for non-persistent commands, such as setting -a `tag/fix` script to run on `repeat`. +a `tag/bugfix` script to run on `repeat`. .. _onMapLoad.init: diff --git a/scripts b/scripts index 4c7eecb5c..f37c0d022 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 4c7eecb5cbeeead3c4374fafc562ee774e8e229e +Subproject commit f37c0d022135ca10fb5375c4e56e11215bf208ae From 47dc8c6c11d4dd121c5dcf8542125d08a24f2dcd Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 13 Aug 2022 21:23:26 -0700 Subject: [PATCH 260/334] update scripts reference --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f37c0d022..3d65c9a75 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f37c0d022135ca10fb5375c4e56e11215bf208ae +Subproject commit 3d65c9a75a8c7a3607e71529f6264bab3e637755 From f1f207b45b57b28cdf0ce5f53456423f28f385ba Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 13 Aug 2022 21:51:36 -0700 Subject: [PATCH 261/334] don't include rst sources in html output --- CMakeLists.txt | 4 +++- conf.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c341735c9..c02b3a670 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,7 +506,9 @@ if(BUILD_DOCS) COMMAND ${CMAKE_COMMAND} -E touch ${SPHINX_OUTPUT}) install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/ - DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) + DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs + FILES_MATCHING PATTERN "*" + PATTERN html/_sources EXCLUDE) install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/ DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) install(FILES docs/changelogs/news.rst docs/changelogs/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) diff --git a/conf.py b/conf.py index ff4cdc59d..0eaeb95f7 100644 --- a/conf.py +++ b/conf.py @@ -316,6 +316,9 @@ html_domain_indices = False # If false, no genindex.html is generated. html_use_index = True +# don't link to rst sources in the generated pages +html_show_sourcelink = False + html_css_files = [ 'dfhack.css', ] From 488fd677429310a684a5ac1d1e1d0eef98a671cf Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 14 Aug 2022 23:01:20 -0700 Subject: [PATCH 262/334] output version of found sphinx and python --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c02b3a670..c278c4542 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,8 +442,10 @@ endif() add_subdirectory(data) add_subdirectory(scripts) -find_package(Sphinx QUIET) if(BUILD_DOCS) + find_package(Python3) + find_package(Sphinx) + if(NOT SPHINX_FOUND) message(SEND_ERROR "Sphinx not found but BUILD_DOCS enabled") endif() From 340b524348eabc0fa750232305000586e5b8646f Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 15 Aug 2022 17:49:44 -0400 Subject: [PATCH 263/334] Invoke build.py with cmake-found python In the Buildmaster GCC 4.8 image, `/usr/bin/env python3` appears to find the system Python (3.4) as opposed to the newer Python (3.6) we install separately. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c278c4542..60bffa758 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -493,7 +493,7 @@ if(BUILD_DOCS) "${CMAKE_BINARY_DIR}/docs/text" ) add_custom_command(OUTPUT ${SPHINX_OUTPUT} - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py" + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py" html text --sphinx="${SPHINX_EXECUTABLE}" -- -q DEPENDS ${SPHINX_DEPS} COMMENT "Building documentation with Sphinx" From b01ef4a96237431d545cf6b999208d4809ecdde7 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 15 Aug 2022 23:14:16 -0700 Subject: [PATCH 264/334] add todos about not using strong for text output --- docs/sphinx_extensions/dfhack/tool_docs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index a051b76af..1c4cd74e0 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -55,6 +55,7 @@ def render_dfhack_keybind(command) -> List[nodes.paragraph]: return out for keycmd, key, ctx in _KEYBINDS[command]: n = nodes.paragraph() + # TODO: use inline instead of strong when rendering to text n += nodes.strong('Keybinding:', 'Keybinding:') n += nodes.inline(' ', ' ') for k in key: @@ -97,6 +98,7 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): @staticmethod def make_labeled_paragraph(label, content, label_class=nodes.strong, content_class=nodes.inline) -> nodes.paragraph: return nodes.paragraph('', '', *[ + # TODO: use inline instead of strong when rendering to text label_class('', '{}:'.format(label)), nodes.inline(text=' '), content_class('', content), @@ -121,6 +123,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): } def render_content(self) -> List[nodes.Node]: + # TODO: use inline instead of strong when rendering to text tag_nodes = [nodes.strong(text='Tags:'), nodes.inline(text=' ')] for tag in self.options.get('tags', []): tag_nodes += [ From 7acec133baf4e5c27e8235589f1f6cc465e19de4 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 15 Aug 2022 23:14:50 -0700 Subject: [PATCH 265/334] organize tags by group, add more info about tools --- conf.py | 39 ++++++++++++++++++++------------ docs/Categories.rst | 13 ----------- docs/Documentation.rst | 4 ++-- docs/Tags.rst | 19 ++++++++++++++++ docs/Tools.rst | 50 ++++++++++++++++++++++++++++++++++++++++++ index.rst | 2 +- 6 files changed, 97 insertions(+), 30 deletions(-) delete mode 100644 docs/Categories.rst create mode 100644 docs/Tools.rst diff --git a/conf.py b/conf.py index 0eaeb95f7..c71f69a4b 100644 --- a/conf.py +++ b/conf.py @@ -73,29 +73,40 @@ DOC_ALL_DIRS = doc_all_dirs() def get_tags(): - tags = [] + groups = {} + group_re = re.compile(r'"([^"]+)"') tag_re = re.compile(r'- `tag/([^`]+)`: (.*)') with open('docs/Tags.rst') as f: lines = f.readlines() for line in lines: - m = re.match(tag_re, line.strip()) + line = line.strip() + m = re.match(group_re, line) if m: - tags.append((m.group(1), m.group(2))) - return tags + group = m.group(1) + groups[group] = [] + continue + m = re.match(tag_re, line) + if m: + tag = m.group(1) + desc = m.group(2) + groups[group].append((tag, desc)) + return groups def generate_tag_indices(): os.makedirs('docs/tags', mode=0o755, exist_ok=True) - with write_file_if_changed('docs/tags/index.rst') as topidx: - for tag_tuple in get_tags(): - tag = tag_tuple[0] - with write_file_if_changed(('docs/tags/{name}.rst').format(name=tag)) as tagidx: - tagidx.write('TODO: add links to the tools that have this tag') - topidx.write(('.. _tag/{name}:\n\n').format(name=tag)) - topidx.write(('{name}\n').format(name=tag)) - topidx.write(('{underline}\n').format(underline='-'*len(tag))) - topidx.write(('{desc}\n\n').format(desc=tag_tuple[1])) - topidx.write(('.. include:: /docs/tags/{name}.rst\n\n').format(name=tag)) + tag_groups = get_tags() + for tag_group in tag_groups: + with write_file_if_changed(('docs/tags/by{group}.rst').format(group=tag_group)) as topidx: + for tag_tuple in tag_groups[tag_group]: + tag = tag_tuple[0] + with write_file_if_changed(('docs/tags/{name}.rst').format(name=tag)) as tagidx: + tagidx.write('TODO: add links to the tools that have this tag') + topidx.write(('.. _tag/{name}:\n\n').format(name=tag)) + topidx.write(('{name}\n').format(name=tag)) + topidx.write(('{underline}\n').format(underline='*'*len(tag))) + topidx.write(('{desc}\n\n').format(desc=tag_tuple[1])) + topidx.write(('.. include:: /docs/tags/{name}.rst\n\n').format(name=tag)) def write_tool_docs(): diff --git a/docs/Categories.rst b/docs/Categories.rst deleted file mode 100644 index 46fc71bdf..000000000 --- a/docs/Categories.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _categories: - -Tool categories -=============== - -DFHack tools are grouped to make them easier to find. Note that a tool can -belong to more than one category. If you'd like to see the full list of tools -in one flat list, please refer to the `index `. - -.. contents:: Contents - :local: - -.. include:: tags/index.rst diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 8908272ca..07b2fa467 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -64,8 +64,8 @@ Tags To make it easier for players to find related commands, all plugins and commands are marked with relevant tags. These are used to compile indices and generate cross-links between the -commands, both in the HTML documents and in-game. See the list of available tags -`here ` and think about which categories your new tools belongs in. +commands, both in the HTML documents and in-game. See the list of available `tag-list` and +think about which categories your new tool belongs in. Links ----- diff --git a/docs/Tags.rst b/docs/Tags.rst index 96faf74e2..7f73bf799 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -1,10 +1,26 @@ :orphan: +.. _tag-list: + +DFHack tool tags +================ + +A tool often has at least one tag per group, encompassing when you use the tool, +why you might want to use it, and what kind of thing you're trying to affect. + +See https://docs.google.com/spreadsheets/d/1hiDlo8M_bB_1jE-5HRs2RrrA_VZ4cRu9VXaTctX_nwk/edit#gid=1774645373 +for the tag assignment spreadsheet. + +"when" tags +----------- - `tag/adventure`: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! - `tag/fort`: Tools that are useful while in fort mode. - `tag/legends`: Tools that are useful while in legends mode. - `tag/embark`: Tools that are useful while on the fort embark screen or while creating an adventurer. - `tag/dev`: Tools that are useful when developing scripts or mods. + +"why" tags +---------- - `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) - `tag/auto`: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. - `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. @@ -14,6 +30,9 @@ - `tag/fps`: Tools that help you manage FPS drop. - `tag/bugfix`: Tools that fix specific bugs, either permanently or on-demand. - `tag/armok`: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. + +"what" tags +----------- - `tag/animals`: Tools that interact with animals. - `tag/buildings`: Tools that interact with buildings and furniture. - `tag/stockpiles`: Tools that interact wtih stockpiles. diff --git a/docs/Tools.rst b/docs/Tools.rst new file mode 100644 index 000000000..623fc9c1f --- /dev/null +++ b/docs/Tools.rst @@ -0,0 +1,50 @@ +.. _tools: + +DFHack tools +============ + +DFHack has **a lot** of tools. This page attempts to make it clearer what they +are, how they work, and how to find the ones you want. + +.. contents:: Contents + :local: + +What tools are and how they work +-------------------------------- + +DFHack is a Dwarf Fortress memory access and modification framework, so DFHack +tools normally access Dwarf Fortress internals and make some specific changes. + +Some tools just make a targeted change when you run them, like `unforbid`, which +scans through all your items and removes the ``forbidden`` flag from each of +them. + +Some tools need to be enabled, and then they run in the background and make +changes to the game on your behalf, like `autobutcher`, which monitors your +livestock population and automatically marks excess animals for butchering. + +And some tools just exist to give you information that is otherwise hard to +come by, like `gui/petitions`, which shows you the active petitions for +guildhalls and temples that you have agreed to. + +Finding the tool you need +------------------------- + +DFHack tools are tagged with categories to make them easier to find. Note that a +tool can belong to more than one category. If you'd like to see the full list of +tools in one flat list, please refer to the `index `. + +DFHack tools by game mode +------------------------- + +.. include:: tags/bywhen.rst + +DFHack tools by theme +--------------------- + +.. include:: tags/bywhy.rst + +DFHack tools by what they affect +-------------------------------- + +.. include:: tags/bywhat.rst diff --git a/index.rst b/index.rst index d627267f0..a54ea87f5 100644 --- a/index.rst +++ b/index.rst @@ -28,7 +28,7 @@ User Manual /docs/Introduction /docs/Installing /docs/Core - /docs/Categories + /docs/Tools /docs/guides/index /docs/index-dev /docs/index-about From 60458ad885139d19aacebacbf34415de7b21606b Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 15 Aug 2022 23:43:02 -0700 Subject: [PATCH 266/334] reorganize and alphabetize tags --- docs/Tags.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 7f73bf799..f1251e391 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -14,35 +14,35 @@ for the tag assignment spreadsheet. "when" tags ----------- - `tag/adventure`: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! +- `tag/embark`: Tools that are useful while on the fort embark screen or while creating an adventurer. - `tag/fort`: Tools that are useful while in fort mode. - `tag/legends`: Tools that are useful while in legends mode. -- `tag/embark`: Tools that are useful while on the fort embark screen or while creating an adventurer. -- `tag/dev`: Tools that are useful when developing scripts or mods. "why" tags ---------- -- `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) +- `tag/armok`: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. - `tag/auto`: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. -- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. -- `tag/inspection`: Tools that let you view information that is otherwise difficult to find. +- `tag/bugfix`: Tools that fix specific bugs, either permanently or on-demand. - `tag/design`: Tools that help you design your fort. -- `tag/gameplay`: Tools that introduce new gameplay elements. +- `tag/dev`: Tools that are useful when developing scripts or mods. +- `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) - `tag/fps`: Tools that help you manage FPS drop. -- `tag/bugfix`: Tools that fix specific bugs, either permanently or on-demand. -- `tag/armok`: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. +- `tag/gameplay`: Tools that introduce new gameplay elements. +- `tag/inspection`: Tools that let you view information that is otherwise difficult to find. +- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. "what" tags ----------- - `tag/animals`: Tools that interact with animals. - `tag/buildings`: Tools that interact with buildings and furniture. -- `tag/stockpiles`: Tools that interact wtih stockpiles. -- `tag/items`: Tools that interact with in-game items. -- `tag/map`: Tools that interact with the game map. +- `tag/graphics`: Tools that interact with game graphics. - `tag/interface`: Tools that interact with or extend the DF user interface. -- `tag/workorders`: Tools that interact with workorders. +- `tag/items`: Tools that interact with in-game items. - `tag/jobs`: Tools that interact with jobs. - `tag/labors`: Tools that deal with labor assignment. +- `tag/map`: Tools that interact with the game map. - `tag/military`: Tools that interact with the military. -- `tag/graphics`: Tools that interact with game graphics. - `tag/plants`: Tools that interact with trees, shrubs, and crops. +- `tag/stockpiles`: Tools that interact wtih stockpiles. - `tag/units`: Tools that interact with units. +- `tag/workorders`: Tools that interact with workorders. From 524c49cb2802dfc370def568ac2292193086466f Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 16 Aug 2022 00:12:08 -0700 Subject: [PATCH 267/334] use easier to read underlining in text output --- conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf.py b/conf.py index c71f69a4b..c2e820b9a 100644 --- a/conf.py +++ b/conf.py @@ -354,3 +354,5 @@ latex_toplevel_sectioning = 'part' from sphinx.writers import text text.MAXWIDTH = 52 + +text_sectionchars = '=-~`+"*' From 83e1fa7de0f9282fdffd9fb5184e87c5c81d26b5 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 16 Aug 2022 00:12:25 -0700 Subject: [PATCH 268/334] add summary text in its own paragraph element --- docs/sphinx_extensions/dfhack/tool_docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index 1c4cd74e0..9b1dbec44 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -142,7 +142,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): nodes.paragraph('', '', *tag_nodes), ] if 'no-command' in self.options: - ret_nodes += [nodes.inline(text=self.options.get('summary', ''))] + ret_nodes += [nodes.paragraph('', '', nodes.inline(text=self.options.get('summary', '')))] return ret_nodes def run(self): @@ -161,7 +161,7 @@ class DFHackCommandDirective(DFHackToolDirectiveBase): command = self.get_name_or_docname() return [ self.make_labeled_paragraph('Command', command, content_class=nodes.literal), - nodes.inline(text=self.options.get('summary', '')), + nodes.paragraph('', '', nodes.inline(text=self.options.get('summary', ''))), *render_dfhack_keybind(command), ] From 4373f865a3d015e22e7133063f6da457375c2684 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 16 Aug 2022 08:23:48 -0700 Subject: [PATCH 269/334] make `dfhack` a "when" tag --- docs/Tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index f1251e391..9f3906d58 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -14,6 +14,7 @@ for the tag assignment spreadsheet. "when" tags ----------- - `tag/adventure`: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! +- `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) - `tag/embark`: Tools that are useful while on the fort embark screen or while creating an adventurer. - `tag/fort`: Tools that are useful while in fort mode. - `tag/legends`: Tools that are useful while in legends mode. @@ -25,7 +26,6 @@ for the tag assignment spreadsheet. - `tag/bugfix`: Tools that fix specific bugs, either permanently or on-demand. - `tag/design`: Tools that help you design your fort. - `tag/dev`: Tools that are useful when developing scripts or mods. -- `tag/dfhack`: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) - `tag/fps`: Tools that help you manage FPS drop. - `tag/gameplay`: Tools that introduce new gameplay elements. - `tag/inspection`: Tools that let you view information that is otherwise difficult to find. From 22356e2fd632e865f411342a5fcee6842d928682 Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 16 Aug 2022 09:36:17 -0700 Subject: [PATCH 270/334] resort tags according to new spreadsheet order also fix tag syncing command so short descriptions with internal periods are parsed correctly --- docs/plugins/autodump.rst | 2 +- docs/plugins/automaterial.rst | 2 +- docs/plugins/automelt.rst | 2 +- docs/plugins/autotrade.rst | 2 +- docs/plugins/blueprint.rst | 2 +- docs/plugins/burrows.rst | 2 +- docs/plugins/cleaners.rst | 2 +- docs/plugins/cursecheck.rst | 2 +- docs/plugins/dig.rst | 2 +- docs/plugins/embark-assistant.rst | 2 +- docs/plugins/embark-tools.rst | 2 +- docs/plugins/filltraffic.rst | 2 +- docs/plugins/mode.rst | 2 +- docs/plugins/prospector.rst | 2 +- docs/plugins/reveal.rst | 2 +- docs/plugins/showmood.rst | 2 +- docs/plugins/stockpiles.rst | 2 +- docs/plugins/stonesense.rst | 2 +- docs/plugins/tweak.rst | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index e2d812219..f9aad09d6 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -3,7 +3,7 @@ autodump .. dfhack-tool:: :summary: Automatically set items in a stockpile to be dumped. - :tags: fort productivity fps armok stockpiles items + :tags: fort armok fps productivity items stockpiles :no-command: .. dfhack-command:: autodump diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 513a963d2..e18fe4b72 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -3,7 +3,7 @@ automaterial .. dfhack-tool:: :summary: Sorts building materials by recent usage. - :tags: fort productivity design buildings map + :tags: fort design productivity buildings map :no-command: This plugin makes building constructions (walls, floors, fortifications, etc) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 09b35a378..047297d83 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -3,7 +3,7 @@ automelt .. dfhack-tool:: :summary: Quickly designate items to be melted. - :tags: fort productivity stockpiles items + :tags: fort productivity items stockpiles :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 946e53508..adff6eade 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -3,7 +3,7 @@ autotrade .. dfhack-tool:: :summary: Quickly designate items to be traded. - :tags: fort productivity stockpiles items + :tags: fort productivity items stockpiles :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index ed3d0b29c..64fb3d556 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -3,7 +3,7 @@ blueprint .. dfhack-tool:: :summary: Record a live game map in a quickfort blueprint. - :tags: fort design buildings stockpiles map + :tags: fort design buildings map stockpiles With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 1bdbd959f..601fdb6b8 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -3,7 +3,7 @@ burrows .. dfhack-tool:: :summary: Auto-expand burrows as you dig. - :tags: fort auto productivity design map units + :tags: fort auto design productivity map units :no-command: .. dfhack-command:: burrow diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index ca022c440..8cc5e20ca 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -6,7 +6,7 @@ cleaners .. dfhack-tool:: :summary: Provides commands for cleaning spatter from the map. - :tags: adventure fort fps armok items map units + :tags: adventure fort armok fps items map units :no-command: .. dfhack-command:: clean diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index afe6b537f..6cf74106e 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -3,7 +3,7 @@ cursecheck .. dfhack-tool:: :summary: Check for cursed creatures. - :tags: dev fps stockpiles + :tags: fort armok inspection units This command checks a single map tile (or the whole map/world) for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 1761b7085..4d096e8b1 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -6,7 +6,7 @@ dig .. dfhack-tool:: :summary: Provides commands for designating tiles for digging. - :tags: fort productivity design map + :tags: fort design productivity map :no-command: .. dfhack-command:: digv diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index 22c18996a..797c23c3d 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -3,7 +3,7 @@ embark-assistant .. dfhack-tool:: :summary: Embark site selection support. - :tags: fort embark interface + :tags: embark fort interface Run this command while the pre-embark screen is displayed to show extended (and reasonably correct) resource information for the embark rectangle as well as diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 58b02d47e..df480ff71 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -3,7 +3,7 @@ embark-tools .. dfhack-tool:: :summary: Extend the embark screen functionality. - :tags: fort embark interface + :tags: embark fort interface Usage:: diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index ea48fd385..218532790 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -6,7 +6,7 @@ filltraffic .. dfhack-tool:: :summary: Set traffic designations using flood-fill starting at the cursor. - :tags: fort productivity design map + :tags: fort design productivity map .. dfhack-command:: alltraffic :summary: Set traffic designations for every single tile of the map. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 94ee7cb44..50cc1d0d2 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -3,7 +3,7 @@ mode .. dfhack-tool:: :summary: See and change the game mode. - :tags: dev gameplay armok + :tags: armok dev gameplay .. warning:: diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 7ef3d223c..5764bc44e 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -5,7 +5,7 @@ prospector .. dfhack-tool:: :summary: Provides commands that help you analyze natural resources. - :tags: fort embark inspection armok map + :tags: embark fort armok inspection map :no-command: .. dfhack-command:: prospect diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index 122280a1e..37497111e 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -5,7 +5,7 @@ reveal .. dfhack-tool:: :summary: Reveals the map. - :tags: adventure fort inspection armok map + :tags: adventure fort armok inspection map .. dfhack-tool:: unreveal :summary: Hides previously hidden tiles again. diff --git a/docs/plugins/showmood.rst b/docs/plugins/showmood.rst index 2a294b166..a14dcf225 100644 --- a/docs/plugins/showmood.rst +++ b/docs/plugins/showmood.rst @@ -3,7 +3,7 @@ showmood .. dfhack-tool:: :summary: Shows all items needed for the active strange mood. - :tags: fort inspection armok jobs units + :tags: fort armok inspection jobs units Usage:: diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 0e30b9dc7..960675d44 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -5,7 +5,7 @@ stockpiles .. dfhack-tool:: :summary: Import and export stockpile settings. - :tags: fort productivity design stockpiles + :tags: fort design productivity stockpiles :no-command: .. dfhack-command:: copystock diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index b43f95697..bd3d438f2 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -3,7 +3,7 @@ stonesense .. dfhack-tool:: :summary: A 3D isometric visualizer. - :tags: adventure fort map graphics + :tags: adventure fort graphics map .. dfhack-command:: ssense :summary: An alias for stonesense. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index c04392c62..0bc156960 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -3,7 +3,7 @@ tweak .. dfhack-tool:: :summary: A collection of tweaks and bugfixes. - :tags: adventure fort fps bugfix armok interface + :tags: adventure fort armok bugfix fps interface Usage:: From fe7414baaeba948b59900aef715862607eabe80c Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 17 Aug 2022 22:22:06 -0400 Subject: [PATCH 271/334] Make .dfhack-tool-summary styles consistent with Sphinx 5+ Under Sphinx 5, `topic` nodes now render as `