diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25f043624..00e01ea22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.18.2 + rev: 0.18.3 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks diff --git a/.readthedocs.requirements.txt b/.readthedocs.requirements.txt new file mode 100644 index 000000000..1f028de41 --- /dev/null +++ b/.readthedocs.requirements.txt @@ -0,0 +1 @@ +sphinx==4.4.0 diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..e66d3da1b --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-20.04 + tools: + python: "3" + +submodules: + include: all + +sphinx: + configuration: conf.py + +formats: all + +python: + install: + - requirements: .readthedocs.requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aa57d574..2e6f5c835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "0.47.05") -set(DFHACK_RELEASE "r6") +set(DFHACK_RELEASE "r7") set(DFHACK_PRERELEASE FALSE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/conf.py b/conf.py index 68d11f0f9..661b0daea 100644 --- a/conf.py +++ b/conf.py @@ -58,9 +58,6 @@ def doc_dir(dirname, files, prefix): def doc_all_dirs(): """Collect the commands and paths to include in our docs.""" 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/builtins'): tools.extend(doc_dir(root, files, os.path.relpath(root, 'docs/builtins'))) for root, _, files in os.walk('docs/plugins'): @@ -69,76 +66,30 @@ def doc_all_dirs(): tools.extend(doc_dir(root, files, os.path.relpath(root, 'scripts/docs'))) return tuple(tools) -DOC_ALL_DIRS = doc_all_dirs() - - -def get_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: - line = line.strip() - m = re.match(group_re, line) - if m: - 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) - 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(): """ 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. + the original documentation. """ - for k in DOC_ALL_DIRS: - header = ':orphan:\n' + for k in doc_all_dirs(): label = ('.. _{name}:\n\n').format(name=k[0]) 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 os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), 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': outfile.write(label) outfile.write(include) -# Actually call the docs generator and run test write_tool_docs() -generate_tag_indices() + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.8' +needs_sphinx = '3.4.3' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -315,11 +266,9 @@ html_sidebars = { ] } -# If false, no module index is generated. -html_domain_indices = False - -# If false, no genindex.html is generated. -html_use_index = True +# generate domain indices but not the (unused) genindex +html_use_index = False +html_domain_indices = True # don't link to rst sources in the generated pages html_show_sourcelink = False diff --git a/data/examples/init/onMapLoad_dreamfort.init b/data/examples/init/onMapLoad_dreamfort.init index 05d32134f..fe2b1d54e 100644 --- a/data/examples/init/onMapLoad_dreamfort.init +++ b/data/examples/init/onMapLoad_dreamfort.init @@ -1,6 +1,7 @@ # This dfhack config file automates common tasks for your forts. # It was written for the Dreamfort set of quickfort blueprints, but the -# configuration here is useful for any fort! Feed free to edit or override +# configuration here is useful for any fort! Copy this file to your +# dfhack-config/init directory to use. Feed free to edit or override # to your liking. # Uncomment this next line if you want buildingplan (and quickfort) to use only @@ -48,13 +49,7 @@ seedwatch all 30 # ensures important tasks get assigned to workers. # otherwise these job types can get ignored in busy forts. -prioritize -a StoreItemInVehicle StoreItemInBag StoreItemInBarrel PullLever -prioritize -a StoreItemInLocation StoreItemInHospital -prioritize -a DestroyBuilding RemoveConstruction RecoverWounded DumpItem -prioritize -a CleanSelf SlaughterAnimal PrepareRawFish ExtractFromRawFish -prioritize -a TradeAtDepot BringItemToDepot CleanTrap ManageWorkOrders -prioritize -a --haul-labor=Food,Body StoreItemInStockpile -prioritize -a --reaction-name=TAN_A_HIDE CustomReaction +prioritize -aq defaults # autobutcher settings are saved in the savegame, so we only need to set them once. # this way, any custom settings you set during gameplay are not overwritten @@ -78,7 +73,7 @@ on-new-fortress autobutcher target 50 50 14 2 BIRD_GOOSE on-new-fortress autobutcher target 2 2 4 2 ALPACA SHEEP LLAMA # pigs give milk and meat and are zero-maintenance. on-new-fortress autobutcher target 5 5 6 2 PIG -# butcher all unprofitable animals +# immediately butcher all unprofitable animals on-new-fortress autobutcher target 0 0 0 0 HORSE YAK DONKEY WATER_BUFFALO GOAT CAVY BIRD_DUCK BIRD_GUINEAFOWL # watch for new animals on-new-fortress autobutcher autowatch diff --git a/docs/Compile.rst b/docs/Compile.rst index 0f3880816..ae23a1b73 100644 --- a/docs/Compile.rst +++ b/docs/Compile.rst @@ -289,7 +289,7 @@ DF, which causes DF to use your system libstdc++ instead:: rm libs/libstdc++.so.6 Note that distributing binaries compiled with newer GCC versions may result in -the opposite compatibily issue: users with *older* GCC versions may encounter +the opposite compatibility issue: users with *older* GCC versions may encounter similar errors. This is why DFHack distributes both GCC 4.8 and GCC 7 builds. If you are planning on distributing binaries to other users, we recommend using an older GCC (but still at least 4.8) version if possible. @@ -314,7 +314,7 @@ Notes for GCC 8+ or OS X 10.10+ users If none of these situations apply to you, skip to `osx-setup`. -If you have issues building on OS X 10.10 (Yosemite) or above, try definining +If you have issues building on OS X 10.10 (Yosemite) or above, try defining the following environment variable:: export MACOSX_DEPLOYMENT_TARGET=10.9 @@ -503,7 +503,7 @@ in their name. If this redirect doesn't occur, just copy, paste, and enter the download link again and you should see the options. You need to get: Visual C++ Build Tools for Visual Studio 2015 with Update 3. Click the download button next to it and a dropdown of download formats will appear. -Select the DVD format to download an ISO file. When the donwload is complete, +Select the DVD format to download an ISO file. When the download is complete, click on the ISO file and a folder will popup with the following contents: * packages (folder) @@ -561,7 +561,7 @@ Additional dependencies: installing with the Chocolatey Package Manager ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The remainder of dependencies - Git, CMake, StrawberryPerl, and Python - can be -most easily installed using the Chocolatey Package Manger. Chocolatey is a +most easily installed using the Chocolatey Package Manager. Chocolatey is a \*nix-style package manager for Windows. It's fast, small (8-20MB on disk) and very capable. Think "``apt-get`` for Windows." diff --git a/docs/Core.rst b/docs/Core.rst index e03a7fcba..012236d59 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/bugfix` script to run on `repeat`. +a `bugfix-tag-index` script to run on `repeat`. .. _onMapLoad.init: diff --git a/docs/Documentation.rst b/docs/Documentation.rst index 13ae58dca..6ac5ef382 100644 --- a/docs/Documentation.rst +++ b/docs/Documentation.rst @@ -76,7 +76,7 @@ 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:: +If you want to link to a heading in your own page, you can specify it like this:: `Heading text exactly as written`_ diff --git a/docs/Introduction.rst b/docs/Introduction.rst index d90983d8c..798a9c3c8 100644 --- a/docs/Introduction.rst +++ b/docs/Introduction.rst @@ -28,7 +28,7 @@ or for coexistence in a single DF install, even with incompatible components. For developers, DFHack unites the various ways tools access DF memory and allows easier development of new tools. As an open-source project under -`various open-source licences `, contributions are welcome. +`various open-source licenses `, contributions are welcome. .. contents:: Contents @@ -54,7 +54,7 @@ used by the DFHack console. to be used this way. * Commands can also run at startup via `init files `, - on in batches at other times with the `script` command. + or in batches at other times with the `script` command. * 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. diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 64ccdd595..c2b84f0f0 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -740,7 +740,7 @@ Functions: * ``dfhack.matinfo.decode(type,index)`` - Looks up material info for the given number pair; if not found, returs *nil*. + Looks up material info for the given number pair; if not found, returns *nil*. * ``....decode(matinfo)``, ``....decode(item)``, ``....decode(obj)`` @@ -1099,7 +1099,7 @@ Other * ``dfhack.gui.getDepthAt(x, y)`` Returns the distance from the z-level of the tile at map coordinates (x, y) to - the closest ground z-level below. Defaults to 0, unless overriden by plugins. + the closest ground z-level below. Defaults to 0, unless overridden by plugins. Job module ---------- @@ -1869,7 +1869,7 @@ Among them are: - ``full_rectangle = true`` For buildings like stockpiles or farm plots that can normally - accomodate individual tile exclusion, forces an error if any + accommodate individual tile exclusion, forces an error if any tiles within the specified width*height are obstructed. - ``items = { item, item ... }``, or ``filters = { {...}, {...}... }`` @@ -2169,7 +2169,7 @@ Supported callbacks and fields are: Called when keyboard or mouse events are available. If any keys are pressed, the keys argument is a table mapping them to *true*. - Note that this refers to logical keybingings computed from real keys via + Note that this refers to logical keybindings computed from real keys via options; if multiple interpretations exist, the table will contain multiple keys. The table also may contain special keys: @@ -2494,7 +2494,7 @@ Core context specific functions: unit of time used, and may be one of ``'frames'`` (raw FPS), ``'ticks'`` (unpaused FPS), ``'days'``, ``'months'``, ``'years'`` (in-game time). All timers other than - ``'frames'`` are cancelled when the world is unloaded, + ``'frames'`` are canceled when the world is unloaded, and cannot be queued until it is loaded again. Returns the timer id, or *nil* if unsuccessful due to world being unloaded. @@ -2677,7 +2677,7 @@ environment by the mandatory init file dfhack.lua: .. _lua-string: -String class extentions +String class extensions ----------------------- DFHack extends Lua's basic string class to include a number of convenience @@ -2698,7 +2698,7 @@ functions. These are invoked just like standard string functions, e.g.:: * ``string:split([delimiter[, plain]])`` Split a string by the given delimiter. If no delimiter is specified, space - (``' '``) is used. The delimter is treated as a pattern unless a ``plain`` is + (``' '``) is used. The delimiter is treated as a pattern unless a ``plain`` is specified and set to ``true``. To treat multiple successive delimiter characters as a single delimiter, e.g. to avoid getting empty string elements, pass a pattern like ``' +'``. Be aware that passing patterns that match empty @@ -2975,10 +2975,10 @@ parameters. (e.g. combining the previous two examples into ``-abcdparam``) Long options focus on clarity. They are usually entire words, or several words - combined with hypens (``-``) or underscores (``_``). If they take an argument, - the argument can be separated from the option name by a space or an equals - sign (``=``). For example, the following two commandlines are equivalent: - ``yourscript --style pretty`` and ``yourscript --style=pretty``. + combined with hyphens (``-``) or underscores (``_``). If they take an + argument, the argument can be separated from the option name by a space or an + equals sign (``=``). For example, the following two commandlines are + equivalent: ``yourscript --style pretty`` and ``yourscript --style=pretty``. Another reason to use long options is if they represent an esoteric parameter that you don't expect to be commonly used and that you don't want to "waste" a @@ -3150,8 +3150,8 @@ Each entry has several properties associated with it: 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: + The optional ``include`` and ``exclude`` filter params are maps (or lists of + maps) with the following elements: :str: if a string, filters by the given substring. if a table of strings, includes entry names that match any of the given substrings. @@ -3160,6 +3160,13 @@ Each entry has several properties associated with it: :entry_type: if a string, matches entries of the given type. if a table of strings, includes entries that match any of the given types. + Elements in a map are ANDed together (e.g. if both ``str`` and ``tag`` are + specified, the match is on any of the ``str`` elements AND any of the ``tag`` + elements). + + If lists of filters are passed instead of a single map, the maps are ORed + (that is, the match succeeds if any of the filters match). + If ``include`` is ``nil`` or empty, then all entries are included. If ``exclude`` is ``nil`` or empty, then no entries are filtered out. @@ -3172,7 +3179,7 @@ create profiler objects which can be used to profile and generate report. * ``profiler.newProfiler([variant[, sampling_frequency]])`` - Returns an profile object with ``variant`` either ``'time'`` or ``'call'``. + Returns a profile object with ``variant`` either ``'time'`` or ``'call'``. ``'time'`` variant takes optional ``sampling_frequency`` parameter to select lua instruction counts between samples. Default is ``'time'`` variant with ``10*1000`` frequency. @@ -3257,7 +3264,7 @@ Implements a trivial single-inheritance class system. The main difference is that attributes are processed as a separate initialization step, before any ``init`` methods are called. They - also make the directy relation between instance fields and constructor + also make the direct relation between instance fields and constructor arguments more explicit. * ``new_obj = Class{ foo = arg, bar = arg, ... }`` @@ -3267,8 +3274,8 @@ Implements a trivial single-inheritance class system. 1. An empty instance table is created, and its metatable set. 2. The ``preinit`` methods are called via ``invoke_before`` (see below) - with the table used as argument to the class. These methods are intended - for validating and tweaking that argument table. + with the table used as the argument to the class. These methods are + intended for validating and tweaking that argument table. 3. Declared ATTRS are initialized from the argument table or their default values. 4. The ``init`` methods are called via ``invoke_after`` with the argument table. This is the main constructor method. @@ -3339,7 +3346,7 @@ A module for reading custom tokens added to the raws by mods. Where ``typeInstance`` is a unit, entity, item, job, projectile, building, plant, or interaction instance. Gets ``typeDefinition`` and then returns the same as ``getToken(typeDefinition, token)``. - For units, it gets the token from the race or caste instead if appplicable. For plants growth items, + For units, it gets the token from the race or caste instead if applicable. For plants growth items, it gets the token from the plant or plant growth instead if applicable. For plants it does the same but with growth number -1. @@ -3661,7 +3668,7 @@ It also always has the following fields: These fields are computed by the layout process: -:frame_parent_rect: The ViewRect represeting the client area of the parent view. +:frame_parent_rect: The ViewRect representing the client area of the parent view. :frame_rect: The ``mkdims`` rect of the outer frame in parent-local coordinates. :frame_body: The ViewRect representing the body part of the View's own frame. @@ -3897,13 +3904,13 @@ Base of all the widgets. Inherits from View and has the following attributes: :r: gap between the right edges of the frame and the parent. :b: gap between the bottom edges of the frame and the parent. :w: maximum width of the frame. - :h: maximum heigth of the frame. + :h: maximum height of the frame. :xalign: X alignment of the frame. :yalign: Y alignment of the frame. First the ``l,t,r,b`` fields restrict the available area for placing the frame. If ``w`` and ``h`` are not specified or - larger then the computed area, it becomes the frame. Otherwise + larger than the computed area, it becomes the frame. Otherwise the smaller frame is placed within the are based on the ``xalign/yalign`` fields. If the align hints are omitted, they are assumed to be 0, 1, or 0.5 based on which of the ``l/r/t/b`` @@ -4025,6 +4032,52 @@ following keyboard hotkeys: - 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. +Scrollbar class +--------------- + +This Widget subclass implements mouse-interactive scrollbars whose bar sizes +represent the amount of content currently visible in an associated display +widget (like a `Label class`_ or a `List class`_). By default they are styled +like scrollbars used in the vanilla DF help screens, but they are configurable. + +Scrollbars have the following attributes: + +:fg: Specifies the pen for the scroll icons and the active part of the bar. Default is ``COLOR_LIGHTGREEN``. +:bg: Specifies the pen for the background part of the scrollbar. Default is ``COLOR_CYAN``. +:on_scroll: A callback called when the scrollbar is scrolled. It will be called with a single string parameter with a value of "up_large", "down_large", "up_small", or "down_small". + +The Scrollbar widget implements the following methods: + +* ``scrollbar:update(top_elem, elems_per_page, num_elems)`` + + Updates the info about the widget that the scrollbar is paired with. + The ``top_elem`` param is the (one-based) index of the first visible element. + The ``elems_per_page`` param is the maximum number of elements that can be + shown at one time. The ``num_elems`` param is the total number of elements + that the paried widget can scroll through. The scrollbar will adjust its + scrollbar size and position accordingly. + +Clicking on the arrows at the top or the bottom of a scrollbar will scroll an +associated widget by a small amount. Clicking on the unfilled portion of the +scrollbar above or below the filled area will scroll by a larger amount in that +direction. The amount of scrolling done in each case in determined by the +associated widget, and after scrolling is complete, the associated widget must +call ``scrollbar:update()`` with updated new display info. + +You can click and drag the scrollbar to scroll to a specific spot, or you can +click and hold on the end arrows or in the unfilled portion of the scrollbar to +scroll multiple times, just like in a normal browser scrollbar. The speed of +scroll events when the mouse button is held down is controlled by two global +variables: + +:``SCROLL_INITIAL_DELAY_MS``: The delay before the second scroll event. +:``SCROLL_DELAY_MS``: The delay between further scroll events. + +The defaults are 300 and 20, respectively, but they can be overridden by the +user in their :file:`dfhack-config/init/dfhack.init` file, for example:: + + :lua require('gui.widgets').SCROLL_DELAY_MS = 100 + Label class ----------- @@ -4045,17 +4098,6 @@ It has the following attributes: 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_scrollbar: Controls scrollbar display: ``false`` for no scrollbar, ``'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``. -:scrollbar_fg: Specifies the pen for the scroll icons and the active part of the bar. Default is ``COLOR_LIGHTGREEN`` (the same as the native DF help screens). -:scrollbar_bg: Specifies the pen for the background part of the scrollbar. Default is ``COLOR_CYAN`` (the same as the native DF help screens). - -If the scrollbar is shown, it will react to mouse clicks on the scrollbar itself. -Clicking on the arrows at the top or the bottom will scroll by one line, and -clicking on the unfilled portion of the scrollbar will scroll by a half page in -that direction. The text itself is represented as a complex structure, and passed to the object via the ``text`` argument of the constructor, or via @@ -4148,7 +4190,8 @@ The Label widget implements the following methods: 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``. + ``+halfpage``, or ``-halfpage``. It returns the number of lines that were + actually scrolled (negative for scrolling up). WrappedLabel class ------------------ @@ -4276,7 +4319,6 @@ Every list item may be specified either as a string, or as a lua table with the following fields: :text: Specifies the label text in the same format as the Label text. -:caption, [1]: Deprecated legacy aliases for **text**. :text_*: Reserved for internal use. :key: Specifies a keybinding that acts as a shortcut for the specified item. :icon: Specifies an icon string, or a pen to paint a single character. May be a callback. @@ -4437,7 +4479,7 @@ Functions .. note:: this is the only mandatory field. :fix_impassible: - if true make impassible tiles impassible to liquids too + if true make impassable tiles impassable to liquids too :consume: how much machine power is needed to work. Disables reactions if not supplied enough and ``needs_power==1`` @@ -4461,7 +4503,7 @@ Functions :canBeRoomSubset: a flag if this building can be counted in room. 1 means it can, 0 means it can't and -1 default building behaviour :auto_gears: - a flag that automatically fills up gears and animate. It looks over building definition for gear icons and maps them. + a flag that automatically fills up gears and animations. It looks over the building definition for gear icons and maps them. Animate table also might contain: @@ -4472,7 +4514,7 @@ Functions ``getPower(building)`` returns two number - produced and consumed power if building can be modified and returns nothing otherwise -``setPower(building,produced,consumed)`` sets current productiona and consumption for a building. +``setPower(building,produced,consumed)`` sets current power production and consumption for a building. Examples -------- @@ -4506,7 +4548,7 @@ Native functions provided by the `buildingplan` plugin: * ``bool isPlanModeEnabled(df::building_type type, int16_t subtype, int32_t custom)`` returns whether the buildingplan UI is enabled for the specified building type. * ``bool isPlannedBuilding(df::building *bld)`` returns whether the given building is managed by buildingplan. * ``void addPlannedBuilding(df::building *bld)`` suspends the building jobs and adds the building to the monitor list. -* ``void doCycle()`` runs a check for whether buildlings in the monitor list can be assigned items and unsuspended. This method runs automatically twice a game day, so you only need to call it directly if you want buildingplan to do a check right now. +* ``void doCycle()`` runs a check for whether buildings in the monitor list can be assigned items and unsuspended. This method runs automatically twice a game day, so you only need to call it directly if you want buildingplan to do a check right now. * ``void scheduleCycle()`` schedules a cycle to be run during the next non-paused game frame. Can be called multiple times while the game is paused and only one cycle will be scheduled. burrows @@ -4740,7 +4782,7 @@ List of events 1. ``onReactionCompleting(reaction,reaction_product,unit,input_items,input_reagents,output_items,call_native)`` - Is called once per reaction product, before reaction had a chance to call native code for item creation. + Is called once per reaction product, before the reaction has a chance to call native code for item creation. Setting ``call_native.value=false`` cancels further processing: no items are created and ``onReactionComplete`` is not called. 2. ``onReactionComplete(reaction,reaction_product,unit,input_items,input_reagents,output_items)`` @@ -4796,7 +4838,7 @@ These events are straight from EventManager module. Each of them first needs to 4. ``onJobCompleted(job)`` - Gets called when job is finished. The job that is passed to this function is a copy. Requires a frequency of 0 in order to distinguish between workshop jobs that were cancelled by the user and workshop jobs that completed successfully. + Gets called when job is finished. The job that is passed to this function is a copy. Requires a frequency of 0 in order to distinguish between workshop jobs that were canceled by the user and workshop jobs that completed successfully. 5. ``onUnitDeath(unit_id)`` @@ -4855,7 +4897,7 @@ Functions 5. ``registerSidebar(shop_name,callback)`` - Enable callback when sidebar for ``shop_name`` is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib. Also accepts a ``class`` instead of function + Enable callback when sidebar for ``shop_name`` is drawn. Useful for custom workshop views e.g. using gui.dwarfmode lib. Also accepts a ``class`` instead of function as callback. Best used with ``gui.dwarfmode`` class ``WorkshopOverlay``. Examples @@ -4898,7 +4940,7 @@ luasocket ========= A way to access csocket from lua. The usage is made similar to luasocket in vanilla lua distributions. Currently -only subset of functions exist and only tcp mode is implemented. +only a subset of the functions exist and only tcp mode is implemented. .. contents:: :local: @@ -4977,7 +5019,7 @@ Functions - ``render_map_rect(x,y,z,w,h)`` - returns a table with w*h*4 entries of rendered tiles. The format is same as ``df.global.gps.screen`` (tile,foreground,bright,background). + returns a table with w*h*4 entries of rendered tiles. The format is the same as ``df.global.gps.screen`` (tile,foreground,bright,background). .. _pathable-api: @@ -5094,7 +5136,7 @@ Scripts :local: Any files with the ``.lua`` extension placed into the :file:`hack/scripts` folder -are automatically made avaiable as DFHack commands. The command corresponding to +are automatically made available as DFHack commands. The command corresponding to a script is simply the script's filename, relative to the scripts folder, with the extension omitted. For example: diff --git a/docs/Memory-research.rst b/docs/Memory-research.rst index d9c0833e3..c4c7aeb63 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 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/NEWS.rst b/docs/NEWS.rst index fb33aebf9..13d5e38de 100644 --- a/docs/NEWS.rst +++ b/docs/NEWS.rst @@ -22,6 +22,6 @@ See `dev-changelog` for a list of changes grouped by development releases. Older Changelogs ================ -Are kept in a seperate file: `History` +Are kept in a separate file: `History` .. that's ``docs/History.rst``, if you're reading the raw text. diff --git a/docs/Structures-intro.rst b/docs/Structures-intro.rst index 48aa571c8..9d304949f 100644 --- a/docs/Structures-intro.rst +++ b/docs/Structures-intro.rst @@ -18,7 +18,7 @@ layout changes, and will need to be recompiled for every new DF version. Addresses of DF global objects and vtables are stored in a separate file, :file:`symbols.xml`. Since these are only absolute addresses, they do not need -to be compiled in to DFHack code, and are instead loaded at runtime. This makes +to be compiled into DFHack code, and are instead loaded at runtime. This makes fixes and additions to global addresses possible without recompiling DFHack. In an installed copy of DFHack, this file can be found at the root of the ``hack`` folder. diff --git a/docs/Support.rst b/docs/Support.rst deleted file mode 100644 index af8bd583c..000000000 --- a/docs/Support.rst +++ /dev/null @@ -1,3 +0,0 @@ -:orphan: - -Please continue to the new `support` page. diff --git a/docs/Tags.rst b/docs/Tags.rst index 9f3906d58..2ee18de48 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -13,36 +13,36 @@ 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. +- `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! +- `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.) +- `embark `: Tools that are useful while on the fort embark screen or while creating an adventurer. +- `fort `: Tools that are useful while in fort mode. +- `legends `: Tools that are useful while in legends mode. "why" tags ---------- -- `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/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/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. -- `tag/productivity`: Tools that help you do things that you could do manually, but using the tool is better and faster. +- `armok `: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. +- `auto `: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. +- `bugfix `: Tools that fix specific bugs, either permanently or on-demand. +- `design `: Tools that help you design your fort. +- `dev `: Tools that are useful when developing scripts or mods. +- `fps `: Tools that help you manage FPS drop. +- `gameplay `: Tools that introduce new gameplay elements. +- `inspection `: Tools that let you view information that is otherwise difficult to find. +- `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/graphics`: Tools that interact with game graphics. -- `tag/interface`: Tools that interact with or extend the DF user interface. -- `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/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. +- `animals `: Tools that interact with animals. +- `buildings `: Tools that interact with buildings and furniture. +- `graphics `: Tools that interact with game graphics. +- `interface `: Tools that interact with or extend the DF user interface. +- `items `: Tools that interact with in-game items. +- `jobs `: Tools that interact with jobs. +- `labors `: Tools that deal with labor assignment. +- `map `: Tools that interact with the game map. +- `military `: Tools that interact with the military. +- `plants `: Tools that interact with trees, shrubs, and crops. +- `stockpiles `: Tools that interact with stockpiles. +- `units `: Tools that interact with units. +- `workorders `: Tools that interact with workorders. diff --git a/docs/Tools.rst b/docs/Tools.rst index 623fc9c1f..401276d7f 100644 --- a/docs/Tools.rst +++ b/docs/Tools.rst @@ -30,9 +30,11 @@ 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 are tagged with categories to make them easier to find. These +categories are listed in the next few sections. Note that a tool can belong to +more than one category. If you already know what you're looking for, try the +`search` or Ctrl-F on this page. If you'd like to see the full list of tools in +one flat list, please refer to the `annotated index `. DFHack tools by game mode ------------------------- @@ -48,3 +50,14 @@ DFHack tools by what they affect -------------------------------- .. include:: tags/bywhat.rst + +All DFHack tools alphabetically +------------------------------- + +.. toctree:: + :glob: + :maxdepth: 1 + :titlesonly: + + tools/* + tools/*/* diff --git a/docs/builtins/ls.rst b/docs/builtins/ls.rst index 7305a0256..cd6bc4126 100644 --- a/docs/builtins/ls.rst +++ b/docs/builtins/ls.rst @@ -40,3 +40,5 @@ Options Don't print out the tags associated with each command. ``--dev`` Include commands intended for developers and modders. +``--exclude [,...]`` + Exclude commands that match any of the given strings. diff --git a/docs/changelog.txt b/docs/changelog.txt index e4166c8e6..ae97bd203 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -33,12 +33,31 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future +## New Plugins + +## Fixes + +## Misc Improvements +- `ls`: indent tag listings and wrap them in the right column for better readability +- `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output. +- `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down +- UX: List widgets now have mouse-interactive scrollbars +- UX: You can now hold down the mouse button on a scrollbar to make it scroll multiple times. +- UX: You can now drag the scrollbar to scroll to a specific spot + +## Documentation + +## API + +## Lua +- ``widgets.Scrollbar``: new scrollbar widget that can be paired with an associated scrollable widget. Integrated with ``widgets.Label`` and ``widgets.List``. + +# 0.47.05-r7 + ## New Plugins - `autonestbox`: split off from `zone` into its own plugin. Note that to enable, the command has changed from ``autonestbox start`` to ``enable autonestbox``. - `autobutcher`: split off from `zone` into its own plugin. Note that to enable, the command has changed from ``autobutcher start`` to ``enable autobutcher``. -- `overlay`: display a "DFHack" button in the lower left corner that you can click to start the new GUI command launcher. - -## New Tweaks +- `overlay`: display a "DFHack" button in the lower left corner that you can click to start the new GUI command launcher. The `dwarfmonitor` weather display had to be moved to make room for the button. If you are seeing the weather indicator rendered over the overlay button, please remove the ``dfhack-config/dwarfmonitor.json`` file to fix the weather indicator display offset. ## New Internal Commands - `tags`: new built-in command to list the tool category tags and their definitions. tags associated with each tool are visible in the tool help and in the output of `ls`. @@ -49,6 +68,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `dig-now`: Fix direction of smoothed walls when adjacent to a door or floodgate - ``job.removeJob()``: ensure jobs are removed from the world list when they are canceled - `quickfort`: `Dreamfort ` blueprint set: declare the hospital zone before building the coffer; otherwise DF fails to stock the hospital with materials +- ``dfhack.buildings.findCivzonesAt``: no longer return duplicate civzones after loading a save with existing civzones ## Misc Improvements - Init scripts: ``dfhack.init`` and other init scripts have moved to ``dfhack-config/init/``. If you have customized your ``dfhack.init`` file and want to keep your changes, please move the part that you have customized to the new location at ``dfhack-config/init/dfhack.init``. If you do not have changes that you want to keep, do not copy anything, and the new defaults will be used automatically. @@ -86,6 +106,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Removed ``Windows`` module (C++-only) - unused. - ``Constructions`` module (C++-only): removed ``t_construction``, ``isValid()``, ``getCount()``, ``getConstruction()``, and ``copyConstruction()``. Access ``world.constructions`` directly instead. - ``Gui::getSelectedItem()``, ``Gui::getAnyItem()``: added support for the artifacts screen +- ``Units::teleport()``: now sets ``unit.idle_area`` to discourage units from walking back to their original location (or teleporting back, if using `fastdwarf`) ## Lua - History: added ``dfhack.getCommandHistory(history_id, history_filename)`` and ``dfhack.addCommandToHistory(history_id, history_filename, command)`` so gui scripts can access a commandline history without requiring a terminal. diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index fa0510290..da33a5a9d 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -129,7 +129,7 @@ provides two libraries for this, ``repeat-util`` and `eventful `. ``repeat-util`` is used to run a function once per a configurable number of frames (paused or unpaused), ticks (unpaused), in-game days, months, or years. If you need to be aware the instant something happens, you'll need to run a -check once a tick. Be careful not to do this gratuitiously, though, since +check once a tick. Be careful not to do this gratuitously, though, since running that often can slow down the game! ``eventful``, on the other hand, is much more performance-friendly since it will @@ -176,10 +176,10 @@ you can react to with ``eventful``. Now, you may have noticed that you won't be able to register multiple callbacks with a single key named after your mod. You can, of course, call all the -functions you want from a single registed callback. Alternately, you can create -multiple callbacks using different keys, using your mod ID as a key name prefix. -If you do register multiple callbacks, though, there are no guarantees about the -call order. +functions you want from a single registered callback. Alternately, you can +create multiple callbacks using different keys, using your mod ID as a key name +prefix. If you do register multiple callbacks, though, there are no guarantees +about the call order. Custom raw tokens ----------------- @@ -348,6 +348,9 @@ timer:: The structure of a full mod --------------------------- +For reference, `Tachy Guns `__ is a +full mod that conforms to this guide. + Create a folder for mod projects somewhere outside your Dwarf Fortress installation directory (e.g. ``/path/to/mymods/``) and use your mod IDs as the names for the mod folders within it. In the example below, we'll use a mod ID of @@ -425,13 +428,18 @@ Ok, you're all set up! Now, let's take a look at an example moduleA.onLoad() moduleB.onLoad() - -- register your callbacks - repeatUtil.scheduleEvery(modId .. ' every tick', 1, 'ticks', - moduleA.every1Tick) + -- multiple functions in the same repeat callback + repeatUtil.scheduleEvery(modId .. ' every tick', 1, 'ticks', function() + moduleA.every1Tick() + moduleB.every1Tick() + end) + + -- one function per repeat callback (you can put them in the + -- above format if you prefer) repeatUtil.scheduleEvery(modId .. ' 100 frames', 1, 'frames', moduleD.every100Frames) - -- multiple functions in the same callback + -- multiple functions in the same eventful callback eventful.onReactionComplete[modId] = function(reaction, reaction_product, unit, input_items, input_reagents, output_items) @@ -442,7 +450,7 @@ Ok, you're all set up! Now, let's take a look at an example unit, input_items, input_reagents, output_items) end - -- one function per callback (you can put them in the + -- one function per eventful callback (you can put them in the -- above format if you prefer) eventful.onProjItemCheckMovement[modId] = moduleD.onProjItemCheckMovement eventful.onProjUnitCheckMovement[modId] = moduleD.onProjUnitCheckMovement diff --git a/docs/guides/quickfort-alias-guide.rst b/docs/guides/quickfort-alias-guide.rst index 50e5d7650..a89e6f338 100644 --- a/docs/guides/quickfort-alias-guide.rst +++ b/docs/guides/quickfort-alias-guide.rst @@ -80,7 +80,7 @@ sequence, potentially with other aliases. If the alias is the only text in the cell, the alias name is matched and its expansion is used. If the alias has other keys before or after it, the alias name must be surrounded in curly brackets (:kbd:`{` and :kbd:`}`). An alias can be surrounded in curly brackets -even if it is the only text in the cell, it just isn't necesary. For example, +even if it is the only text in the cell, it just isn't necessary. For example, the following blueprint uses the ``aliasname`` alias by itself in the first two rows and uses it as part of a longer sequence in the third row:: @@ -454,7 +454,7 @@ be used for either the ``quantum_enable`` or ``route_enable`` sub-aliases. Experienced Dwarf Fortress players may be wondering how the same aliases can work in both contexts since the keys for entering the configuration screen differ. Fear not! There is some sub-alias magic at work here. If you define -your own stockpile configuraiton aliases, you can use the magic yourself by +your own stockpile configuration aliases, you can use the magic yourself by building your aliases on the ``*prefix`` aliases described later in this guide. @@ -652,7 +652,7 @@ sheetprefix enablesheet disablesheet Then, for each item category, there are aliases that manipulate interesting subsets of that category: -* Exclusive aliases forbid everthing within a category and then enable only +* Exclusive aliases forbid everything within a category and then enable only the named item type (or named class of items) * ``forbid*`` aliases forbid the named type and leave the rest of the stockpile untouched. diff --git a/docs/guides/quickfort-library-guide.rst b/docs/guides/quickfort-library-guide.rst index 86c82ac60..157bb10a1 100644 --- a/docs/guides/quickfort-library-guide.rst +++ b/docs/guides/quickfort-library-guide.rst @@ -198,7 +198,7 @@ Light aquifer tap ~~~~~~~~~~~~~~~~~ The aquifer tap helps you create a safe, everlasting source of fresh water from -a light aquifer. See the step-by-step guide, including informaton on how to +a light aquifer. See the step-by-step guide, including information on how to create a drainage system so your dwarves don't drown when digging the tap, by running ``quickfort run library/aquifer_tap.csv -n /help``. diff --git a/docs/guides/quickfort-user-guide.rst b/docs/guides/quickfort-user-guide.rst index 693ab033c..6f606c43a 100644 --- a/docs/guides/quickfort-user-guide.rst +++ b/docs/guides/quickfort-user-guide.rst @@ -190,8 +190,8 @@ dug-out area:: Cw Cw Cw # # # # # # -Note my generosity -- in addition to the bed (:kbd:`b`) I've built a chest -(:kbd:`c`) here for the dwarf as well. You must use the full series of keys +Note my generosity -- in addition to the bed (:kbd:`b`) I've built a container +(:kbd:`h`) here for the dwarf as well. You must use the full series of keys needed to build something in each cell, e.g. :kbd:`C`:kbd:`w` indicates we should enter DF's constructions submenu (:kbd:`C`) and select walls (:kbd:`w`). @@ -245,7 +245,7 @@ If there weren't an alias named ``booze`` then the literal characters spell those aliases correctly! You can save a lot of time and effort by using aliases instead of adding all -key seqences directly to your blueprints. For more details, check out the +key sequences directly to your blueprints. For more details, check out the `quickfort-alias-guide`. You can also see examples of aliases being used in the query blueprints in the :source:`DFHack blueprint library `. You can create @@ -683,7 +683,7 @@ three vertical tiles like this:: ` end here ` # # # # # -Then to carve the cross, you'd do a horizonal segment:: +Then to carve the cross, you'd do a horizontal segment:: ` ` ` # start here ` end here # @@ -740,7 +740,7 @@ Or you could use the aliases to specify tile by tile:: # # # # The aliases can also be used to designate a solid block of track. This is -epecially useful for obliterating low-quality engravings so you can re-smooth +especially useful for obliterating low-quality engravings so you can re-smooth and re-engrave with higher quality. For example, you could use the following sequence of blueprints to ensure a 10x10 floor area contains only masterwork engravings:: @@ -1157,7 +1157,7 @@ blueprint:: "#meta label(help) message(This is the help text for the blueprint set contained in this file. - First, make sure that you embark in...) blueprint set walkthough" + First, make sure that you embark in...) blueprint set walkthrough" could more naturally be written as a ``#notes`` blueprint:: @@ -1739,7 +1739,7 @@ priorities `. Use dig priorities to control ramp creation. We can `ensure `__ -the bottom level is carved out before the layer above is channelled by assigning +the bottom level is carved out before the layer above is channeled by assigning the channel designations lower priorities (the ``h5``\s in the third layer -- scroll down). diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index f71a14343..96fbcf836 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -25,10 +25,10 @@ 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. +New veins are generated using natural-looking 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 diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 873bd761f..1ce32fcbe 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -12,3 +12,6 @@ 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`. + +You must have a mod installed that adds the appropriate tokens in order for this +plugin to do anything. diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index 83c99271a..f288f9073 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -6,7 +6,7 @@ autobutcher :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 +excess livestock for slaughter. It requires that you add the target race(s) to a watch list. Units will be ignored if they are: * Untamed @@ -44,7 +44,7 @@ Usage - 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 + on your current watchlist (including the races which are currently 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, diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index a71d3ed02..08c84327a 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -5,7 +5,7 @@ autohauler :summary: Automatically manage hauling labors. :tags: fort auto labors -Similar to `autolabor`, but instead of managing all labors, ``autohauler`` only +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. @@ -19,7 +19,7 @@ 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 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 +autohauler's effects. By default, this is the unused ALCHEMIST labor, but it can be changed by the user. Usage diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index ed39c2666..63b830dfe 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -11,7 +11,7 @@ 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. +change mid-job, are handled slightly differently to minimize churn. Dwarves on active military duty or dwarves assigned to burrows are left untouched by autolabor. @@ -39,16 +39,16 @@ 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 +or merchants, never to the chief medical dwarf, and less often to the bookkeeper 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, then to additional -dwarfs that meet any of these conditions: +For each labor, a preference order is calculated based on skill, excluding those +who can't do the job. Dwarves who are masters of a skill are deprioritized for +other skills. 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 @@ -62,7 +62,7 @@ Examples ``autolabor MINE 5`` Keep at least 5 dwarves with mining enabled. ``autolabor CUT_GEM 1 1`` - Keep exactly 1 dwarf with gemcutting enabled. + Keep exactly 1 dwarf with gem cutting enabled. ``autolabor COOK 1 1 3`` Keep 1 dwarf with cooking enabled, selected only from the top 3. ``autolabor FEED_WATER_CIVILIANS haulers`` diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index 068664638..ac1b3a1da 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -48,6 +48,6 @@ 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 +The ``automaterial`` plugin also enables extra construction placement modes, +such as designating areas larger than 10x10 and allowing you to designate hollow rectangles instead of the default filled ones. diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst index 9d5683f2b..27074fe1c 100644 --- a/docs/plugins/autonestbox.rst +++ b/docs/plugins/autonestbox.rst @@ -5,13 +5,13 @@ autonestbox :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. +To use this feature, you must create pen/pasture zones on the same tiles as +built 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 diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 9d7d7ef8d..b999c0e99 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -103,6 +103,10 @@ 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"``. +``--smooth`` + Record all smooth tiles in the ``smooth`` phase. If this parameter is not + specified, only tiles that will later be carved into fortifications or + engraved will be smoothed. ``-t``, ``--splitby `` Split blueprints into multiple files. See the `Splitting output into multiple files`_ section below for details. If not specified, defaults to diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 50d736b95..fb3876e97 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -34,7 +34,7 @@ Usage ``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. + Clear target burrow tiles and add 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 [ ...]`` diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index f9467315f..f986a1e3d 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -35,7 +35,7 @@ Examples ``changelayer GRANITE`` Convert the layer at the cursor position into granite. ``changelayer SILTY_CLAY force`` - Convert teh layer at the cursor position into clay, even if it's stone. + Convert the 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. diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index f033a4a6a..4e4512bb1 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -5,7 +5,7 @@ changevein :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 +You can change a vein to any inorganic 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. diff --git a/docs/plugins/command-prompt.rst b/docs/plugins/command-prompt.rst index 7ccad6c71..c4b464a80 100644 --- a/docs/plugins/command-prompt.rst +++ b/docs/plugins/command-prompt.rst @@ -13,7 +13,7 @@ Usage 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 +This is most useful for developers, who can set a keybinding to open a language interpreter for lua or Ruby by starting with the `:lua ` or `:rb ` portions of the command already filled in. diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index 2d623241c..94012919b 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -33,7 +33,7 @@ Examples - ``cursecheck detail all`` Give detailed info about all cursed creatures including deceased ones. - ``cursecheck nick`` - Give a nickname all living/active cursed creatures. + Give a nickname to all living/active cursed creatures. .. note:: diff --git a/docs/plugins/debug.rst b/docs/plugins/debug.rst index 0e184595f..0c5a587fb 100644 --- a/docs/plugins/debug.rst +++ b/docs/plugins/debug.rst @@ -42,13 +42,13 @@ Usage ----- ``debugfilter category [] []`` - List available debug plugin and category names. If filters aren't givenm + List available debug plugin and category names. If filters aren't given 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 + parameter is the id listed as the 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 @@ -61,7 +61,7 @@ Usage ``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. + Enable a space separated 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 diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 4384ade65..a10db97df 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -25,7 +25,7 @@ dig :summary: Designate circles. .. dfhack-command:: digtype - :summary: Designate all vein tiles of the selected type. + :summary: Designate all vein tiles of the same type as the selected tile. .. dfhack-command:: digexp :summary: Designate dig patterns for exploratory mining. @@ -50,9 +50,9 @@ Usage 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. +``digtype [] [-p] [-z]`` + Designate all vein tiles of the same type as the selected tile. See the + `digtype`_ section below for options. ``digexp [] [] [-p]`` Designate dig patterns for exploratory mining. See the `digexp`_ section below for options. @@ -143,6 +143,10 @@ Designation options: ``clear`` Clear any designations. +You can also pass a ``-z`` option, which restricts designations to the current +z-level and down. This is useful when you don't want to designate tiles on the +same z-levels as your carefully dug fort above. + digexp ------ diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index bfdbd13b5..79472ae58 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -87,7 +87,7 @@ Some widgets support additional options: * ``cursor`` widget: * ``format``: Specifies the format. ``X``, ``x``, ``Y``, and ``y`` are - replaced with the corresponding cursor cordinates, while all other + replaced with the corresponding cursor coordinates, 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, diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index e7ddf73d6..fa165250d 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -5,12 +5,12 @@ dwarfvet :summary: Allows animals to be treated at animal hospitals. :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 -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. +Annoyed that 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. Usage ----- diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 64db27953..781d66e90 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -14,7 +14,7 @@ Usage embark-tools enable|disable all embark-tools enable|disable [ ...] -Avaliable tools are: +Available tools are: ``anywhere`` Allows embarking anywhere (including sites, mountain-only biomes, and diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 991c77314..0559d4a2f 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -18,7 +18,7 @@ Usage 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 +the fix just runs once. You can also have it run periodically by enabling the plugin. Examples @@ -35,7 +35,7 @@ Options ``here`` Only operate on the tile at the cursor. ``-n`` - Report issues, but do not any write changes to the map. + Report issues, but do not write any 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/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index a32af524b..26ab378ee 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -64,7 +64,7 @@ Examples ``forceequip v bp QQQ`` List the bodyparts of the selected unit. ``forceequip bp LH`` - Equips an appopriate item onto the unit's left hand. + Equips an appropriate 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 diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index 5c3632b6a..e3443280d 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -37,7 +37,7 @@ 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. +many jobs of each labor are 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 @@ -112,7 +112,7 @@ Advanced usage ``labormanager priority `` Set the priority value for labor to . ``labormanager max `` - Set maximum number of dwarves that can be assigned to a labor. + Set the 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`` diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index ed2816818..9bded57ab 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -11,7 +11,7 @@ Usage ----- ``lair`` - Mark the map as monster lair. + Mark the map as a monster lair. ``lair reset`` Mark the map as ordinary (not lair). diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index 3dc7f35b7..f333f3be0 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -102,7 +102,7 @@ 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. - ``Chef`` (needed: 0, 3) - Buchery, Tanning, and Cooking. It is important to focus just a few dwarves + Butchery, 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) diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index efe3ed0c6..935fc1010 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -2,7 +2,7 @@ map-render ========== .. dfhack-tool:: - :summary: Provides a Lua API for rerendering portions of the map. + :summary: Provides a Lua API for re-rendering portions of the map. :tags: dev graphics :no-command: diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 5e608ca46..701a656b5 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -2,7 +2,7 @@ power-meter =========== .. dfhack-tool:: - :summary: Allow presure plates to measure power. + :summary: Allow pressure plates to measure power. :tags: fort gameplay buildings :no-command: diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 1a3fc7ed9..5512a428b 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -2,7 +2,7 @@ regrass ======= .. dfhack-tool:: - :summary: Regrows all the grass. + :summary: Regrow all the grass. :tags: adventure fort armok animals map Use this command if your grazers have eaten everything down to the dirt. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index 747b74642..ee909fd9a 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -12,7 +12,7 @@ Usage ----- ``rendermax light`` - Light the map tiles realisitically. Outside tiles are light during the day + Light the map tiles realistically. 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`` diff --git a/docs/plugins/resume.rst b/docs/plugins/resume.rst index d6e0b81cb..af3fe161d 100644 --- a/docs/plugins/resume.rst +++ b/docs/plugins/resume.rst @@ -12,7 +12,7 @@ resume 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. +scared off by wildlife or items temporarily blocking building sites. Usage ----- diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index 19066076b..bbcc07b6b 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -7,19 +7,19 @@ reveal :summary: Reveals the map. :tags: adventure fort armok inspection map -.. dfhack-tool:: unreveal +.. dfhack-command:: unreveal :summary: Hides previously hidden tiles again. -.. dfhack-tool:: revforget +.. dfhack-command:: revforget :summary: Discard records about what was visible before revealing the map. -.. dfhack-tool:: revtoggle +.. dfhack-command:: revtoggle :summary: Switch between reveal and unreveal. -.. dfhack-tool:: revflood +.. dfhack-command:: revflood :summary: Hide everything, then reveal tiles with a path to the cursor. -.. dfhack-tool:: nopause +.. dfhack-command:: nopause :summary: Disable pausing. This reveals all z-layers in fort mode. It also works in adventure mode, but any diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index c489b4c40..e588e3196 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -9,7 +9,7 @@ search :no-command: Search options are added to the Stocks, Animals, Trading, Stockpile, Noble -aassignment candidates), Military (position candidates), Burrows (unit list), +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. diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index c471f467d..b41f3a977 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -21,7 +21,7 @@ Usage 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 + Adds the specified 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 `` diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index d8fae9579..03eb3a72c 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -21,4 +21,4 @@ Usage stocks show Running ``stocks show`` will bring you to the fortress-wide stock management -screen from whereever you are. +screen from wherever you are. diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 44fba00df..7b93dd6de 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -58,7 +58,7 @@ 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. + Clear the paint specification 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. diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index 1085897c8..80282f6d9 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -2,7 +2,7 @@ tubefill ======== .. dfhack-tool:: - :summary: Replentishes mined-out adamantine. + :summary: Replenishes mined-out adamantine. :tags: fort armok map Veins that were originally hollow will be left alone. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index bb125fc0d..aa1ad578b 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -66,7 +66,7 @@ Commands that persist until disabled or DF quits: ``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`). + Fixes a crash in the work order condition 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 diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index 9013ba189..f085a40da 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -122,7 +122,7 @@ 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 +raw tokens for items (the same syntax used for custom reaction inputs). For more information, see :wiki:`this wiki page `. The subsequent parts are optional: diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 93b244399..0f77e1d3e 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -74,8 +74,8 @@ 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. +:minage : Minimum age. Must be followed by a number. +:maxage : Maximum age. Must be followed by a 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, diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py index c7883e14c..836bab217 100644 --- a/docs/sphinx_extensions/dfhack/tool_docs.py +++ b/docs/sphinx_extensions/dfhack/tool_docs.py @@ -3,15 +3,21 @@ # 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 collections import defaultdict import logging import os -from typing import List, Optional, Type +import re +from typing import Dict, Iterable, List, Optional, Tuple, Type import docutils.nodes as nodes +from docutils.nodes import Node import docutils.parsers.rst.directives as rst_directives import sphinx import sphinx.addnodes as addnodes import sphinx.directives +from sphinx.domains import Domain, Index, IndexEntry +from sphinx.util.docutils import SphinxDirective +from sphinx.util.nodes import process_index_entry import dfhack.util @@ -46,7 +52,6 @@ def make_summary(builder: sphinx.builders.Builder, summary: str) -> nodes.paragr para += nodes.inline(text=summary) return para - _KEYBINDS = {} _KEYBINDS_RENDERED = set() # commands whose keybindings have been rendered @@ -105,20 +110,39 @@ def check_missing_keybinds(): logger.warning('Undocumented keybindings for command: %s', missing_command) +_anchor_pattern = re.compile(r'^\d+') + +def to_anchor(name: str) -> str: + name = name.lower() + name = name.replace('/', '-') + name = re.sub(_anchor_pattern, '', name) + return name + class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription): has_content = False required_arguments = 0 optional_arguments = 1 + def get_tool_name_from_docname(self): + parts = self.env.docname.split('/') + if 'tools' in parts: + return '/'.join(parts[parts.index('tools') + 1:]) + else: + return parts[-1] + def get_name_or_docname(self): if self.arguments: return self.arguments[0] - else: - parts = self.env.docname.split('/') - if 'tools' in parts: - return '/'.join(parts[parts.index('tools') + 1:]) - else: - return parts[-1] + return self.get_tool_name_from_docname() + + def add_index_entries(self, name) -> None: + docname = self.env.docname + anchor = to_anchor(self.get_tool_name_from_docname()) + tags = self.env.domaindata['tag-repo']['doctags'][docname] + indexdata = (name, self.options.get('summary', ''), '', docname, anchor, 0) + self.env.domaindata['all']['objects'].append(indexdata) + for tag in tags: + self.env.domaindata[tag]['objects'].append(indexdata) @staticmethod def wrap_box(*children: List[nodes.Node]) -> nodes.Admonition: @@ -145,13 +169,15 @@ class DFHackToolDirective(DFHackToolDirectiveBase): def render_content(self) -> List[nodes.Node]: tag_paragraph = self.make_labeled_paragraph('Tags') - for tag in self.options.get('tags', []): + tags = self.options.get('tags', []) + self.env.domaindata['tag-repo']['doctags'][self.env.docname] = tags + for tag in tags: tag_paragraph += [ addnodes.pending_xref(tag, nodes.inline(text=tag), **{ 'reftype': 'ref', 'refdomain': 'std', - 'reftarget': 'tag/' + tag, - 'refexplicit': False, + 'reftarget': tag + '-tag-index', + 'refexplicit': True, 'refwarn': True, }), nodes.inline(text=' | '), @@ -160,6 +186,7 @@ class DFHackToolDirective(DFHackToolDirectiveBase): ret_nodes = [tag_paragraph] if 'no-command' in self.options: + self.add_index_entries(self.get_name_or_docname() + ' (plugin)') ret_nodes += [make_summary(self.env.app.builder, self.options.get('summary', ''))] return ret_nodes @@ -177,6 +204,7 @@ class DFHackCommandDirective(DFHackToolDirectiveBase): def render_content(self) -> List[nodes.Node]: command = self.get_name_or_docname() + self.add_index_entries(command) return [ self.make_labeled_paragraph('Command', command, content_class=nodes.literal), make_summary(self.env.app.builder, self.options.get('summary', '')), @@ -184,16 +212,114 @@ class DFHackCommandDirective(DFHackToolDirectiveBase): ] +class TagRepoDomain(Domain): + name = 'tag-repo' + label = 'Holds tag associations per document' + initial_data = {'doctags': {}} + + def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: + self.data['doctags'].update(otherdata['doctags']) + + +def get_tags(): + groups = {} + group_re = re.compile(r'"([^"]+)"') + tag_re = re.compile(r'- `([^ ]+) <[^>]+>`: (.*)') + with open(os.path.join(dfhack.util.DOCS_ROOT, 'Tags.rst')) as f: + lines = f.readlines() + for line in lines: + line = line.strip() + m = re.match(group_re, line) + if m: + 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 tag_domain_get_objects(self): + for obj in self.data['objects']: + yield(obj) + +def tag_domain_merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: + seen = set() + objs = self.data['objects'] + for obj in objs: + seen.add(obj[0]) + for obj in otherdata['objects']: + if obj[0] not in seen: + objs.append(obj) + objs.sort() + +def tag_index_generate(self, docnames: Optional[Iterable[str]] = None) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]: + content = defaultdict(list) + for name, desc, _, docname, anchor, _ in self.domain.data['objects']: + first_letter = name[0].lower() + extra, descr = desc, '' + if self.domain.env.app.builder.format == 'html': + extra, descr = '', desc + content[first_letter].append( + IndexEntry(name, 0, docname, anchor, extra, '', descr)) + return (sorted(content.items()), False) + +def register_index(app, tag, title): + domain_class = type(tag+'Domain', (Domain, ), { + 'name': tag, + 'label': 'Container domain for tag: ' + tag, + 'initial_data': {'objects': []}, + 'merge_domaindata': tag_domain_merge_domaindata, + 'get_objects': tag_domain_get_objects, + }) + index_class = type(tag+'Index', (Index, ), { + 'name': 'tag-index', + 'localname': title, + 'shortname': tag, + 'generate': tag_index_generate, + }) + app.add_domain(domain_class) + app.add_index_to_domain(tag, index_class) + +def init_tag_indices(app): + os.makedirs(os.path.join(dfhack.util.DOCS_ROOT, 'tags'), mode=0o755, exist_ok=True) + tag_groups = get_tags() + for tag_group in tag_groups: + group_file_path = os.path.join(dfhack.util.DOCS_ROOT, 'tags', 'by{group}.rst'.format(group=tag_group)) + with dfhack.util.write_file_if_changed(group_file_path) as topidx: + for tag_tuple in tag_groups[tag_group]: + tag, desc = tag_tuple[0], tag_tuple[1] + topidx.write(('- `{name} <{name}-tag-index>`\n').format(name=tag)) + topidx.write((' {desc}\n').format(desc=desc)) + register_index(app, tag, desc) + + +def update_index_titles(app): + for domain in app.env.domains.values(): + for index in domain.indices: + if index.shortname == 'all': + continue + if app.builder.format == 'html': + index.localname = '"%s" tag index

%s

' % (index.shortname, index.localname) + else: + index.localname = '"%s" tag index - %s' % (index.shortname, index.localname) + def register(app): app.add_directive('dfhack-tool', DFHackToolDirective) app.add_directive('dfhack-command', DFHackCommandDirective) - + update_index_titles(app) _KEYBINDS.update(scan_all_keybinds(os.path.join(dfhack.util.DFHACK_ROOT, 'data', 'init'))) - def setup(app): app.connect('builder-inited', register) + app.add_domain(TagRepoDomain) + register_index(app, 'all', 'Index of DFHack tools') + init_tag_indices(app) + # TODO: re-enable once detection is corrected # app.connect('build-finished', lambda *_: check_missing_keybinds()) diff --git a/library/Core.cpp b/library/Core.cpp index 87f78f56c..73336b2e7 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -622,12 +622,18 @@ void ls_helper(color_ostream &con, const vector ¶ms) { vector filter; bool skip_tags = false; bool show_dev_commands = false; + string exclude_strs = ""; + bool in_exclude = false; for (auto str : params) { - if (str == "--notags") + if (in_exclude) + exclude_strs = str; + else if (str == "--notags") skip_tags = true; else if (str == "--dev") show_dev_commands = true; + else if (str == "--exclude") + in_exclude = true; else filter.push_back(str); } @@ -636,7 +642,7 @@ void ls_helper(color_ostream &con, const vector ¶ms) { auto L = Lua::Core::State; Lua::StackUnwinder top(L); - if (!lua_checkstack(L, 4) || + if (!lua_checkstack(L, 5) || !Lua::PushModulePublic(con, L, "helpdb", "ls")) { con.printerr("Failed to load helpdb Lua code\n"); return; @@ -645,8 +651,9 @@ void ls_helper(color_ostream &con, const vector ¶ms) { Lua::PushVector(L, filter); Lua::Push(L, skip_tags); Lua::Push(L, show_dev_commands); + Lua::Push(L, exclude_strs); - if (!Lua::SafeCall(con, L, 3, 0)) { + if (!Lua::SafeCall(con, L, 4, 0)) { con.printerr("Failed Lua call to helpdb.ls.\n"); } } diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7076570b9..eb8d92244 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -359,6 +359,149 @@ function EditField:onInput(keys) return self.modal end +--------------- +-- Scrollbar -- +--------------- + +-- these can be overridden by the user, e.g.: +-- require('gui.widgets').SCROLL_DELAY_MS = 100 +SCROLL_INITIAL_DELAY_MS = 300 +SCROLL_DELAY_MS = 20 + +Scrollbar = defclass(Scrollbar, Widget) + +Scrollbar.ATTRS{ + fg = COLOR_LIGHTGREEN, + bg = COLOR_CYAN, + on_scroll = DEFAULT_NIL, +} + +function Scrollbar:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.w = init_table.frame.w or 1 +end + +function Scrollbar:init() + self.last_scroll_ms = 0 + self.is_first_click = false + self.scroll_spec = nil + self.is_dragging = false -- index of the scrollbar tile that we're dragging + self:update(1, 1, 1) +end + +-- calculate and cache the number of tiles of empty space above the top of the +-- scrollbar and the number of tiles the scrollbar should occupy to represent +-- the percentage of text that is on the screen. +-- if elems_per_page or num_elems are not specified, the last values passed to +-- Scrollbar:update() are used. +function Scrollbar:update(top_elem, elems_per_page, num_elems) + if not top_elem then error('must specify index of new top element') end + elems_per_page = elems_per_page or self.elems_per_page + num_elems = num_elems or self.num_elems + + local frame_height = self.frame_body and self.frame_body.height or 3 + local scrollbar_body_height = frame_height - 2 + local height = math.max(1, math.floor( + (math.min(elems_per_page, num_elems) * scrollbar_body_height) / + num_elems)) + + local max_pos = scrollbar_body_height - height + local pos = (num_elems == elems_per_page) and 0 or + math.ceil(((top_elem-1) * max_pos) / + (num_elems - elems_per_page)) + + self.top_elem = top_elem + self.elems_per_page, self.num_elems = elems_per_page, num_elems + self.bar_offset, self.bar_height = pos, height +end + +local function scrollbar_do_drag(scrollbar) + local x,y = dfhack.screen.getMousePos() + x,y = scrollbar.frame_body:localXY(x,y) + local bar_idx = y - scrollbar.bar_offset + local delta = bar_idx - scrollbar.is_dragging + if delta < -scrollbar.bar_height then + scrollbar.on_scroll('up_large') + elseif delta < 0 then + scrollbar.on_scroll('up_small') + elseif delta > scrollbar.bar_height then + scrollbar.on_scroll('down_large') + elseif delta > 0 then + scrollbar.on_scroll('down_small') + end +end + +local UP_ARROW_CHAR = string.char(24) +local DOWN_ARROW_CHAR = string.char(25) +local NO_ARROW_CHAR = string.char(32) +local BAR_CHAR = string.char(7) +local BAR_BG_CHAR = string.char(179) + +function Scrollbar:onRenderBody(dc) + -- don't draw if all elements are visible + if self.elems_per_page >= self.num_elems then return end + -- render up arrow if we're not at the top + dc:seek(0, 0):char( + self.top_elem == 1 and NO_ARROW_CHAR or UP_ARROW_CHAR, self.fg, self.bg) + -- render scrollbar body + local starty = self.bar_offset + 1 + local endy = self.bar_offset + self.bar_height + for y=1,dc.height-2 do + dc:seek(0, y) + if y >= starty and y <= endy then + dc:char(BAR_CHAR, self.fg) + else + dc:char(BAR_BG_CHAR, self.bg) + end + end + -- render down arrow if we're not at the bottom + local last_visible_el = self.top_elem + self.elems_per_page - 1 + dc:seek(0, dc.height-1):char( + last_visible_el >= self.num_elems and NO_ARROW_CHAR or DOWN_ARROW_CHAR, + self.fg, self.bg) + if not self.on_scroll then return end + -- manage state for dragging and continuous scrolling + if self.is_dragging then + scrollbar_do_drag(self) + end + if df.global.enabler.mouse_lbut_down == 0 then + self.last_scroll_ms = 0 + self.is_dragging = false + self.scroll_spec = nil + return + end + if self.last_scroll_ms == 0 then return end + local now = dfhack.getTickCount() + local delay = self.is_first_click and + SCROLL_INITIAL_DELAY_MS or SCROLL_DELAY_MS + if now - self.last_scroll_ms >= delay then + self.is_first_click = false + self.on_scroll(self.scroll_spec) + self.last_scroll_ms = now + end +end + +function Scrollbar:onInput(keys) + if not keys._MOUSE_L_DOWN or not self.on_scroll then return false end + local _,y = self:getMousePos() + if not y then return false end + local scroll_spec = nil + if y == 0 then scroll_spec = 'up_small' + elseif y == self.frame_body.height - 1 then scroll_spec = 'down_small' + elseif y <= self.bar_offset then scroll_spec = 'up_large' + elseif y > self.bar_offset + self.bar_height then scroll_spec = 'down_large' + else + self.is_dragging = y - self.bar_offset + return true + end + self.scroll_spec = scroll_spec + self.on_scroll(scroll_spec) + -- reset continuous scroll state + self.is_first_click = true + self.last_scroll_ms = dfhack.getTickCount() + return true +end + ----------- -- Label -- ----------- @@ -546,12 +689,15 @@ Label.ATTRS{ on_click = DEFAULT_NIL, on_rclick = DEFAULT_NIL, scroll_keys = STANDARDSCROLL, - show_scrollbar = DEFAULT_NIL, -- DEFAULT_NIL, 'right', 'left', false - scrollbar_fg = COLOR_LIGHTGREEN, - scrollbar_bg = COLOR_CYAN } function Label:init(args) + self.scrollbar = Scrollbar{ + frame={r=0}, + on_scroll=self:callback('on_scrollbar')} + + self:addviews{self.scrollbar} + -- 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) @@ -560,6 +706,12 @@ function Label:init(args) end end +local function update_label_scrollbar(label) + local body_height = label.frame_body and label.frame_body.height or 1 + label.scrollbar:update(label.start_line_num, body_height, + label:getTextHeight()) +end + function Label:setText(text) self.start_line_num = 1 self.text = text @@ -569,87 +721,8 @@ function Label:setText(text) self.frame = self.frame or {} self.frame.h = self:getTextHeight() end -end -function Label:update_scroll_inset() - if self.show_scrollbar == nil then - self._show_scrollbar = self:getTextHeight() > self.frame_body.height and 'right' or false - else - self._show_scrollbar = self.show_scrollbar - end - if self._show_scrollbar then - -- here self._show_scrollbar can only be either - -- 'left' or any true value which we interpret as right - local l,t,r,b = gui.parse_inset(self.frame_inset) - if self._show_scrollbar == 'left' and l <= 0 then - l = 1 - elseif r <= 0 then - r = 1 - end - self.frame_inset = {l=l,t=t,r=r,b=b} - end -end - --- the position is the number of tiles of empty space above the top of the --- scrollbar, and the height is the number of tiles the scrollbar should occupy --- to represent the percentage of text that is on the screen. -local function get_scrollbar_pos_and_height(label) - local first_visible_line = label.start_line_num - local text_height = label:getTextHeight() - local last_visible_line = first_visible_line + label.frame_body.height - 1 - local scrollbar_body_height = label.frame_body.height - 2 - local displayed_lines = last_visible_line - first_visible_line - - local height = math.floor(((displayed_lines-1) * scrollbar_body_height) / - text_height) - - local max_pos = scrollbar_body_height - height - local pos = math.ceil(((first_visible_line-1) * max_pos) / - (text_height - label.frame_body.height)) - - return pos, height -end - -local UP_ARROW_CHAR = string.char(24) -local DOWN_ARROW_CHAR = string.char(25) -local NO_ARROW_CHAR = string.char(32) -local BAR_CHAR = string.char(7) -local BAR_BG_CHAR = string.char(179) - -function Label:render_scrollbar(dc, x, y1, y2) - -- render up arrow if we're not at the top - dc:seek(x, y1):char( - self.start_line_num == 1 and NO_ARROW_CHAR or UP_ARROW_CHAR, - self.scrollbar_fg, self.scrollbar_bg) - -- render scrollbar body - local pos, height = get_scrollbar_pos_and_height(self) - local starty = y1 + pos + 1 - local endy = y1 + pos + height - for y=y1+1,y2-1 do - if y >= starty and y <= endy then - dc:seek(x, y):char(BAR_CHAR, self.scrollbar_fg) - else - dc:seek(x, y):char(BAR_BG_CHAR, self.scrollbar_bg) - end - end - -- render down arrow if we're not at the bottom - local last_visible_line = self.start_line_num + self.frame_body.height - 1 - dc:seek(x, y2):char( - last_visible_line >= self:getTextHeight() and - NO_ARROW_CHAR or DOWN_ARROW_CHAR, - self.scrollbar_fg, self.scrollbar_bg) -end - -function Label:computeFrame(parent_rect) - local frame_rect,body_rect = Label.super.computeFrame(self, parent_rect) - - self.frame_rect = frame_rect - self.frame_body = parent_rect:viewport(body_rect or frame_rect) - - self:update_scroll_inset() -- frame_body is now set - - -- recalc with updated frame_inset - return Label.super.computeFrame(self, parent_rect) + update_label_scrollbar(self) end function Label:preUpdateLayout() @@ -659,6 +732,10 @@ function Label:preUpdateLayout() end end +function Label:postUpdateLayout() + update_label_scrollbar(self) +end + function Label:itemById(id) if self.text_ids then return self.text_ids[id] @@ -682,44 +759,19 @@ function Label:onRenderBody(dc) render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self)) end -function Label:onRenderFrame(dc, rect) - if self._show_scrollbar then - local x = self._show_scrollbar == 'left' - and self.frame_body.x1-dc.x1-1 - or self.frame_body.x2-dc.x1+1 - self:render_scrollbar(dc, - x, - self.frame_body.y1-dc.y1, - self.frame_body.y2-dc.y1 - ) - end -end - -function Label:click_scrollbar() - if not self._show_scrollbar then return end - local rect = self.frame_body - local x, y = dscreen.getMousePos() - - if self._show_scrollbar == 'left' and x ~= rect.x1-1 or x ~= rect.x2+1 then - return - end - if y < rect.y1 or y > rect.y2 then - return +function Label:on_scrollbar(scroll_spec) + local v = 0 + if scroll_spec == 'down_large' then + v = '+halfpage' + elseif scroll_spec == 'up_large' then + v = '-halfpage' + elseif scroll_spec == 'down_small' then + v = 1 + elseif scroll_spec == 'up_small' then + v = -1 end - if y == rect.y1 then - return -1 - elseif y == rect.y2 then - return 1 - else - local pos, height = get_scrollbar_pos_and_height(self) - if y <= rect.y1 + pos then - return '-halfpage' - elseif y > rect.y1 + pos + height then - return '+halfpage' - end - end - return nil + self:scroll(v) end function Label:scroll(nlines) @@ -740,24 +792,28 @@ function Label:scroll(nlines) local n = self.start_line_num + nlines n = math.min(n, self:getTextHeight() - self.frame_body.height + 1) n = math.max(n, 1) + nlines = n - self.start_line_num self.start_line_num = n + update_label_scrollbar(self) return nlines end function Label:onInput(keys) if is_disabled(self) then return false end - if keys._MOUSE_L_DOWN then - if not self:scroll(self:click_scrollbar()) and - self:getMousePos() and self.on_click then - self:on_click() - end + if self:inputToSubviews(keys) then + return true + end + if keys._MOUSE_L_DOWN and self:getMousePos() and self.on_click then + self:on_click() + return true end if keys._MOUSE_R_DOWN and self:getMousePos() and self.on_rclick then self:on_rclick() + return true end for k,v in pairs(self.scroll_keys) do - if keys[k] then - self:scroll(v) + if keys[k] and 0 ~= self:scroll(v) then + return true end end return check_text_keys(self, keys) @@ -787,7 +843,7 @@ end -- we can't set the text in init() since we may not yet have a frame that we -- can get wrapping bounds from. function WrappedLabel:postComputeFrame() - local wrapped_text = self:getWrappedText(self.frame_body.width) + local wrapped_text = self:getWrappedText(self.frame_body.width-1) if not wrapped_text then return end local text = {} for _,line in ipairs(wrapped_text:split(NEWLINE)) do @@ -953,6 +1009,11 @@ List.ATTRS{ function List:init(info) self.page_top = 1 self.page_size = 1 + self.scrollbar = Scrollbar{ + frame={r=0}, + on_scroll=self:callback('on_scrollbar')} + + self:addviews{self.scrollbar} if info.choices then self:setChoices(info.choices, info.selected) @@ -1017,13 +1078,21 @@ function List:postComputeFrame(body) self:moveCursor(0) end +local function update_list_scrollbar(list) + list.scrollbar:update(list.page_top, list.page_size, #list.choices) +end + +function List:postUpdateLayout() + update_list_scrollbar(self) +end + function List:moveCursor(delta, force_cb) - local page = math.max(1, self.page_size) local cnt = #self.choices if cnt < 1 then self.page_top = 1 self.selected = 1 + update_list_scrollbar(self) if force_cb and self.on_select then self.on_select(nil,nil) end @@ -1046,14 +1115,40 @@ function List:moveCursor(delta, force_cb) end end + local buffer = 1 + math.min(4, math.floor(self.page_size/10)) + self.selected = 1 + off % cnt - self.page_top = 1 + page * math.floor((self.selected-1) / page) + if (self.selected - buffer) < self.page_top then + self.page_top = math.max(1, self.selected - buffer) + elseif (self.selected + buffer + 1) > (self.page_top + self.page_size) then + local max_page_top = cnt - self.page_size + 1 + self.page_top = math.max(1, + math.min(max_page_top, self.selected - self.page_size + buffer + 1)) + end + update_list_scrollbar(self) if (force_cb or delta ~= 0) and self.on_select then self.on_select(self:getSelected()) end end +function List:on_scrollbar(scroll_spec) + local v = 0 + if scroll_spec == 'down_large' then + v = math.ceil(self.page_size / 2) + elseif scroll_spec == 'up_large' then + v = -math.ceil(self.page_size / 2) + elseif scroll_spec == 'down_small' then + v = 1 + elseif scroll_spec == 'up_small' then + v = -1 + end + + local max_page_top = math.max(1, #self.choices - self.page_size + 1) + self.page_top = math.max(1, math.min(max_page_top, self.page_top + v)) + update_list_scrollbar(self) +end + function List:onRenderBody(dc) local choices = self.choices local top = self.page_top @@ -1122,6 +1217,9 @@ function List:submit2() end function List:onInput(keys) + if self:inputToSubviews(keys) then + return true + end if self.on_submit and keys.SELECT then self:submit() return true diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index e5359b323..5af41c929 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -14,6 +14,8 @@ local _ENV = mkmodule('helpdb') +local argparse = require('argparse') + local MAX_STALE_MS = 60000 -- paths @@ -120,7 +122,7 @@ No help available. ]] local function make_default_entry(entry_name, help_source, kwargs) local default_long_help = DEFAULT_HELP_TEMPLATE:format( - entry_name, ('*'):rep(#entry_name)) + entry_name, ('='):rep(#entry_name)) return { help_source=help_source, short_help='No help available.', @@ -275,7 +277,7 @@ local function make_script_entry(old_entry, entry_name, kwargs) 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), + end_marker=(is_rb and SCRIPT_DOC_END_RUBY or SCRIPT_DOC_END), first_line_is_short_help=(is_rb and '#' or '%-%-')}) return entry end @@ -321,6 +323,8 @@ local function scan_builtins(old_db) HELP_SOURCES.RENDERED or HELP_SOURCES.STUB, {entry_types=entry_types}) end + -- easter egg: replace underline for 'die' help with tombstones + textdb.die.long_help = textdb.die.long_help:gsub('=', string.char(239)) end -- scan for enableable plugins and plugin-provided commands and add their help @@ -394,7 +398,7 @@ local function initialize_tags() desc = desc .. ' ' .. line tag_index[tag].description = desc else - _,_,tag,desc = line:find('^%* (%w+): (.+)') + _,_,tag,desc = line:find('^%* (%w+)[^:]*: (.+)') if not tag then goto continue end tag_index[tag] = {description=desc} in_desc = true @@ -586,6 +590,8 @@ function sort_by_basename(a, b) return false end +-- returns true if all filter elements are matched (i.e. any of the tags AND +-- any of the strings AND any of the entry_types) local function matches(entry_name, filter) if filter.tag then local matched = false @@ -628,9 +634,18 @@ local function matches(entry_name, filter) return true end +local function matches_any(entry_name, filters) + for _,filter in ipairs(filters) do + if matches(entry_name, filter) then + return true + end + end + return false +end + -- normalizes the lists in the filter and returns nil if no filter elements are -- populated -local function normalize_filter(f) +local function normalize_filter_map(f) if not f then return nil end local filter = {} filter.str = normalize_string_list(f.str) @@ -642,11 +657,21 @@ local function normalize_filter(f) return filter end +local function normalize_filter_list(fs) + if not fs then return nil end + local filter_list = {} + for _,f in ipairs(#fs > 0 and fs or {fs}) do + table.insert(filter_list, normalize_filter_map(f)) + end + if #filter_list == 0 then return nil end + return filter_list +end + -- 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: +-- the optional include and exclude filter params are maps (or lists of maps) +-- with the following elements: -- str - if a string, filters by the given substring. if a table of strings, -- 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, @@ -656,14 +681,18 @@ end -- 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. +-- filter elements in a map are ANDed together (e.g. if both str and tag are +-- specified, the match is on any of the str elements AND any of the tag +-- elements). If lists of maps are passed, the maps are ORed (that is, the match +-- succeeds if any of the filters match). function search_entries(include, exclude) ensure_db() - include = normalize_filter(include) - exclude = normalize_filter(exclude) + include = normalize_filter_list(include) + exclude = normalize_filter_list(exclude) local entries = {} for entry in pairs(entrydb) do - if (not include or matches(entry, include)) and - (not exclude or not matches(entry, exclude)) then + if (not include or matches_any(entry, include)) and + (not exclude or not matches_any(entry, exclude)) then table.insert(entries, entry) end end @@ -724,13 +753,15 @@ end local function list_entries(skip_tags, include, exclude) local entries = search_entries(include, exclude) for _,entry in ipairs(entries) do - print_columns(entry, get_entry_short_help(entry)) + local short_help = get_entry_short_help(entry) if not skip_tags then local tags = set_to_sorted_list(get_entry_tags(entry)) if #tags > 0 then - print((' tags: %s'):format(table.concat(tags, ', '))) + local taglist = table.concat(tags, ', ') + short_help = short_help .. NEWLINE .. 'tags: ' .. taglist end end + print_columns(entry, short_help) end if #entries == 0 then print('No matches.') @@ -739,21 +770,30 @@ end -- 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 +-- filter_str - if a tag name (or a list of tag names), will filter by that +-- tag/those tags. otherwise, will filter as a substring/list of +-- substrings -- 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) +-- exclude_strs - comma-separated list of strings. entries are excluded if +-- they match any of the strings. +function ls(filter_str, skip_tags, show_dev_commands, exclude_strs) local include = {entry_type={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 {tag='dev'}) + local excludes = {} + if exclude_strs and #exclude_strs > 0 then + table.insert(excludes, {str=argparse.stringList(exclude_strs)}) + end + if not show_dev_commands then + table.insert(excludes, {tag='dev'}) + end + list_entries(skip_tags, include, excludes) end local function list_tags() diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index dd8d5f0e9..1918771ae 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -308,9 +308,8 @@ df::building *Buildings::findAtTile(df::coord pos) static unordered_map corner1; static unordered_map corner2; -static void cacheBuilding(df::building *building) { +static void cacheBuilding(df::building *building, bool is_civzone) { int32_t id = building->id; - bool is_civzone = !building->isSettingOccupancy(); df::coord p1(min(building->x1, building->x2), min(building->y1,building->y2), building->z); df::coord p2(max(building->x1, building->x2), max(building->y1,building->y2), building->z); @@ -344,7 +343,7 @@ static void cacheNewCivzones() { auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE]; int32_t idx = df::building::binsearch_index(vec, id); if (idx > -1) - cacheBuilding(vec[idx]); + cacheBuilding(vec[idx], true); } nextCivzone = nextBuildingId; } @@ -1311,8 +1310,9 @@ void Buildings::updateBuildings(color_ostream&, void* ptr) if (building) { - if (!corner1.count(id)) - cacheBuilding(building); + bool is_civzone = !building->isSettingOccupancy(); + if (!corner1.count(id) && !is_civzone) + cacheBuilding(building, false); } else if (corner1.count(id)) { diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 8e9f624ea..32b9f28e5 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -174,6 +174,7 @@ bool Units::teleport(df::unit *unit, df::coord target_pos) // move unit to destination unit->pos = target_pos; + unit->idle_area = target_pos; // move unit's riders (including babies) to destination if (unit->flags1.bits.ridden) diff --git a/library/xml b/library/xml index f5fab13fb..f2b59b8d5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f5fab13fb652dd953e9d59e8deca032b26131208 +Subproject commit f2b59b8d5036cca90a88245d0d7c8b2a713f60ee diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 3a0cb60b4..20aa1aa99 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -76,6 +76,8 @@ struct blueprint_options { // base name to use for generated files string name; + // whether to capture all smoothed tiles + bool smooth = false; // whether to capture engravings and smooth the tiles that will be engraved bool engrave = false; @@ -103,6 +105,7 @@ static const struct_field_info blueprint_options_fields[] = { { struct_field_info::PRIMITIVE, "height", offsetof(blueprint_options, height), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "depth", offsetof(blueprint_options, depth), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "name", offsetof(blueprint_options, name), df::identity_traits::get(), 0, 0 }, + { struct_field_info::PRIMITIVE, "smooth", offsetof(blueprint_options, smooth), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "engrave", offsetof(blueprint_options, engrave), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "auto_phase", offsetof(blueprint_options, auto_phase), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits::identity, 0, 0 }, @@ -219,8 +222,8 @@ static const char * get_tile_smooth_minimal(const df::coord &pos, return NULL; } -static const char * get_tile_smooth(const df::coord &pos, - const tile_context &tc) { +static const char * get_tile_smooth_with_engravings(const df::coord &pos, + const tile_context &tc) { const char * smooth_minimal = get_tile_smooth_minimal(pos, tc); if (smooth_minimal) return smooth_minimal; @@ -244,6 +247,30 @@ static const char * get_tile_smooth(const df::coord &pos, return NULL; } +static const char * get_tile_smooth_all(const df::coord &pos, + const tile_context &tc) { + const char * smooth_minimal = get_tile_smooth_minimal(pos, tc); + if (smooth_minimal) + return smooth_minimal; + + df::tiletype *tt = Maps::getTileType(pos); + if (!tt) + return NULL; + + switch (tileShape(*tt)) + { + case tiletype_shape::FLOOR: + case tiletype_shape::WALL: + if (tileSpecial(*tt) == tiletype_special::SMOOTH) + return "s"; + break; + default: + break; + } + + return NULL; +} + static const char * get_track_str(const char *prefix, df::tiletype tt) { TileDirection tdir = tileDirection(tt); @@ -1096,9 +1123,13 @@ static bool do_transform(color_ostream &out, vector processors; + get_tile_fn* smooth_get_tile_fn = get_tile_smooth_minimal; + if (opts.engrave) smooth_get_tile_fn = get_tile_smooth_with_engravings; + if (opts.smooth) smooth_get_tile_fn = get_tile_smooth_all; + add_processor(processors, opts, "dig", "dig", opts.dig, get_tile_dig); add_processor(processors, opts, "dig", "smooth", opts.carve, - opts.engrave ? get_tile_smooth : get_tile_smooth_minimal); + smooth_get_tile_fn); add_processor(processors, opts, "dig", "carve", opts.carve, opts.engrave ? get_tile_carve : get_tile_carve_minimal); add_processor(processors, opts, "build", "build", opts.build, @@ -1187,23 +1218,11 @@ 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.blueprint", "print_help") || - !Lua::SafeCall(out, L, 0, 0)) - { - out.printerr("Failed to load blueprint Lua code\n"); - } -} - // returns whether blueprint generation was successful. populates files with the // names of the files that were generated -static bool do_blueprint(color_ostream &out, - const vector ¶meters, - vector &files) { +static command_result do_blueprint(color_ostream &out, + const vector ¶meters, + vector &files) { CoreSuspender suspend; if (parameters.size() >= 1 && parameters[0] == "gui") { @@ -1221,13 +1240,12 @@ static bool do_blueprint(color_ostream &out, blueprint_options options; if (!get_options(out, options, parameters) || options.help) { - print_help(out); - return options.help; + return CR_WRONG_USAGE; } if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); - return false; + return CR_FAILURE; } // start coordinates can come from either the commandline or the map cursor @@ -1236,13 +1254,13 @@ static bool do_blueprint(color_ostream &out, if (!Gui::getCursorCoords(start)) { out.printerr("Can't get cursor coords! Make sure you specify the" " --cursor parameter or have an active cursor in DF.\n"); - return false; + return CR_FAILURE; } } if (!Maps::isValidTilePos(start)) { out.printerr("Invalid start position: %d,%d,%d\n", start.x, start.y, start.z); - return false; + return CR_FAILURE; } // end coords are one beyond the last processed coordinate. note that @@ -1265,7 +1283,7 @@ static bool do_blueprint(color_ostream &out, bool ok = do_transform(out, start, end, options, files); cache(NULL); - return ok; + return ok ? CR_OK : CR_FAILURE; } // entrypoint when called from Lua. returns the names of the generated files @@ -1284,7 +1302,7 @@ static int run(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) out = &Core::getInstance().getConsole(); - if (do_blueprint(*out, argv, files)) { + if (CR_OK == do_blueprint(*out, argv, files)) { Lua::PushVector(L, files); return 1; } @@ -1294,13 +1312,13 @@ static int run(lua_State *L) { command_result blueprint(color_ostream &out, vector ¶meters) { vector files; - if (do_blueprint(out, parameters, files)) { + command_result cr = do_blueprint(out, parameters, files); + if (cr == CR_OK) { out.print("Generated blueprint file(s):\n"); for (string &fname : files) out.print(" %s\n", fname.c_str()); - return CR_OK; } - return CR_FAILURE; + return cr; } DFHACK_PLUGIN_LUA_COMMANDS { diff --git a/plugins/dig.cpp b/plugins/dig.cpp index ddc2cd97e..945bf3613 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -35,6 +35,7 @@ command_result digtype (color_ostream &out, vector & parameters); DFHACK_PLUGIN("dig"); REQUIRE_GLOBAL(ui_sidebar_menus); REQUIRE_GLOBAL(world); +REQUIRE_GLOBAL(window_z); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { @@ -1417,16 +1418,18 @@ command_result digtype (color_ostream &out, vector & parameters) //mostly copy-pasted from digv int32_t priority = parse_priority(out, parameters); CoreSuspender suspend; - if ( parameters.size() > 1 ) + + if (!Maps::IsValid()) { - out.printerr("Too many parameters.\n"); + out.printerr("Map is not available!\n"); return CR_FAILURE; } - int32_t targetDigType; - if ( parameters.size() == 1 ) - { - string parameter = parameters[0]; + uint32_t xMax,yMax,zMax; + Maps::getSize(xMax,yMax,zMax); + + int32_t targetDigType = -1; + for (string parameter : parameters) { if ( parameter == "clear" ) targetDigType = tile_dig_designation::No; else if ( parameter == "dig" ) @@ -1441,26 +1444,16 @@ command_result digtype (color_ostream &out, vector & parameters) targetDigType = tile_dig_designation::DownStair; else if ( parameter == "up" ) targetDigType = tile_dig_designation::UpStair; + else if ( parameter == "-z" ) + zMax = *window_z + 1; else { - out.printerr("Invalid parameter.\n"); + out.printerr("Invalid parameter: '%s'.\n", parameter.c_str()); return CR_FAILURE; } } - else - { - targetDigType = -1; - } - - if (!Maps::IsValid()) - { - out.printerr("Map is not available!\n"); - return CR_FAILURE; - } int32_t cx, cy, cz; - uint32_t xMax,yMax,zMax; - Maps::getSize(xMax,yMax,zMax); uint32_t tileXMax = xMax * 16; uint32_t tileYMax = yMax * 16; Gui::getCursorCoords(cx,cy,cz); diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index 7b0076e14..5c73be7c0 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -3,37 +3,6 @@ local _ENV = mkmodule('plugins.blueprint') local argparse = require('argparse') local utils = require('utils') --- the info here is very basic and minimal, so hopefully we won't need to change --- it when features are added and the full blueprint docs in Plugins.rst are --- updated. -local help_text = [=[ - -blueprint -========= - -Records the structure of a portion of your fortress in quickfort blueprints. - -Usage: - - blueprint [] [ []] [] - blueprint gui [ []] [] - -Examples: - -blueprint gui - Runs gui/blueprint, the interactive blueprint frontend, where all - configuration 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. Output files are written to - the "blueprints" directory. - -See the online DFHack documentation for more examples and details. -]=] - -function print_help() print(help_text) end - local valid_phase_list = { 'dig', 'carve', @@ -154,6 +123,7 @@ local function process_args(opts, args) {'h', 'help', handler=function() opts.help = true end}, {'s', 'playback-start', hasArg=true, handler=function(optarg) parse_start(opts, optarg) end}, + {nil, 'smooth', handler=function() opts.smooth = true end}, {'t', 'splitby', hasArg=true, handler=function(optarg) parse_split_strategy(opts, optarg) end}, }) diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index 30a5546a4..1a0cc7c02 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -70,7 +70,6 @@ #include "df/viewscreen_topicmeetingst.h" #include "df/viewscreen_topicmeeting_takerequestsst.h" #include "df/viewscreen_tradeagreementst.h" -#include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_tradelistst.h" #include "df/viewscreen_treasurelistst.h" #include "df/viewscreen_unitlist_page.h" @@ -211,7 +210,6 @@ IMPLEMENT_HOOKS(topicmeeting_fill_land_holder_positions) IMPLEMENT_HOOKS(topicmeeting) IMPLEMENT_HOOKS(topicmeeting_takerequests) IMPLEMENT_HOOKS(tradeagreement) -IMPLEMENT_HOOKS(tradegoods) IMPLEMENT_HOOKS(tradelist) IMPLEMENT_HOOKS(treasurelist) IMPLEMENT_HOOKS(unitlist) @@ -310,7 +308,6 @@ DFhackCExport command_result plugin_enable(color_ostream &, bool enable) { INTERPOSE_HOOKS_FAILED(topicmeeting) || INTERPOSE_HOOKS_FAILED(topicmeeting_takerequests) || INTERPOSE_HOOKS_FAILED(tradeagreement) || - INTERPOSE_HOOKS_FAILED(tradegoods) || INTERPOSE_HOOKS_FAILED(tradelist) || INTERPOSE_HOOKS_FAILED(treasurelist) || INTERPOSE_HOOKS_FAILED(unitlist) || diff --git a/scripts b/scripts index 484988bb3..937cf27f9 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 484988bb3d64f7aed9442af3b226faa77d8fd00d +Subproject commit 937cf27f9b41301be6df0fe1d75d77b6f089f688 diff --git a/test/library/gui/widgets.Label.lua b/test/library/gui/widgets.Label.lua index c43b5e886..4693d3d0d 100644 --- a/test/library/gui/widgets.Label.lua +++ b/test/library/gui/widgets.Label.lua @@ -11,70 +11,6 @@ fs.ATTRS = { focus_path = 'test-framed-screen', } -function test.correct_frame_body_with_scroll_icons() - 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{} - expect.eq(o.subviews.text.frame_body.width, 9, "Label's frame_body.x2 and .width should be one smaller because of show_scrollbar.") -end - -function test.correct_frame_body_with_few_text_lines() - local t = {} - for i = 1, 10 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{} - expect.eq(o.subviews.text.frame_body.width, 10, "Label's frame_body.x2 and .width should not change with show_scrollbar = false.") -end - -function test.correct_frame_body_without_show_scrollbar() - 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, - show_scrollbar = false, - }, - } - end - - local o = fs{} - expect.eq(o.subviews.text.frame_body.width, 10, "Label's frame_body.x2 and .width should not change with show_scrollbar = false.") -end - function test.scroll() local t = {} for i = 1, 12 do diff --git a/test/library/gui/widgets.Scrollbar.lua b/test/library/gui/widgets.Scrollbar.lua new file mode 100644 index 000000000..dbe033ba4 --- /dev/null +++ b/test/library/gui/widgets.Scrollbar.lua @@ -0,0 +1,93 @@ +local gui = require('gui') +local widgets = require('gui.widgets') + +function test.update() + local s = widgets.Scrollbar{} + s.frame_body = {height=100} -- give us some space to work with + + -- initial defaults + expect.eq(1, s.top_elem) + expect.eq(1, s.elems_per_page) + expect.eq(1, s.num_elems) + expect.eq(0, s.bar_offset) + expect.eq(1, s.bar_height) + + -- top_elem, elems_per_page, num_elems + s:update(1, 10, 0) + expect.eq(1, s.top_elem) + expect.eq(10, s.elems_per_page) + expect.eq(0, s.num_elems) + expect.eq(0, s.bar_offset) + expect.eq(1, s.bar_height) + + -- first 10 of 50 shown + s:update(1, 10, 50) + expect.eq(1, s.top_elem) + expect.eq(10, s.elems_per_page) + expect.eq(50, s.num_elems) + expect.eq(0, s.bar_offset) + expect.eq(19, s.bar_height) + + -- bottom 10 of 50 shown + s:update(41, 10, 50) + expect.eq(41, s.top_elem) + expect.eq(10, s.elems_per_page) + expect.eq(50, s.num_elems) + expect.eq(79, s.bar_offset) + expect.eq(19, s.bar_height) + + -- ~middle 10 of 50 shown + s:update(23, 10, 50) + expect.eq(23, s.top_elem) + expect.eq(10, s.elems_per_page) + expect.eq(50, s.num_elems) + expect.eq(44, s.bar_offset) + expect.eq(19, s.bar_height) +end + +function test.onInput() + local spec = nil + local mock_on_scroll = function(scroll_spec) spec = scroll_spec end + local s = widgets.Scrollbar{on_scroll=mock_on_scroll} + s.frame_body = {height=100} -- give us some space to work with + local y = nil + s.getMousePos = function() return 0, y end + + -- put scrollbar somewhere in the middle so we can click above and below it + s:update(23, 10, 50) + + expect.false_(s:onInput{}, 'no mouse down') + expect.false_(s:onInput{_MOUSE_L_DOWN=true}, 'no y coord') + + spec, y = nil, 0 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('up_small', spec, 'on up arrow') + + spec, y = nil, 1 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('up_large', spec, 'on body above bar') + + spec, y = nil, 44 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('up_large', spec, 'on body just above bar') + + spec, y = nil, 45 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.nil_(spec, 'on top of bar') + + spec, y = nil, 63 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.nil_(spec, 'on bottom of bar') + + spec, y = nil, 64 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('down_large', spec, 'on body just below bar') + + spec, y = nil, 98 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('down_large', spec, 'on body below bar') + + spec, y = nil, 99 + expect.true_(s:onInput{_MOUSE_L_DOWN=true}) + expect.eq('down_small', spec, 'on down arrow') +end diff --git a/test/library/helpdb.lua b/test/library/helpdb.lua index 7a223bcaa..1f1e58ba9 100644 --- a/test/library/helpdb.lua +++ b/test/library/helpdb.lua @@ -36,89 +36,100 @@ local mock_script_db = { inscript_docs=true, inscript_short_only=true, nodocs_script=true, + dev_script=true, } local files = { ['hack/docs/docs/Tags.txt']=[[ * fort: Tools that are useful while in fort mode. -* armok: Tools that give you complete control over - an aspect of the game or provide access to - information that the game intentionally keeps - hidden. +* armok: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. * map: Tools that interact with the game map. * units: Tools that interact with units. +* dev: Dev tools. + * nomembers: Nothing is tagged with this. ]], ['hack/docs/docs/tools/hascommands.txt']=[[ hascommands -*********** +=========== -**Tags:** fort | armok | units +Tags: fort | armok | units -Documented a plugin that -has commands. + Documented a plugin that has commands. -**Command:** "boxbinders" +Command: "boxbinders" -Documented boxbinders. + Documented boxbinders. -**Command:** "bindboxers" +Command: "bindboxers" -Documented bindboxers. + Documented bindboxers. Documented full help. ]], ['hack/docs/docs/tools/samename.txt']=[[ samename -******** +======== -**Tags:** fort | armok -| units +Tags: fort | armok | units -**Command:** "samename" +Command: "samename" -Documented samename. + Documented samename. Documented full help. ]], ['hack/docs/docs/tools/nocommand.txt']=[[ nocommand -********* +========= -**Tags:** fort | armok | -units +Tags: fort | armok | units -Documented nocommand. + Documented nocommand. Documented full help. ]], ['hack/docs/docs/tools/basic.txt']=[[ basic -***** +===== -**Tags:** map +Tags: map -**Command:** "basic" +Command: "basic" -Documented basic. + Documented basic. Documented full help. ]], ['hack/docs/docs/tools/subdir/scriptname.txt']=[[ subdir/scriptname -***************** +================= -**Tags:** map +Tags: map -**Command:** "subdir/scriptname" +Command: "subdir/scriptname" -Documented subdir/scriptname. + Documented subdir/scriptname. Documented full help. + ]], + ['hack/docs/docs/tools/dev_script.txt']=[[ +dev_script +========== + +Tags: dev + +Command: "dev_script" + + Short desc. + +Full help. +]====] +script contents ]], ['scripts/scriptpath/basic.lua']=[[ -- in-file short description for basic @@ -126,11 +137,11 @@ Documented full help. basic ===== -**Tags:** map +Tags: map -**Command:** "basic" +Command: "basic" -in-file basic. + in-file basic. Documented full help. ]====] @@ -142,11 +153,11 @@ script contents subdir/scriptname ================= -**Tags:** map +Tags: map -**Command:** "subdir/scriptname" +Command: "subdir/scriptname" -in-file scriptname. + in-file scriptname. Documented full help. ]====] @@ -158,11 +169,11 @@ script contents inscript_docs ============= -**Tags:** map | badtag +Tags: map | badtag -**Command:** "inscript_docs" +Command: "inscript_docs" -in-file inscript_docs. + in-file inscript_docs. Documented full help. ]====] @@ -182,11 +193,11 @@ script contents basic ===== -**Tags:** map +Tags: map -**Command:** "basic" +Command: "basic" -in-file basic (other). + in-file basic (other). Documented full help. ]====] @@ -198,11 +209,11 @@ script contents subdir/scriptname ================= -**Tags:** map +Tags: map -**Command:** "subdir/scriptname" +Command: "subdir/scriptname" -in-file scriptname (other). + in-file scriptname (other). Documented full help. ]====] @@ -214,14 +225,17 @@ script contents inscript_docs ============= -**Tags:** map +Tags: map -**Command:** "inscript_docs" +Command: "inscript_docs" -in-file inscript_docs (other). + in-file inscript_docs (other). Documented full help. ]====] +script contents + ]], + ['other/scriptpath/dev_script.lua']=[[ script contents ]], } @@ -400,15 +414,15 @@ end function test.get_entry_long_help() local expected = [[ basic -***** +===== -**Tags:** map +Tags: map -**Command:** +Command: "basic" -Documented -basic. + Documented + basic. Documented full help. @@ -432,34 +446,34 @@ full help. -- plugins/commands that have no doc files get the default template expect.eq([[ls -** +== No help available. ]], h.get_entry_long_help('ls')) expect.eq([[nodocs_hascommands -****************** +================== No help available. ]], h.get_entry_long_help('nodocs_hascommands')) expect.eq([[nodocs_hascommands -****************** +================== No help available. ]], h.get_entry_long_help('nodoc_command')) expect.eq([[Nodocs samename. This command has the same name as its host plugin but no rst docs.]], h.get_entry_long_help('nodocs_samename')) expect.eq([[nodocs_nocommand -**************** +================ No help available. ]], h.get_entry_long_help('nodocs_nocommand')) expect.eq([[nodocs_script -************* +============= No help available. ]], h.get_entry_long_help('nodocs_script')) expect.eq([[inscript_short_only -******************* +=================== No help available. ]], h.get_entry_long_help('inscript_short_only')) @@ -468,11 +482,11 @@ No help available. expect.eq([[inscript_docs ============= -**Tags:** map | badtag +Tags: map | badtag -**Command:** "inscript_docs" +Command: "inscript_docs" -in-file inscript_docs. + in-file inscript_docs. Documented full help.]], h.get_entry_long_help('inscript_docs')) end @@ -501,7 +515,7 @@ function test.is_tag() end function test.get_tags() - expect.table_eq({'armok', 'fort', 'map', 'nomembers', 'units'}, + expect.table_eq({'armok', 'dev', 'fort', 'map', 'nomembers', 'units'}, h.get_tags()) end @@ -540,8 +554,8 @@ end function test.search_entries() -- all entries, in alphabetical order by last path component local expected = {'?', 'alias', 'basic', 'bindboxers', 'boxbinders', - 'clear', 'cls', 'die', 'dir', 'disable', 'devel/dump-rpc', 'enable', - 'fpause', 'hascommands', 'help', 'hide', 'inscript_docs', + 'clear', 'cls', 'dev_script', 'die', 'dir', 'disable', 'devel/dump-rpc', + 'enable', 'fpause', 'hascommands', 'help', 'hide', 'inscript_docs', 'inscript_short_only', 'keybinding', 'kill-lua', 'load', 'ls', 'man', 'nocommand', 'nodoc_command', 'nodocs_hascommands', 'nodocs_nocommand', 'nodocs_samename', 'nodocs_script', 'plug', 'reload', 'samename', @@ -561,19 +575,26 @@ function test.search_entries() expect.table_eq(expected, h.search_entries({str='script', entry_type='builtin'})) - expected = {'inscript_docs', 'inscript_short_only','nodocs_script', - 'subdir/scriptname'} + expected = {'dev_script', 'inscript_docs', 'inscript_short_only', + 'nodocs_script', 'subdir/scriptname'} expect.table_eq(expected, h.search_entries({str='script'}, {entry_type='builtin'})) expected = {'bindboxers', 'boxbinders'} expect.table_eq(expected, h.search_entries({str='box'})) + + expected = {'bindboxers', 'boxbinders', 'inscript_docs', + 'inscript_short_only', 'nodocs_script', 'subdir/scriptname'} + expect.table_eq(expected, h.search_entries({{str='script'}, {str='box'}}, + {{entry_type='builtin'}, + {tag='dev'}}), + 'multiple filters for include and exclude') end function test.get_commands() local expected = {'?', 'alias', 'basic', 'bindboxers', 'boxbinders', - 'clear', 'cls', 'die', 'dir', 'disable', 'devel/dump-rpc', 'enable', - 'fpause', 'help', 'hide', 'inscript_docs', 'inscript_short_only', + 'clear', 'cls', 'dev_script', 'die', 'dir', 'disable', 'devel/dump-rpc', + 'enable', 'fpause', 'help', 'hide', 'inscript_docs', 'inscript_short_only', 'keybinding', 'kill-lua', 'load', 'ls', 'man', 'nodoc_command', 'nodocs_samename', 'nodocs_script', 'plug', 'reload', 'samename', 'script', 'subdir/scriptname', 'sc-script', 'show', 'tags', 'type', @@ -604,21 +625,23 @@ function test.tags() local mock_print = mock.func() mock.patch(h, 'print', mock_print, function() h.tags() - expect.eq(7, mock_print.call_count) + expect.eq(8, mock_print.call_count) expect.eq('armok Tools that give you complete control over an aspect of the', mock_print.call_args[1][1]) expect.eq(' game or provide access to information that the game', mock_print.call_args[2][1]) expect.eq(' intentionally keeps hidden.', mock_print.call_args[3][1]) - expect.eq('fort Tools that are useful while in fort mode.', + expect.eq('dev Dev tools.', mock_print.call_args[4][1]) - expect.eq('map Tools that interact with the game map.', + expect.eq('fort Tools that are useful while in fort mode.', mock_print.call_args[5][1]) - expect.eq('nomembers Nothing is tagged with this.', + expect.eq('map Tools that interact with the game map.', mock_print.call_args[6][1]) - expect.eq('units Tools that interact with units.', + expect.eq('nomembers Nothing is tagged with this.', mock_print.call_args[7][1]) + expect.eq('units Tools that interact with units.', + mock_print.call_args[8][1]) end) end @@ -643,7 +666,7 @@ function test.ls() expect.eq(5, mock_print.call_count) expect.eq('inscript_docs in-file short description for inscript_docs.', mock_print.call_args[1][1]) - expect.eq(' tags: map', mock_print.call_args[2][1]) + expect.eq(' tags: map', mock_print.call_args[2][1]) expect.eq('nodoc_command cpp description.', mock_print.call_args[3][1]) expect.eq('nodocs_samename Nodocs samename.', @@ -658,15 +681,15 @@ function test.ls() expect.eq(6, mock_print.call_count) expect.eq('bindboxers Bind your boxers.', mock_print.call_args[1][1]) - expect.eq(' tags: armok, fort, units', + expect.eq(' tags: armok, fort, units', mock_print.call_args[2][1]) expect.eq('boxbinders Box your binders.', mock_print.call_args[3][1]) - expect.eq(' tags: armok, fort, units', + expect.eq(' tags: armok, fort, units', mock_print.call_args[4][1]) expect.eq('samename Samename.', mock_print.call_args[5][1]) - expect.eq(' tags: armok, fort, units', + expect.eq(' tags: armok, fort, units', mock_print.call_args[6][1]) end) @@ -676,4 +699,29 @@ function test.ls() expect.eq(1, mock_print.call_count) expect.eq('No matches.', mock_print.call_args[1][1]) end) + + -- test skipping tags and excluding strings + mock_print = mock.func() + mock.patch(h, 'print', mock_print, function() + h.ls('armok', true, false, 'boxer,binder') + expect.eq(1, mock_print.call_count) + expect.eq('samename Samename.', mock_print.call_args[1][1]) + end) + + -- test excluding dev scripts + mock_print = mock.func() + mock.patch(h, 'print', mock_print, function() + h.ls('_script', true, false, 'inscript,nodocs') + expect.eq(1, mock_print.call_count) + expect.eq('No matches.', mock_print.call_args[1][1]) + end) + + -- test including dev scripts + mock_print = mock.func() + mock.patch(h, 'print', mock_print, function() + h.ls('_script', true, true, 'inscript,nodocs') + expect.eq(1, mock_print.call_count) + expect.eq('dev_script Short desc.', + mock_print.call_args[1][1]) + end) end diff --git a/test/plugins/blueprint.lua b/test/plugins/blueprint.lua index 27a2634b4..a19d519b8 100644 --- a/test/plugins/blueprint.lua +++ b/test/plugins/blueprint.lua @@ -35,6 +35,12 @@ function test.parse_gui_commandline() name='blueprint', engrave=true,}, opts) + opts = {} + b.parse_gui_commandline(opts, {'--smooth'}) + expect.table_eq({auto_phase=true, format='minimal', split_strategy='none', + name='blueprint', smooth=true,}, + opts) + opts = {} b.parse_gui_commandline(opts, {'--engrave'}) expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',