diff --git a/.github/release_template.md b/.github/release_template.md
new file mode 100644
index 000000000..5ca0a1983
--- /dev/null
+++ b/.github/release_template.md
@@ -0,0 +1,64 @@
+#### Q: How do I download DFHack?
+**A:** Either add to your Steam library from our [Steam page](https://store.steampowered.com/app/2346660/DFHack) or scroll to the latest release on our [GitHub releases page](https://github.com/DFHack/dfhack/releases), expand the "Assets" list, and download the file for your platform (e.g. `dfhack-XX.XX-rX-Windows-64bit.zip`.
+
+-------------
+
+This release is compatible with all distributions of Dwarf Fortress: [Steam](https://store.steampowered.com/app/975370/Dwarf_Fortress/), [Itch](https://kitfoxgames.itch.io/dwarf-fortress), and [Classic](https://www.bay12games.com/dwarves/).
+
+- [Install DFHack from Steam](https://store.steampowered.com/app/2346660/DFHack)
+- [Manual install](https://docs.dfhack.org/en/stable/docs/Installing.html#installing)
+- [Quickstart guide (for players)](https://docs.dfhack.org/en/stable/docs/Quickstart.html#quickstart)
+- [Modding guide (for modders)](https://docs.dfhack.org/en/stable/docs/guides/modding-guide.html)
+
+Please report any issues (or feature requests) on the DFHack [GitHub issue tracker](https://github.com/DFHack/dfhack/issues). When reporting issues, please upload a zip file of your savegame and a zip file of your `mods` directory to the cloud and add links to the GitHub issue. Make sure your files are downloadable by "everyone with the link". We need your savegame to reproduce the problem and test the fix, and we need your active mods so we can load your savegame. Issues with savegames and mods attached get fixed first!
+
+Highlights
+----------------------------------
+
+
+Highlight 1, Highlight 2
+
+### Highlight 1
+
+Demo screenshot/vidcap
+
+Text
+
+### Highlight 2
+
+Demo screenshot/vidcap
+
+Text
+
+
+
+Announcements
+----------------------------------
+
+
+Annc 1, PSAs
+
+### Annc 1
+
+Text
+
+### PSAs
+
+As always, remember that, just like the vanilla DF game, DFHack tools can also have bugs. It is a good idea to **save often and keep backups** of the forts that you care about.
+
+Many DFHack tools that worked in previous (pre-Steam) versions of DF have not been updated yet and are marked with the "unavailable" tag in their docs. If you try to run them, they will show a warning and exit immediately. You can run the command again to override the warning (though of course the tools may not work). We make no guarantees of reliability for the tools that are marked as "unavailable".
+
+The in-game interface for running DFHack commands (`gui/launcher`) will not show "unavailable" tools by default. You can still run them if you know their names, or you can turn on dev mode by hitting Ctrl-D while in `gui/launcher` and they will be added to the autocomplete list. Some tools do not compile yet and are not available at all, even when in dev mode.
+
+If you see a tool complaining about the lack of a cursor, know that it's referring to the **keyboard** cursor (which used to be the only real option in Dwarf Fortress). You can enable the keyboard cursor by entering mining mode or selecting the dump/forbid tool and hitting Alt-K (the DFHack keybinding for `toggle-kbd-cursor`). We're working on making DFHack tools more mouse-aware and accessible so this step isn't necessary in the future.
+
+
+
+Generated release notes
+====================
+
+
+New tools, fixes, and improvements
+
+%RELEASE_NOTES%
+
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
new file mode 100644
index 000000000..aa5571e0b
--- /dev/null
+++ b/.github/workflows/build-linux.yml
@@ -0,0 +1,168 @@
+name: Build linux64
+
+on:
+ workflow_call:
+ inputs:
+ dfhack_ref:
+ type: string
+ scripts_ref:
+ type: string
+ structures_ref:
+ type: string
+ artifact-name:
+ type: string
+ append-date-and-hash:
+ type: boolean
+ default: false
+ cache-id:
+ type: string
+ default: ''
+ cache-readonly:
+ type: boolean
+ default: false
+ platform-files:
+ type: boolean
+ default: true
+ common-files:
+ type: boolean
+ default: true
+ docs:
+ type: boolean
+ default: false
+ html:
+ type: boolean
+ default: true
+ stonesense:
+ type: boolean
+ default: false
+ extras:
+ type: boolean
+ default: false
+ tests:
+ type: boolean
+ default: false
+ xml-dump-type-sizes:
+ type: boolean
+ default: false
+ gcc-ver:
+ type: string
+ default: "10"
+
+jobs:
+ build-linux64:
+ name: Build linux64
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Install basic build dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install ninja-build
+ - name: Install binary build dependencies
+ if: inputs.platform-files || inputs.xml-dump-type-sizes
+ run: |
+ sudo apt-get install \
+ ccache \
+ gcc-${{ inputs.gcc-ver }} \
+ g++-${{ inputs.gcc-ver }} \
+ libxml-libxslt-perl
+ - name: Install stonesense dependencies
+ if: inputs.stonesense
+ run: sudo apt-get install libgl-dev
+ - name: Install doc dependencies
+ if: inputs.docs
+ run: pip install 'sphinx<4.4.0'
+ - name: Clone DFHack
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.dfhack_ref && github.repository || 'DFHack/dfhack' }}
+ ref: ${{ inputs.dfhack_ref }}
+ submodules: true
+ fetch-depth: ${{ !inputs.platform-files && 1 || 0 }}
+ - name: Clone scripts
+ if: inputs.scripts_ref
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.scripts_ref && github.repository || 'DFHack/scripts' }}
+ ref: ${{ inputs.scripts_ref }}
+ path: scripts
+ - name: Clone structures
+ if: inputs.structures_ref
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.structures_ref && github.repository || 'DFHack/df-structures' }}
+ ref: ${{ inputs.structures_ref }}
+ path: library/xml
+ - name: Fetch ccache
+ if: inputs.platform-files
+ uses: actions/cache/restore@v3
+ with:
+ path: ~/.cache/ccache
+ key: linux-gcc-${{ inputs.gcc-ver }}-${{ inputs.cache-id }}-${{ github.sha }}
+ restore-keys: |
+ linux-gcc-${{ inputs.gcc-ver }}-${{ inputs.cache-id }}
+ linux-gcc-${{ inputs.gcc-ver }}
+ - name: Configure DFHack
+ env:
+ CC: gcc-${{ inputs.gcc-ver }}
+ CXX: g++-${{ inputs.gcc-ver }}
+ run: |
+ cmake \
+ -S . \
+ -B build \
+ -G Ninja \
+ -DCMAKE_INSTALL_PREFIX=build/image \
+ -DCMAKE_BUILD_TYPE=Release \
+ ${{ inputs.platform-files && '-DCMAKE_C_COMPILER_LAUNCHER=ccache' || '' }} \
+ ${{ inputs.platform-files && '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache' || '' }} \
+ -DBUILD_LIBRARY:BOOL=${{ inputs.platform-files }} \
+ -DBUILD_PLUGINS:BOOL=${{ inputs.platform-files }} \
+ -DBUILD_STONESENSE:BOOL=${{ inputs.stonesense }} \
+ -DBUILD_DEV_PLUGINS:BOOL=${{ inputs.extras }} \
+ -DBUILD_SIZECHECK:BOOL=${{ inputs.extras }} \
+ -DBUILD_SKELETON:BOOL=${{ inputs.extras }} \
+ -DBUILD_DOCS:BOOL=${{ inputs.docs }} \
+ -DBUILD_DOCS_NO_HTML:BOOL=${{ !inputs.html }} \
+ -DBUILD_TESTS:BOOL=${{ inputs.tests }} \
+ -DBUILD_XMLDUMP:BOOL=${{ inputs.xml-dump-type-sizes }} \
+ ${{ inputs.xml-dump-type-sizes && '-DINSTALL_XMLDUMP:BOOL=1' || ''}} \
+ -DINSTALL_DATA_FILES:BOOL=${{ inputs.common-files }} \
+ -DINSTALL_SCRIPTS:BOOL=${{ inputs.common-files }}
+ - name: Build DFHack
+ run: ninja -C build install
+ - name: Run cpp tests
+ if: inputs.platform-files
+ run: ninja -C build test
+ - name: Finalize cache
+ if: inputs.platform-files
+ run: |
+ ccache --show-stats --verbose
+ ccache --max-size 40M
+ ccache --cleanup
+ ccache --max-size 500M
+ ccache --zero-stats
+ - name: Save ccache
+ if: inputs.platform-files && !inputs.cache-readonly
+ uses: actions/cache/save@v3
+ with:
+ path: ~/.cache/ccache
+ key: linux-gcc-${{ inputs.gcc-ver }}-${{ inputs.cache-id }}-${{ github.sha }}
+ - name: Format artifact name
+ if: inputs.artifact-name
+ id: artifactname
+ run: |
+ if test "false" = "${{ inputs.append-date-and-hash }}"; then
+ echo name=${{ inputs.artifact-name }} >> $GITHUB_OUTPUT
+ else
+ echo name=${{ inputs.artifact-name }}-$(date +%Y%m%d)-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
+ fi
+ - name: Prep artifact
+ if: inputs.artifact-name
+ run: |
+ cd build/image
+ tar cjf ../../${{ steps.artifactname.outputs.name }}.tar.bz2 .
+ - name: Upload artifact
+ if: inputs.artifact-name
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ steps.artifactname.outputs.name }}
+ path: ${{ steps.artifactname.outputs.name }}.tar.bz2
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
new file mode 100644
index 000000000..795482d80
--- /dev/null
+++ b/.github/workflows/build-windows.yml
@@ -0,0 +1,135 @@
+name: Build win64
+
+on:
+ workflow_call:
+ inputs:
+ dfhack_ref:
+ type: string
+ scripts_ref:
+ type: string
+ structures_ref:
+ type: string
+ artifact-name:
+ type: string
+ append-date-and-hash:
+ type: boolean
+ default: false
+ cache-id:
+ type: string
+ default: ''
+ cache-readonly:
+ type: boolean
+ default: false
+ platform-files:
+ type: boolean
+ default: true
+ common-files:
+ type: boolean
+ default: true
+ docs:
+ type: boolean
+ default: false
+ html:
+ type: boolean
+ default: true
+ stonesense:
+ type: boolean
+ default: false
+ tests:
+ type: boolean
+ default: false
+ xml-dump-type-sizes:
+ type: boolean
+ default: false
+ launchdf:
+ type: boolean
+ default: false
+
+
+jobs:
+ build-win64:
+ name: Build win64
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install ccache
+ - name: Clone DFHack
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.dfhack_ref && github.repository || 'DFHack/dfhack' }}
+ ref: ${{ inputs.dfhack_ref }}
+ submodules: true
+ fetch-depth: 0
+ - name: Clone scripts
+ if: inputs.scripts_ref
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.scripts_ref && github.repository || 'DFHack/scripts' }}
+ ref: ${{ inputs.scripts_ref }}
+ path: scripts
+ - name: Clone structures
+ if: inputs.structures_ref
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.structures_ref && github.repository || 'DFHack/df-structures' }}
+ ref: ${{ inputs.structures_ref }}
+ path: library/xml
+ - name: Get 3rd party SDKs
+ if: inputs.launchdf
+ uses: actions/checkout@v3
+ with:
+ repository: DFHack/3rdparty
+ ref: main
+ ssh-key: ${{ secrets.DFHACK_3RDPARTY_TOKEN }}
+ path: depends/steam
+ - name: Fetch ccache
+ if: inputs.platform-files
+ uses: actions/cache/restore@v3
+ with:
+ path: build/win64-cross/ccache
+ key: win-msvc-${{ inputs.cache-id }}-${{ github.sha }}
+ restore-keys: |
+ win-msvc-${{ inputs.cache-id }}
+ win-msvc
+ - name: Cross-compile
+ env:
+ CMAKE_EXTRA_ARGS: -DBUILD_LIBRARY=${{ inputs.platform-files }} -DBUILD_STONESENSE:BOOL=${{ inputs.stonesense }} -DBUILD_DOCS:BOOL=${{ inputs.docs }} -DBUILD_DOCS_NO_HTML:BOOL=${{ !inputs.html }} -DINSTALL_DATA_FILES:BOOL=${{ inputs.common-files }} -DINSTALL_SCRIPTS:BOOL=${{ inputs.common-files }} -DBUILD_DFLAUNCH:BOOL=${{ inputs.launchdf }} -DBUILD_TESTS:BOOL=${{ inputs.tests }} -DBUILD_XMLDUMP:BOOL=${{ inputs.xml-dump-type-sizes }} ${{ inputs.xml-dump-type-sizes && '-DINSTALL_XMLDUMP:BOOL=1' || '' }}
+ run: |
+ cd build
+ bash -x build-win64-from-linux.sh
+ - name: Finalize cache
+ run: |
+ cd build
+ ccache -d win64-cross/ccache --show-stats --verbose
+ ccache -d win64-cross/ccache --max-size 150M
+ ccache -d win64-cross/ccache --cleanup
+ ccache -d win64-cross/ccache --max-size 500M
+ ccache -d win64-cross/ccache --zero-stats
+ - name: Save ccache
+ if: inputs.platform-files && !inputs.cache-readonly
+ uses: actions/cache/save@v3
+ with:
+ path: build/win64-cross/ccache
+ key: win-msvc-${{ inputs.cache-id }}-${{ github.sha }}
+ - name: Format artifact name
+ if: inputs.artifact-name
+ id: artifactname
+ run: |
+ if test "false" = "${{ inputs.append-date-and-hash }}"; then
+ echo name=${{ inputs.artifact-name }} >> $GITHUB_OUTPUT
+ else
+ echo name=${{ inputs.artifact-name }}-$(date +%Y%m%d)-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
+ fi
+ - name: Prep artifact
+ if: inputs.artifact-name
+ run: |
+ cd build/win64-cross/output
+ tar cjf ../../../${{ steps.artifactname.outputs.name }}.tar.bz2 .
+ - name: Upload artifact
+ if: inputs.artifact-name
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ steps.artifactname.outputs.name }}
+ path: ${{ steps.artifactname.outputs.name }}.tar.bz2
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c0b621c4c..10a6d332d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,218 +3,32 @@ name: Build
on: [push, pull_request]
jobs:
- build:
- runs-on: ${{ matrix.os }}
- name: build (Linux, GCC ${{ matrix.gcc }}, ${{ matrix.plugins }} plugins)
- strategy:
- fail-fast: false
- matrix:
- os:
- - ubuntu-22.04
- gcc:
- - 10
- plugins:
- - default
- include:
- - os: ubuntu-22.04
- gcc: 12
- plugins: all
- steps:
- - name: Set up Python 3
- uses: actions/setup-python@v4
- with:
- python-version: 3
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install \
- ccache \
- libgtk2.0-0 \
- libncursesw5 \
- libsdl-image1.2-dev \
- libsdl-ttf2.0-dev \
- libsdl1.2-dev \
- libxml-libxml-perl \
- libxml-libxslt-perl \
- lua5.3 \
- ninja-build \
- zlib1g-dev
- pip install 'sphinx<4.4.0'
- - name: Install GCC
- run: |
- sudo apt-get install gcc-${{ matrix.gcc }} g++-${{ matrix.gcc }}
- - name: Clone DFHack
- uses: actions/checkout@v1
- with:
- fetch-depth: 0 # unlimited - we need past tags
- submodules: true
- - name: Set up environment
- id: env_setup
- run: |
- DF_VERSION="$(sh ci/get-df-version.sh)"
- echo "df_version=${DF_VERSION}" >> $GITHUB_OUTPUT
- echo "DF_VERSION=${DF_VERSION}" >> $GITHUB_ENV
- echo "DF_FOLDER=${HOME}/DF/${DF_VERSION}/df_linux" >> $GITHUB_ENV
- echo "CCACHE_DIR=${HOME}/.ccache" >> $GITHUB_ENV
- # - name: Fetch DF cache
- # uses: actions/cache@v3
- # with:
- # path: ~/DF
- # key: dfcache-${{ steps.env_setup.outputs.df_version }}-${{ hashFiles('ci/download-df.sh') }}
- - name: Fetch ccache
- uses: actions/cache@v3
- with:
- path: ~/.ccache
- key: ccache-${{ matrix.os }}-gcc-${{ matrix.gcc }}-${{ github.ref_name }}-${{ github.sha }}
- restore-keys: |
- ccache-${{ matrix.os }}-gcc-${{ matrix.gcc }}-${{ github.ref_name }}
- ccache-${{ matrix.os }}-gcc-${{ matrix.gcc }}
- # - name: Download DF
- # run: |
- # sh ci/download-df.sh
- - name: Configure DFHack
- env:
- CC: gcc-${{ matrix.gcc }}
- CXX: g++-${{ matrix.gcc }}
- run: |
- cmake \
- -S . \
- -B build-ci \
- -G Ninja \
- -DDFHACK_BUILD_ARCH=64 \
- -DBUILD_TESTS:BOOL=ON \
- -DBUILD_DEV_PLUGINS:BOOL=${{ matrix.plugins == 'all' }} \
- -DBUILD_SIZECHECK:BOOL=${{ matrix.plugins == 'all' }} \
- -DBUILD_SKELETON:BOOL=${{ matrix.plugins == 'all' }} \
- -DBUILD_STONESENSE:BOOL=${{ matrix.plugins == 'all' }} \
- -DBUILD_SUPPORTED:BOOL=1 \
- -DCMAKE_C_COMPILER_LAUNCHER=ccache \
- -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
- -DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
- - name: Build DFHack
- run: |
- ninja -C build-ci install
- ccache --max-size 50M
- ccache --cleanup
- ccache --show-stats
- - name: Run cpp unit tests
- id: run_tests_cpp
- run: |
- ninja -C build-ci test
- exit $?
- # - name: Run lua tests
- # id: run_tests_lua
- # run: |
- # export TERM=dumb
- # status=0
- # script -qe -c "python ci/run-tests.py --headless --keep-status \"$DF_FOLDER\"" || status=$((status + 1))
- # python ci/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt" || status=$((status + 2))
- # mkdir -p artifacts
- # cp "$DF_FOLDER"/test*.json "$DF_FOLDER"/*.log artifacts || status=$((status + 4))
- # exit $status
- # - name: Upload test artifacts
- # uses: actions/upload-artifact@v1
- # if: (success() || failure()) && steps.run_tests.outcome != 'skipped'
- # continue-on-error: true
- # with:
- # name: test-artifacts-${{ matrix.gcc }}
- # path: artifacts
- - name: Clean up DF folder
- # prevent DFHack-generated files from ending up in the cache
- # (download-df.sh also removes them, this is just to save cache space)
- if: success() || failure()
- run: |
- rm -rf "$DF_FOLDER"
+ test:
+ uses: ./.github/workflows/test.yml
+ with:
+ dfhack_ref: ${{ github.ref }}
+ secrets: inherit
- build-cross-win64:
- name: Build MSVC win64
- runs-on: ubuntu-22.04
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install ccache
- - name: Clone DFHack
- uses: actions/checkout@v3
- with:
- submodules: true
- fetch-depth: 0
- - name: Fetch ccache
- uses: actions/cache@v3
- with:
- path: build/win64-cross/ccache
- key: ccache-win64-cross-msvc-${{ github.ref_name }}-${{ github.sha }}
- restore-keys: |
- ccache-win64-cross-msvc-${{ github.ref_name }}
- ccache-win64-cross-msvc
- - name: Cross-compile win64 artifacts
- env:
- CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1'
- run: |
- cd build
- bash -x build-win64-from-linux.sh
- ccache -d win64-cross/ccache --max-size 200M
- ccache -d win64-cross/ccache --cleanup
- ccache -d win64-cross/ccache --show-stats
- - name: Format artifact name
- id: artifactname
- run: |
- echo name=$(date +%Y%m%d)-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
- - name: Upload win64 artifacts
- uses: actions/upload-artifact@v3
- with:
- name: dfhack-win64-build-${{ steps.artifactname.outputs.name }}
- path: build/win64-cross/output/*
+ package:
+ uses: ./.github/workflows/package.yml
+ with:
+ dfhack_ref: ${{ github.ref }}
+ secrets: inherit
docs:
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python 3
- uses: actions/setup-python@v4
- with:
- python-version: 3
- - name: Install dependencies
- run: |
- pip install 'sphinx'
- - name: Clone DFHack
- uses: actions/checkout@v1
- with:
- submodules: true
- - name: Build docs
- run: |
- sphinx-build -W --keep-going -j auto --color . docs/html
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ dfhack_ref: ${{ github.ref }}
+ platform-files: false
+ common-files: false
+ docs: true
+ secrets: inherit
lint:
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python 3
- uses: actions/setup-python@v4
- with:
- python-version: 3
- - name: Install Lua
- run: |
- sudo apt-get update
- sudo apt-get install lua5.3
- - name: Clone DFHack
- uses: actions/checkout@v1
- with:
- submodules: true
- # don't need tags here
- - name: Check whitespace
- run: |
- python ci/lint.py --git-only --github-actions
- - name: Check Authors.rst
- if: success() || failure()
- run: |
- python ci/authors-rst.py
- - name: Check for missing documentation
- if: success() || failure()
- run: |
- python ci/script-docs.py
- - name: Check Lua syntax
- if: success() || failure()
- run: |
- python ci/script-syntax.py --ext=lua --cmd="luac5.3 -p" --github-actions
+ uses: ./.github/workflows/lint.yml
+ with:
+ dfhack_ref: ${{ github.ref }}
+ secrets: inherit
check-pr:
runs-on: ubuntu-latest
diff --git a/.github/workflows/buildmaster-rebuild.yml b/.github/workflows/buildmaster-rebuild.yml
deleted file mode 100644
index d4c7a70e6..000000000
--- a/.github/workflows/buildmaster-rebuild.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Buildmaster rebuild
-
-on:
- workflow_dispatch:
- inputs:
- pull_request:
- description: Pull request ID
- type: string
- required: true # remove if we support commit rebuilds later
-
-jobs:
- rebuild:
- runs-on: ubuntu-latest
- name: Trigger Buildmaster
- steps:
- - name: Set up Python 3
- uses: actions/setup-python@v4
- with:
- python-version: 3
- - name: Install dependencies
- run: |
- pip install requests
- - name: Clone DFHack
- uses: actions/checkout@v1
- with:
- fetch-depth: 1
- submodules: false
- - name: Run
- env:
- DFHACK_BUILDMASTER_WEBHOOK_URL: ${{ secrets.DFHACK_BUILDMASTER_WEBHOOK_URL }}
- DFHACK_BUILDMASTER_WEBHOOK_SECRET: ${{ secrets.DFHACK_BUILDMASTER_WEBHOOK_SECRET }}
- GITHUB_REPO: ${{ github.repository }}
- GITHUB_TOKEN: ${{ github.token }}
- run: |
- python ci/buildmaster-rebuild-pr.py --pull-request ${{ github.event.inputs.pull_request }}
diff --git a/.github/workflows/clean-cache.yml b/.github/workflows/clean-cache.yml
index d3e12959d..e88e46c01 100644
--- a/.github/workflows/clean-cache.yml
+++ b/.github/workflows/clean-cache.yml
@@ -1,5 +1,7 @@
name: Clean up PR caches
+
on:
+ workflow_call:
pull_request_target:
types:
- closed
@@ -18,7 +20,7 @@ jobs:
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
echo "Fetching list of cache keys"
- cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1)
+ cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1)
set +e
echo "Deleting caches..."
diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml
new file mode 100644
index 000000000..8ce6a3bfa
--- /dev/null
+++ b/.github/workflows/github-release.yml
@@ -0,0 +1,81 @@
+name: Deploy to GitHub
+
+on:
+ push:
+ tags:
+ - '*-r*'
+
+ workflow_dispatch:
+ inputs:
+ ref:
+ description: Tag
+ required: true
+
+jobs:
+ package:
+ uses: ./.github/workflows/package.yml
+ with:
+ dfhack_ref: ${{ github.event.inputs && github.event.inputs.ref || github.event.ref }}
+ append-date-and-hash: false
+ cache-readonly: true
+ launchdf: true
+ secrets: inherit
+
+ create-update-release:
+ name: Draft GitHub release
+ needs: package
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Install doc dependencies
+ run: pip install 'sphinx<4.4.0'
+ - name: Clone DFHack
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.inputs && github.event.inputs.ref || github.event.ref }}
+ submodules: true
+ - name: Get tag
+ id: gettag
+ run: |
+ TAG=$(git describe --tags --abbrev=0 --exact-match)
+ echo name="$TAG" >> $GITHUB_OUTPUT
+ echo type=$(echo "$TAG" | egrep 'r[0-9]+$' && echo "release" || echo "prerelease") >> $GITHUB_OUTPUT
+ - name: Generate release text
+ run: |
+ python docs/gen_changelog.py -a
+ CHANGELOG_FILE=docs/changelogs/${{ steps.gettag.outputs.name }}-github.txt
+ if ! test -f $CHANGELOG_FILE; then CHANGELOG_FILE=docs/changelogs/future-github.txt; fi
+ TOKEN_LINE=$(grep -Fhne '%RELEASE_NOTES%' .github/release_template.md | sed 's/:.*//')
+ head -n $((TOKEN_LINE - 1)) .github/release_template.md > release_body.md
+ CHANGELOG_LINES=$(wc -l <$CHANGELOG_FILE)
+ tail -n $((CHANGELOG_LINES - 4)) $CHANGELOG_FILE >> release_body.md
+ tail -n 1 .github/release_template.md >> release_body.md
+ cat release_body.md
+ - name: Stage release
+ uses: actions/download-artifact@v3
+ - name: Prep artifacts
+ run: |
+ mkdir artifacts
+ cd dfhack-windows64-build
+ tar xjf dfhack-windows64-build.tar.bz2
+ rm dfhack-windows64-build.tar.bz2
+ zip -qr ../artifacts/dfhack-${{ steps.gettag.outputs.name }}-Windows-64bit.zip .
+ cd ../dfhack-linux64-build
+ mv dfhack-linux64-build.tar.bz2 ../artifacts/dfhack-${{ steps.gettag.outputs.name }}-Linux-64bit.tar.bz2
+ - name: Create or update GitHub release
+ uses: ncipollo/release-action@v1
+ with:
+ artifacts: "artifacts/dfhack-*"
+ bodyFile: "release_body.md"
+ allowUpdates: true
+ artifactErrorsFailBuild: true
+ draft: true
+ name: "DFHack ${{ steps.gettag.outputs.name }}"
+ omitBodyDuringUpdate: true
+ omitDraftDuringUpdate: true
+ omitNameDuringUpdate: true
+ omitPrereleaseDuringUpdate: true
+ prerelease: ${{ steps.gettag.outputs.type == 'prerelease' }}
+ replacesArtifacts: true
+ tag: ${{ steps.gettag.outputs.name }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 000000000..77be37f37
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,44 @@
+name: Lint
+
+on:
+ workflow_call:
+ inputs:
+ dfhack_ref:
+ type: string
+ scripts_ref:
+ type: string
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install Lua
+ run: |
+ sudo apt-get update
+ sudo apt-get install lua5.3
+ - name: Clone DFHack
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.dfhack_ref && github.repository || 'DFHack/dfhack' }}
+ ref: ${{ inputs.dfhack_ref }}
+ - name: Get scripts submodule ref
+ if: '!inputs.scripts_ref'
+ id: scriptssubmoduleref
+ run: echo ref=$(git submodule | fgrep scripts | cut -c2-41) >> $GITHUB_OUTPUT
+ - name: Clone scripts
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.scripts_ref && github.repository || 'DFHack/scripts' }}
+ ref: ${{ inputs.scripts_ref || steps.scriptssubmoduleref.outputs.ref }}
+ path: scripts
+ - name: Check whitespace
+ run: python ci/lint.py --git-only --github-actions
+ - name: Check Authors.rst
+ if: always()
+ run: python ci/authors-rst.py
+ - name: Check for missing documentation
+ if: always()
+ run: python ci/script-docs.py
+ - name: Check Lua syntax
+ if: always()
+ run: python ci/script-syntax.py --ext=lua --cmd="luac5.3 -p" --github-actions
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
new file mode 100644
index 000000000..733f567cf
--- /dev/null
+++ b/.github/workflows/package.yml
@@ -0,0 +1,52 @@
+name: Package
+
+on:
+ workflow_call:
+ inputs:
+ dfhack_ref:
+ type: string
+ scripts_ref:
+ type: string
+ structures_ref:
+ type: string
+ append-date-and-hash:
+ type: boolean
+ default: true
+ cache-readonly:
+ type: boolean
+ default: false
+ launchdf:
+ type: boolean
+ default: false
+
+jobs:
+ package-win64:
+ name: Windows
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ dfhack_ref: ${{ inputs.dfhack_ref }}
+ scripts_ref: ${{ inputs.scripts_ref }}
+ structures_ref: ${{ inputs.structures_ref }}
+ artifact-name: dfhack-windows64-build
+ append-date-and-hash: ${{ inputs.append-date-and-hash }}
+ cache-id: release
+ cache-readonly: ${{ inputs.cache-readonly }}
+ stonesense: true
+ docs: true
+ launchdf: ${{ inputs.launchdf }}
+ secrets: inherit
+
+ package-linux:
+ name: Linux
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ dfhack_ref: ${{ inputs.dfhack_ref }}
+ scripts_ref: ${{ inputs.scripts_ref }}
+ structures_ref: ${{ inputs.structures_ref }}
+ artifact-name: dfhack-linux64-build
+ append-date-and-hash: ${{ inputs.append-date-and-hash }}
+ cache-id: release
+ cache-readonly: ${{ inputs.cache-readonly }}
+ stonesense: true
+ docs: true
+ secrets: inherit
diff --git a/.github/workflows/steam-deploy.yml b/.github/workflows/steam-deploy.yml
new file mode 100644
index 000000000..d4c4d9e8a
--- /dev/null
+++ b/.github/workflows/steam-deploy.yml
@@ -0,0 +1,92 @@
+name: Deploy to Steam
+
+on:
+ push:
+ tags:
+ - '*-r*'
+
+ workflow_dispatch:
+ inputs:
+ ref:
+ description: Branch or commit hash
+ type: string
+ required: true
+ default: develop
+ version:
+ description: Version or build description
+ type: string
+ required: true
+ release_channel:
+ description: Steam release channel
+ type: string
+ required: true
+ default: staging
+
+jobs:
+ depot-common:
+ name: Common depot files
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ artifact-name: common-depot
+ dfhack_ref: ${{ github.event.inputs && github.event.inputs.ref || github.event.ref }}
+ platform-files: false
+ docs: true
+ stonesense: true
+ secrets: inherit
+
+ depot-win64:
+ name: Windows depot files
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ artifact-name: win64-depot
+ dfhack_ref: ${{ github.event.inputs && github.event.inputs.ref || github.event.ref }}
+ cache-id: release
+ cache-readonly: true
+ common-files: false
+ stonesense: true
+ launchdf: true
+ secrets: inherit
+
+ depot-linux64:
+ name: Linux depot files
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ artifact-name: linux64-depot
+ dfhack_ref: ${{ github.event.inputs && github.event.inputs.ref || github.event.ref }}
+ cache-id: release
+ cache-readonly: true
+ common-files: false
+ stonesense: true
+ secrets: inherit
+
+ deploy-to-steam:
+ name: Deploy to Steam
+ needs:
+ - depot-common
+ - depot-win64
+ - depot-linux64
+ runs-on: ubuntu-latest
+ concurrency: steam
+ steps:
+ - name: Download depot files
+ uses: actions/download-artifact@v3
+ - name: Stage depot files
+ run: |
+ for name in common win64 linux64; do
+ cd ${name}-depot
+ tar xjf ${name}-depot.tar.bz2
+ rm ${name}-depot.tar.bz2
+ cd ..
+ done
+ - name: Steam deploy
+ uses: game-ci/steam-deploy@v3
+ with:
+ username: ${{ secrets.STEAM_USERNAME }}
+ configVdf: ${{ secrets.STEAM_CONFIG_VDF}}
+ appId: 2346660
+ buildDescription: ${{ github.event.inputs && github.event.inputs.version || github.ref_name }}
+ rootPath: .
+ depot1Path: common-depot
+ depot2Path: win64-depot
+ depot3Path: linux64-depot
+ releaseBranch: ${{ github.event.inputs && github.event.inputs.release_channel || 'staging' }}
diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml
deleted file mode 100644
index e4178e1d0..000000000
--- a/.github/workflows/steam.yml
+++ /dev/null
@@ -1,78 +0,0 @@
-name: Deploy to Steam
-
-on:
- workflow_dispatch:
- inputs:
- ref:
- description: Branch or commit hash
- type: string
- required: true
- default: develop
- version:
- description: Version or build description
- type: string
- required: true
- release_channel:
- description: Release channel
- type: string
- required: true
- default: staging
-
-jobs:
- deploy-to-steam:
- name: Deploy to Steam
- runs-on: ubuntu-22.04
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install ccache
- - name: Clone DFHack
- uses: actions/checkout@v3
- with:
- submodules: true
- fetch-depth: 0
- ref: ${{ github.event.inputs.ref }}
- - name: Get 3rd party SDKs
- uses: actions/checkout@v3
- with:
- repository: DFHack/3rdparty
- ref: main
- ssh-key: ${{ secrets.DFHACK_3RDPARTY_TOKEN }}
- path: depends/steam
- - name: Fetch ccache
- uses: actions/cache@v3
- with:
- path: build/win64-cross/ccache
- key: ccache-win64-cross-msvc-${{ github.sha }}
- restore-keys: |
- ccache-win64-cross-msvc
- - name: Cross-compile win64 artifacts
- env:
- CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1'
- steam_username: ${{ secrets.STEAM_SDK_USERNAME }}
- steam_password: ${{ secrets.STEAM_SDK_PASSWORD }}
- run: |
- echo "ref: ${{ github.event.inputs.ref }}"
- echo "sha: ${{ github.sha }}"
- echo "version: ${{ github.event.inputs.version }}"
- echo "release_channel: ${{ github.event.inputs.release_channel }}"
- echo
- cd build
- bash -x build-win64-from-linux.sh
- ccache -d win64-cross/ccache --max-size 200M
- ccache -d win64-cross/ccache --cleanup
- ccache -d win64-cross/ccache --show-stats
- - name: Steam deploy
- uses: game-ci/steam-deploy@v2
- with:
- username: ${{ secrets.STEAM_USERNAME }}
- password: ${{ secrets.STEAM_PASSWORD }}
- configVdf: ${{ secrets.STEAM_CONFIG_VDF}}
- ssfnFileName: ${{ secrets.STEAM_SSFN_FILE_NAME }}
- ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }}
- appId: 2346660
- buildDescription: ${{ github.event.inputs.version }}
- rootPath: build
- depot1Path: win64-cross/output
- releaseBranch: ${{ github.event.inputs.release_channel }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..6255219e6
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,146 @@
+name: Test
+
+on:
+ workflow_call:
+ inputs:
+ dfhack_ref:
+ type: string
+ scripts_ref:
+ type: string
+ structures_ref:
+ type: string
+
+jobs:
+ build-windows:
+ name: Windows MSVC
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ dfhack_ref: ${{ inputs.dfhack_ref }}
+ scripts_ref: ${{ inputs.scripts_ref }}
+ structures_ref: ${{ inputs.structures_ref }}
+ artifact-name: test-msvc
+ cache-id: test
+ docs: true
+ html: false
+ tests: true
+
+ build-linux:
+ name: Linux gcc-${{ matrix.gcc }}
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ dfhack_ref: ${{ inputs.dfhack_ref }}
+ scripts_ref: ${{ inputs.scripts_ref }}
+ structures_ref: ${{ inputs.structures_ref }}
+ artifact-name: test-gcc-${{ matrix.gcc }}
+ cache-id: test
+ stonesense: ${{ matrix.plugins == 'all' }}
+ extras: ${{ matrix.plugins == 'all' }}
+ docs: true
+ html: false
+ tests: true
+ gcc-ver: ${{ matrix.gcc }}
+ secrets: inherit
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - gcc: 10
+ plugins: "default"
+ - gcc: 12
+ plugins: "all"
+
+ run-tests:
+ name: Test (${{ matrix.os }}, ${{ matrix.compiler }}, ${{ matrix.plugins }} plugins, ${{ matrix.config }} config)
+ needs:
+ - build-windows
+ - build-linux
+ runs-on: ${{ matrix.os }}-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: windows
+ compiler: msvc
+ plugins: "default"
+ config: "default"
+ - os: windows
+ compiler: msvc
+ plugins: "default"
+ config: "empty"
+ - os: ubuntu
+ compiler: gcc-10
+ plugins: "default"
+ config: "default"
+ - os: ubuntu
+ compiler: gcc-12
+ plugins: "all"
+ config: "default"
+ steps:
+ - name: Set env
+ shell: bash
+ run: echo "DF_FOLDER=DF" >> $GITHUB_ENV
+ - name: Install dependencies
+ if: matrix.os == 'ubuntu'
+ run: |
+ sudo apt-get update
+ sudo apt-get install \
+ libsdl2-2.0-0 \
+ libsdl2-image-2.0-0
+ - name: Clone DFHack
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ inputs.dfhack_ref && github.repository || 'DFHack/dfhack' }}
+ ref: ${{ inputs.dfhack_ref }}
+ - name: Detect DF version
+ shell: bash
+ run: echo DF_VERSION="$(sh ci/get-df-version.sh)" >> $GITHUB_ENV
+ - name: Fetch DF cache
+ id: restore-df
+ uses: actions/cache/restore@v3
+ with:
+ path: ${{ env.DF_FOLDER }}
+ key: df-${{ matrix.os }}-${{ env.DF_VERSION }}-${{ hashFiles('ci/download-df.sh') }}
+ - name: Download DF
+ if: steps.restore-df.outputs.cache-hit != 'true'
+ run: sh ci/download-df.sh ${{ env.DF_FOLDER }} ${{ matrix.os }} ${{ env.DF_VERSION }}
+ - name: Save DF cache
+ if: steps.restore-df.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v3
+ with:
+ path: ${{ env.DF_FOLDER }}
+ key: df-${{ matrix.os }}-${{ env.DF_VERSION }}-${{ hashFiles('ci/download-df.sh') }}
+ - name: Install blank DFHack init scripts
+ if: matrix.config == 'empty'
+ shell: bash
+ run: |
+ mkdir -p ${{ env.DF_FOLDER }}/dfhack-config/init
+ cd data/dfhack-config/init
+ for fname in *.init; do touch ../../../${{ env.DF_FOLDER }}/dfhack-config/init/$fname; done
+ - name: Download DFHack
+ uses: actions/download-artifact@v3
+ with:
+ name: test-${{ matrix.compiler }}
+ - name: Install DFHack
+ shell: bash
+ run: tar xjf test-${{ matrix.compiler }}.tar.bz2 -C ${{ env.DF_FOLDER }}
+ - name: Start X server
+ if: matrix.os == 'ubuntu'
+ run: Xvfb :0 -screen 0 1600x1200x24 &
+ - name: Run lua tests
+ timeout-minutes: 10
+ env:
+ DISPLAY: :0
+ TERM: xterm-256color
+ run: python ci/run-tests.py --keep-status "${{ env.DF_FOLDER }}"
+ - name: Check RPC interface
+ run: python ci/check-rpc.py "${{ env.DF_FOLDER }}/dfhack-rpc.txt"
+ - name: Upload test artifacts
+ uses: actions/upload-artifact@v3
+ if: always()
+ continue-on-error: true
+ with:
+ name: test-output-${{ matrix.compiler }}-${{ matrix.plugins }}_plugins-${{ matrix.config }}_config
+ path: |
+ ${{ env.DF_FOLDER }}/dfhack-rpc.txt
+ ${{ env.DF_FOLDER }}/test*.json
+ ${{ env.DF_FOLDER }}/*.log
diff --git a/.gitignore b/.gitignore
index a386b260a..5b4c7f6fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
# linux backup files
*~
-#Kdevelop project files
+# Kdevelop project files
*.kdev4
.kdev4
@@ -70,7 +70,7 @@ tags
# Mac OS X .DS_Store files
.DS_Store
-#VS is annoying about this one.
+# VS is annoying about this one.
/build/win64/DF_PATH.txt
/build/win32/DF_PATH.txt
/.vs
@@ -81,5 +81,6 @@ tags
# external plugins
/plugins/CMakeLists.custom.txt
-# steam api
+# 3rd party downloads
depends/steam
+depends/SDL2
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index efa59812d..45d51dc30 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@ ci:
repos:
# shared across repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.4.0
+ rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -20,11 +20,11 @@ repos:
args: ['--fix=lf']
- id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.23.1
+ rev: 0.27.1
hooks:
- id: check-github-workflows
- repo: https://github.com/Lucas-C/pre-commit-hooks
- rev: v1.5.1
+ rev: v1.5.4
hooks:
- id: forbid-tabs
exclude_types:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1309daa4e..7f61bb111 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,8 +7,8 @@ cmake_policy(SET CMP0074 NEW)
project(dfhack)
# set up versioning.
-set(DF_VERSION "50.08")
-set(DFHACK_RELEASE "r4")
+set(DF_VERSION "50.11")
+set(DFHACK_RELEASE "r2")
set(DFHACK_PRERELEASE FALSE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
@@ -30,14 +30,16 @@ else(CMAKE_CONFIGURATION_TYPES)
endif(CMAKE_CONFIGURATION_TYPES)
option(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF)
+option(BUILD_DOCS_NO_HTML "Don't build the HTML docs, only the in-game docs." OFF)
option(REMOVE_SYMBOLS_FROM_DF_STUBS "Remove debug symbols from DF stubs. (Reduces libdfhack size to about half but removes a few useful symbols)" ON)
macro(CHECK_GCC compiler_path)
execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT)
string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT)
- if(${GCC_VERSION_OUT} VERSION_LESS "4.8")
- message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.8 or later")
- elseif(${GCC_VERSION_OUT} VERSION_GREATER "4.9.9")
+ if(${GCC_VERSION_OUT} VERSION_LESS "10")
+ message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 10 or later")
+ # TODO: this may need to be removed when DF linux actually comes out
+ # TODO: and we can test
# GCC 5 changes ABI name mangling to enable C++11 changes.
# This must be disabled to enable linking against DF.
# http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/
@@ -60,14 +62,14 @@ endif()
if(WIN32)
if(NOT MSVC)
- message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1935 is required.")
- elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1935))
- message(SEND_ERROR "MSVC 2022 version 1930 to 1935 is required, Version Found: ${MSVC_VERSION}")
+ message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1937 is required.")
+ elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1937))
+ message(SEND_ERROR "MSVC 2022 version 1930 to 1937 is required, Version Found: ${MSVC_VERSION}")
endif()
endif()
-# Ask for C++11 standard from compilers
-set(CMAKE_CXX_STANDARD 11)
+# Ask for C++-20 standard from compilers
+set(CMAKE_CXX_STANDARD 20)
# Require the standard support from compilers.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Use only standard c++ to keep code portable
@@ -209,28 +211,23 @@ set(DFHACK_BINARY_DESTINATION .)
set(DFHACK_PLUGIN_DESTINATION ${DFHACK_DATA_DESTINATION}/plugins)
# dfhack lua files go here:
set(DFHACK_LUA_DESTINATION ${DFHACK_DATA_DESTINATION}/lua)
-# the windows .lib file goes here:
-set(DFHACK_DEVLIB_DESTINATION ${DFHACK_DATA_DESTINATION})
# user documentation goes here:
set(DFHACK_USERDOC_DESTINATION ${DFHACK_DATA_DESTINATION})
-# developer documentation goes here:
-set(DFHACK_DEVDOC_DESTINATION ${DFHACK_DATA_DESTINATION})
# some options for the user/developer to play with
option(BUILD_LIBRARY "Build the DFHack library." ON)
option(BUILD_PLUGINS "Build the DFHack plugins." ON)
+option(INSTALL_SCRIPTS "Install DFHack scripts." ON)
+option(INSTALL_DATA_FILES "Install DFHack platform independent files." ON)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
if(UNIX)
## flags for GCC
# default to hidden symbols
# ensure compatibility with older CPUs
- # enable C++11 features
add_definitions(-DLINUX_BUILD)
- add_definitions(-D_GLIBCXX_USE_C99)
- set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror")
- set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g")
+ set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror -Wl,--disable-new-dtags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMMON_FLAGS}")
if(DFHACK_BUILD_64)
@@ -292,22 +289,21 @@ endif()
find_package(ZLIB REQUIRED)
include_directories(${ZLIB_INCLUDE_DIRS})
-if(WIN32)
- # Do the same for SDL.dll
- # (DFHack doesn't require this at build time, so no need to move it to the build folder)
- # TODO: remove SDL.dll from our distribution once DF moves to SDL2. we only
- # continue to include it so we don't break Steam players on update by removing
- # the SDL.dll that DF needs.
- set(SDL_DOWNLOAD_DIR ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH})
- if(${DFHACK_BUILD_ARCH} STREQUAL "64")
- download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-SDL.dll"
- ${SDL_DOWNLOAD_DIR}/SDL.dll
- "1ae242c4b94cb03756a1288122a66faf")
- else()
- download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-SDL.dll"
- ${SDL_DOWNLOAD_DIR}/SDL.dll
- "5a09604daca6b2b5ce049d79af935d6a")
- endif()
+if(BUILD_LIBRARY)
+ # Download SDL release and extract into depends in the build dir
+ # all we need are the header files (including generated headers), so the same release package
+ # will work for all platforms
+ # (the above statement is untested for OSX)
+ set(SDL_VERSION 2.26.2)
+ set(SDL_ZIP_MD5 574daf26d48de753d0b1e19823c9d8bb)
+ set(SDL_ZIP_FILE SDL2-devel-${SDL_VERSION}-VC.zip)
+ set(SDL_ZIP_PATH ${dfhack_SOURCE_DIR}/depends/SDL2/)
+ download_file("https://github.com/libsdl-org/SDL/releases/download/release-${SDL_VERSION}/${SDL_ZIP_FILE}"
+ ${SDL_ZIP_PATH}${SDL_ZIP_FILE}
+ ${SDL_ZIP_MD5})
+ file(ARCHIVE_EXTRACT INPUT ${SDL_ZIP_PATH}${SDL_ZIP_FILE}
+ DESTINATION ${SDL_ZIP_PATH})
+ include_directories(${SDL_ZIP_PATH}/SDL2-${SDL_VERSION}/include)
endif()
if(APPLE)
@@ -391,14 +387,17 @@ include_directories(depends/lodepng)
include_directories(depends/tthread)
include_directories(depends/clsocket/src)
include_directories(depends/xlsxio/include)
-add_subdirectory(depends)
+
+if(BUILD_LIBRARY)
+ add_subdirectory(depends)
+endif()
# Testing with CTest
macro(dfhack_test name files)
-if(UNIX AND NOT APPLE) # remove this once our MSVC build env has been updated
+if(BUILD_LIBRARY AND UNIX AND NOT APPLE) # remove this once our MSVC build env has been updated
add_executable(${name} ${files})
target_include_directories(${name} PUBLIC depends/googletest/googletest/include)
- target_link_libraries(${name} dfhack gtest SDL)
+ target_link_libraries(${name} dfhack gtest)
add_test(NAME ${name} COMMAND ${name})
endif()
endmacro()
@@ -410,22 +409,25 @@ if(NOT GIT_FOUND)
endif()
# build the lib itself
+add_subdirectory(library)
if(BUILD_LIBRARY)
- add_subdirectory(library)
- install(FILES LICENSE.rst DESTINATION ${DFHACK_USERDOC_DESTINATION})
- install(FILES docs/changelog-placeholder.txt DESTINATION ${DFHACK_USERDOC_DESTINATION} RENAME changelog.txt)
+ file(WRITE ${CMAKE_BINARY_DIR}/dfhack_setarch.txt ${DFHACK_SETARCH})
+ install(FILES ${CMAKE_BINARY_DIR}/dfhack_setarch.txt DESTINATION ${DFHACK_DATA_DESTINATION})
endif()
-file(WRITE "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" ${DFHACK_SETARCH})
-install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}")
-
# build the plugins
-if(BUILD_PLUGINS)
- add_subdirectory(plugins)
+add_subdirectory(plugins)
+
+if(INSTALL_DATA_FILES)
+ add_subdirectory(data)
+ install(FILES LICENSE.rst DESTINATION ${DFHACK_USERDOC_DESTINATION})
+ install(FILES docs/changelog-placeholder.txt DESTINATION ${DFHACK_USERDOC_DESTINATION} RENAME changelog.txt)
+ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/depends/luacov/src/luacov/ DESTINATION ${DFHACK_DATA_DESTINATION}/lua/luacov)
endif()
-add_subdirectory(data)
-add_subdirectory(scripts)
+if(INSTALL_SCRIPTS)
+ add_subdirectory(scripts)
+endif()
if(BUILD_DOCS)
find_package(Python3)
@@ -466,7 +468,14 @@ if(BUILD_DOCS)
"${CMAKE_CURRENT_SOURCE_DIR}/conf.py"
)
- set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/html/.buildinfo")
+ if(BUILD_DOCS_NO_HTML)
+ set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/text/index.txt")
+ set(SPHINX_BUILD_TARGETS text)
+ else()
+ set(SPHINX_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/docs/html/.buildinfo")
+ set(SPHINX_BUILD_TARGETS html text)
+ endif()
+
set_property(
DIRECTORY PROPERTY ADDITIONAL_CLEAN_FILES TRUE
"${CMAKE_CURRENT_SOURCE_DIR}/docs/changelogs"
@@ -483,9 +492,10 @@ if(BUILD_DOCS)
"${CMAKE_BINARY_DIR}/docs/text"
"${CMAKE_BINARY_DIR}/docs/xml"
)
+
add_custom_command(OUTPUT ${SPHINX_OUTPUT}
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py"
- html text --sphinx="${SPHINX_EXECUTABLE}" -- -q
+ ${SPHINX_BUILD_TARGETS} --sphinx="${SPHINX_EXECUTABLE}" -- -q -W
DEPENDS ${SPHINX_DEPS}
COMMENT "Building documentation with Sphinx"
)
@@ -498,10 +508,12 @@ if(BUILD_DOCS)
add_custom_command(TARGET dfhack_docs POST_BUILD
COMMAND ${CMAKE_COMMAND} -E touch ${SPHINX_OUTPUT})
- install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/
- DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs
- FILES_MATCHING PATTERN "*"
- PATTERN html/_sources EXCLUDE)
+ if(NOT BUILD_DOCS_NO_HTML)
+ install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/
+ DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs
+ FILES_MATCHING PATTERN "*"
+ PATTERN html/_sources EXCLUDE)
+ endif()
install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/
DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs)
install(FILES docs/changelogs/news.rst docs/changelogs/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION})
@@ -586,7 +598,7 @@ endif()
set(DFHACK_BUILD_ARCH_PREV "${DFHACK_BUILD_ARCH}" CACHE STRING "Previous build architecture" FORCE)
option(BUILD_SIZECHECK "Build the sizecheck library, for research" OFF)
-if(BUILD_SIZECHECK)
+if(BUILD_LIBRARY AND BUILD_SIZECHECK)
add_subdirectory(depends/sizecheck)
add_dependencies(dfhack sizecheck)
endif()
diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh
index c0559a999..9404188fd 100755
--- a/build/build-win64-from-linux.sh
+++ b/build/build-win64-from-linux.sh
@@ -45,7 +45,7 @@ if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/b
-e steam_password \
--name dfhack-win \
ghcr.io/dfhack/build-env:msvc \
- bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 $CMAKE_EXTRA_ARGS && dfhack-make -j$jobs install" \
+ bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output -DBUILD_DOCS=1 $CMAKE_EXTRA_ARGS && dfhack-make -j$jobs install" \
; then
echo
echo "Build failed"
diff --git a/ci/buildmaster-rebuild-pr.py b/ci/buildmaster-rebuild-pr.py
deleted file mode 100755
index 7ce988683..000000000
--- a/ci/buildmaster-rebuild-pr.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import hashlib
-import hmac
-import json
-import logging
-import os
-import uuid
-
-import requests
-
-logging.basicConfig(level=logging.DEBUG)
-
-
-def get_required_env(name):
- value = os.environ.get(name)
- if not value:
- raise ValueError(f'Expected environment variable {name!r} to be non-empty')
- return value
-
-
-def make_sig(body, secret, algorithm):
- return hmac.new(secret.encode(), body.encode(), algorithm).hexdigest()
-
-
-webhook_url = get_required_env('DFHACK_BUILDMASTER_WEBHOOK_URL')
-secret = get_required_env('DFHACK_BUILDMASTER_WEBHOOK_SECRET')
-api_token = get_required_env('GITHUB_TOKEN')
-repo = get_required_env('GITHUB_REPO')
-
-
-parser = argparse.ArgumentParser()
-target_group = parser.add_mutually_exclusive_group(required=True)
-target_group.add_argument('--pull-request', type=int, help='Pull request to rebuild')
-args = parser.parse_args()
-
-
-response = requests.get(
- f'https://api.github.com/repos/{repo}/pulls/{args.pull_request}',
- headers={
- 'Authorization': f'Bearer {api_token}',
- },
-)
-response.raise_for_status()
-pull_request_data = response.json()
-
-
-body = json.dumps({
- 'action': 'synchronize',
- 'number': args.pull_request,
- 'pull_request': pull_request_data,
-})
-
-response = requests.post(
- 'https://lubar-webhook-proxy.appspot.com/github/buildmaster',
- headers={
- 'Content-Type': 'application/json',
- 'User-Agent': 'GitHub-Hookshot/' + requests.utils.default_user_agent(),
- 'X-GitHub-Delivery': 'dfhack-rebuild-' + str(uuid.uuid4()),
- 'X-GitHub-Event': 'pull_request',
- 'X-Hub-Signature': 'sha1=' + make_sig(body, secret, hashlib.sha1),
- 'X-Hub-Signature-256': 'sha256=' + make_sig(body, secret, hashlib.sha256),
- },
- data=body,
- timeout=15,
-)
-
-print(response)
-print(str(response.text))
-response.raise_for_status()
diff --git a/ci/check-rpc.py b/ci/check-rpc.py
index aba3e3811..be7d07986 100755
--- a/ci/check-rpc.py
+++ b/ci/check-rpc.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
import glob
+import itertools
import sys
actual = {'': {}}
+SEP = ('=' * 80)
with open(sys.argv[1]) as f:
plugin_name = ''
@@ -26,7 +28,7 @@ for p in glob.iglob('library/proto/*.proto'):
parts = line.split(' ')
expected[''][parts[2]] = (parts[4], parts[6])
-for p in glob.iglob('plugins/proto/*.proto'):
+for p in itertools.chain(glob.iglob('plugins/proto/*.proto'), glob.iglob('plugins/*/proto/*.proto')):
plugin_name = ''
with open(p) as f:
for line in f:
@@ -53,6 +55,7 @@ for plugin_name in actual:
methods = actual[plugin_name]
if plugin_name not in expected:
+ print(SEP)
print('Missing documentation for plugin proto files: ' + plugin_name)
print('Add the following lines:')
print('// Plugin: ' + plugin_name)
@@ -73,12 +76,14 @@ for plugin_name in actual:
missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
+ print(SEP)
print('Incomplete documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Add the following lines:')
for m in missing:
print(m)
error_count += 1
if len(wrong) > 0:
+ print(SEP)
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Replace the following comments:')
for m in wrong:
print(m)
@@ -88,6 +93,7 @@ for plugin_name in expected:
methods = expected[plugin_name]
if plugin_name not in actual:
+ print(SEP)
print('Incorrect documentation for plugin proto files: ' + plugin_name)
print('The following methods are documented, but the plugin does not provide any RPC methods:')
for m in methods:
@@ -102,6 +108,7 @@ for plugin_name in expected:
missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
+ print(SEP)
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Remove the following lines:')
for m in missing:
print(m)
diff --git a/ci/download-df.sh b/ci/download-df.sh
index 8d94d54dd..399b75714 100755
--- a/ci/download-df.sh
+++ b/ci/download-df.sh
@@ -1,52 +1,53 @@
#!/bin/sh
+DF_FOLDER=$1
+OS_TARGET=$2
+DF_VERSION=$3
+
set -e
-df_tardest="df.tar.bz2"
-save_tardest="test_save.tgz"
-
-cd "$(dirname "$0")"
-echo "DF_VERSION: $DF_VERSION"
-echo "DF_FOLDER: $DF_FOLDER"
-mkdir -p "$DF_FOLDER"
-# back out of df_linux
-cd "$DF_FOLDER/.."
-
-if ! test -f "$df_tardest"; then
- minor=$(echo "$DF_VERSION" | cut -d. -f2)
- patch=$(echo "$DF_VERSION" | cut -d. -f3)
- echo "Downloading DF $DF_VERSION"
- while read url; do
- echo "Attempting download: ${url}"
- if wget -v "$url" -O "$df_tardest"; then
- break
- fi
- done <] []
If a done_command is specified, it will be run after the tests complete.
-Options:
-
- -h, --help display this help message and exit.
- -d, --test_dir specifies which directory to look in for tests. defaults to
- the "hack/scripts/test" folder in your DF installation.
- -m, --modes only run tests in the given comma separated list of modes.
- see the next section for a list of valid modes. if not
- specified, the tests are not filtered by modes.
- -r, --resume skip tests that have already been run. remove the
- test_status.json file to reset the record.
- -s, --save_dir the save folder to load for "fortress" mode tests. this
- save is only loaded if a fort is not already loaded when
- a "fortress" mode test is run. if not specified, defaults to
- 'region1'.
- -t, --tests only run tests that match one of the comma separated list of
- patterns. if not specified, no tests are filtered.
-
-Modes:
-
- none the test can be run on any screen
- title the test must be run on the DF title screen. note that if the game
- has a map loaded, "title" mode tests cannot be run
- fortress the test must be run while a map is loaded. if the game is
- currently on the title screen, the save specified by the save_dir
- parameter will be loaded.
-
-Examples:
-
- test runs all tests
- test -r runs all tests that haven't been run before
- test -m none runs tests that don't need the game to be in a
- specific mode
- test -t quickfort runs quickfort tests
- test -d /path/to/dfhack-scripts/repo/test
- runs tests in your dev scripts repo
+Options
+-------
+
+-d, --test_dir specifies which directory to look in for tests. defaults to
+ the "hack/scripts/test" folder in your DF installation.
+-m, --modes only run tests in the given comma separated list of modes.
+ see the next section for a list of valid modes. if not
+ specified, the tests are not filtered by modes.
+-r, --resume skip tests that have already been run. remove the
+ test_status.json file to reset the record.
+-s, --save_dir the save folder to load for "fortress" mode tests. this
+ save is only loaded if a fort is not already loaded when
+ a "fortress" mode test is run. if not specified, defaults to
+ 'region1'.
+-t, --tests only run tests that match one of the comma separated list of
+ patterns. if not specified, no tests are filtered and all tessts
+ are run.
+
+Modes
+-----
+
+none the test can be run on any screen
+title the test must be run on the DF title screen. note that if the game
+ has a map loaded, "title" mode tests cannot be run
+fortress the test must be run while a map is loaded. if the game is
+ currently on the title screen, the save specified by the save_dir
+ parameter will be loaded.
+
+Examples
+--------
+
+test runs all tests
+test -r runs all tests that haven't been run before
+test -m none runs tests that don't need the game to be in a
+ specific mode
+test -t quickfort runs quickfort tests
+test -d /path/to/dfhack-scripts/repo/test
+ runs tests in your dev scripts repo
Default values for the options may be set in a file named test_config.json in
your DF folder. Options with comma-separated values should be written as json
@@ -140,18 +152,37 @@ end
test_envvars.require = clean_require
test_envvars.reqscript = clean_reqscript
-local function is_title_screen(scr)
- scr = scr or dfhack.gui.getCurViewscreen()
- return df.viewscreen_titlest:is_instance(scr)
+local function is_title_screen()
+ return dfhack.gui.matchFocusString('title/Default')
+end
+
+local function wait_for(ms, desc, predicate)
+ local start_ms = dfhack.getTickCount()
+ local prev_ms = start_ms
+ while not predicate() do
+ delay(10)
+ local now_ms = dfhack.getTickCount()
+ if now_ms - start_ms > ms then
+ qerror(('%s took too long (timed out at %s)'):format(
+ desc, dfhack.gui.getCurFocus(true)[1]))
+ end
+ if now_ms - prev_ms > 1000 then
+ print(('Waiting for %s...'):format(desc))
+ prev_ms = now_ms
+ end
+ end
end
-- This only handles pre-fortress-load screens. It will time out if the player
-- has already loaded a fortress or is in any screen that can't get to the title
-- screen by sending ESC keys.
local function ensure_title_screen()
+ if df.viewscreen_dwarfmodest:is_instance(dfhack.gui.getDFViewscreen(true)) then
+ qerror('Cannot reach title screen from loaded fort')
+ end
for i = 1, 100 do
local scr = dfhack.gui.getCurViewscreen()
- if is_title_screen(scr) then
+ if is_title_screen() then
print('Found title screen')
return
end
@@ -160,57 +191,94 @@ local function ensure_title_screen()
if i % 10 == 0 then print('Looking for title screen...') end
end
qerror(string.format('Could not find title screen (timed out at %s)',
- dfhack.gui.getCurFocus(true)))
+ dfhack.gui.getCurFocus(true)[1]))
end
-local function is_fortress(focus_string)
- focus_string = focus_string or dfhack.gui.getCurFocus(true)
- return focus_string == 'dwarfmode/Default'
+local function is_fortress()
+ return dfhack.gui.matchFocusString('dwarfmode/Default')
+end
+
+-- error out if we're not running in a CI environment
+-- the tests may corrupt saves, and we don't want to unexpectedly ruin a real player save
+-- this heuristic is not perfect, but it should be able to detect most cases
+local function ensure_ci_save(scr)
+ if #scr.savegame_header ~= 1
+ or #scr.savegame_header_world ~= 1
+ or not string.find(scr.savegame_header[0].fort_name, 'Dream')
+ then
+ qerror('Unexpected test save in slot 0; please manually load a fort for ' ..
+ 'running fortress mode tests. note that tests may alter or corrupt the ' ..
+ 'fort! Do not save after running tests.')
+ end
+end
+
+local function click_top_title_button(scr)
+ local sw, sh = dfhack.screen.getWindowSize()
+ df.global.gps.mouse_x = sw // 2
+ df.global.gps.precise_mouse_x = df.global.gps.mouse_x * df.global.gps.tile_pixel_x
+ if sh < 60 then
+ df.global.gps.mouse_y = 25
+ else
+ df.global.gps.mouse_y = (sh // 2) + 3
+ end
+ df.global.gps.precise_mouse_y = df.global.gps.mouse_y * df.global.gps.tile_pixel_y
+ gui.simulateInput(scr, '_MOUSE_L')
+end
+
+local function load_first_save(scr)
+ if #scr.savegame_header == 0 then
+ qerror('no savegames available to load')
+ end
+
+ click_top_title_button(scr)
+ wait_for(1000, 'world list', function()
+ return scr.mode == 2
+ end)
+ click_top_title_button(scr)
+ wait_for(1000, 'savegame list', function()
+ return scr.mode == 3
+ end)
+ click_top_title_button(scr)
+ wait_for(1000, 'loadgame progress bar', function()
+ return dfhack.gui.matchFocusString('loadgame')
+ end)
end
-- Requires that a fortress game is already loaded or is ready to be loaded via
--- the "Continue Playing" option in the title screen. Otherwise the function
+-- the "Continue active game" option in the title screen. Otherwise the function
-- will time out and/or exit with error.
local function ensure_fortress(config)
- local focus_string = dfhack.gui.getCurFocus(true)
for screen_timeout = 1,10 do
- if is_fortress(focus_string) then
- print('Loaded fortress map')
+ if is_fortress() then
+ print('Fortress map is loaded')
-- pause the game (if it's not already paused)
dfhack.gui.resetDwarfmodeView(true)
return
end
- local scr = dfhack.gui.getCurViewscreen(true)
- if focus_string == 'title' or
- focus_string == 'dfhack/lua/load_screen' then
+ local scr = dfhack.gui.getCurViewscreen()
+ if dfhack.gui.matchFocusString('title/Default', scr) then
+ print('Attempting to load the test fortress')
+ -- TODO: reinstate loading of a specified save dir; for now
+ -- just load the first possible save, which will at least let us
+ -- run fortress tests in CI
-- qerror()'s on falure
- dfhack.run_script('load-save', config.save_dir)
- elseif focus_string ~= 'loadgame' then
+ -- dfhack.run_script('load-save', config.save_dir)
+ ensure_ci_save(scr)
+ load_first_save(scr)
+ elseif not dfhack.gui.matchFocusString('loadgame', scr) then
-- if we're not actively loading a game, hope we're in
-- a screen where hitting ESC will get us to the game map
-- or the title screen
scr:feed_key(df.interface_key.LEAVESCREEN)
end
-- wait for current screen to change
- local prev_focus_string = focus_string
- for frame_timeout = 1,100 do
- delay(10)
- focus_string = dfhack.gui.getCurFocus(true)
- if focus_string ~= prev_focus_string then
- goto next_screen
- end
- if frame_timeout % 10 == 0 then
- print(string.format(
- 'Loading fortress (currently at screen: %s)',
- focus_string))
- end
- end
- print('Timed out waiting for screen to change')
- break
- ::next_screen::
+ local prev_focus_string = dfhack.gui.getCurFocus()[1]
+ wait_for(60000, 'screen change', function()
+ return dfhack.gui.getCurFocus()[1] ~= prev_focus_string
+ end)
end
qerror(string.format('Could not load fortress (timed out at %s)',
- focus_string))
+ table.concat(dfhack.gui.getCurFocus(), ' ')))
end
local MODES = {
@@ -236,6 +304,25 @@ local function load_test_config(config_file)
return config
end
+local function TestTable()
+ local inner = utils.OrderedTable()
+ local meta = copyall(getmetatable(inner))
+
+ function meta:__newindex(k, v)
+ if inner[k] then
+ error('Attempt to overwrite existing test: ' .. k)
+ elseif type(v) ~= 'function' then
+ error('Attempt to define test as non-function: ' .. k .. ' = ' .. tostring(v))
+ else
+ inner[k] = v
+ end
+ end
+
+ local self = {}
+ setmetatable(self, meta)
+ return self
+end
+
-- we have to save and use the original dfhack.printerr here so our test harness
-- output doesn't trigger its own dfhack.printerr usage detection (see
-- detect_printerr below)
@@ -280,7 +367,7 @@ end
local function build_test_env(path)
local env = {
- test = utils.OrderedTable(),
+ test = TestTable(),
-- config values can be overridden in the test file to define
-- requirements for the tests in that file
config = {
@@ -352,33 +439,46 @@ local function load_tests(file, tests)
if not code then
dfhack.printerr('Failed to load file: ' .. tostring(err))
return false
- else
- dfhack.internal.IN_TEST = true
- local ok, err = dfhack.pcall(code)
- dfhack.internal.IN_TEST = false
- if not ok then
- dfhack.printerr('Error when running file: ' .. tostring(err))
- return false
- else
- if not MODES[env.config.mode] then
- dfhack.printerr('Invalid config.mode: ' .. tostring(env.config.mode))
- return false
- end
- for name, test_func in pairs(env.test) do
- if env.config.wrapper then
- local fn = test_func
- test_func = function() env.config.wrapper(fn) end
- end
- local test_data = {
- full_name = short_filename .. ':' .. name,
- func = test_func,
- private = env_private,
- config = env.config,
- }
- test_data.name = test_data.full_name:gsub('test/', ''):gsub('.lua', '')
- table.insert(tests, test_data)
- end
+ end
+ dfhack.internal.IN_TEST = true
+ local ok, err = dfhack.pcall(code)
+ dfhack.internal.IN_TEST = false
+ if not ok then
+ dfhack.printerr('Error when running file: ' .. tostring(err))
+ return false
+ end
+ if not MODES[env.config.mode] then
+ dfhack.printerr('Invalid config.mode: ' .. tostring(env.config.mode))
+ return false
+ end
+ if not env.config.target then
+ dfhack.printerr('Skipping tests for unspecified target in ' .. file)
+ return true -- TODO: change to false once existing tests have targets specified
+ end
+ local targets = type(env.config.target) == 'table' and env.config.target or {env.config.target}
+ for _,target in ipairs(targets) do
+ if target == 'core' then goto continue end
+ if type(target) ~= 'string' or not helpdb.is_entry(target) or
+ helpdb.get_entry_tags(target).unavailable
+ then
+ dfhack.printerr('Skipping tests for unavailable target: ' .. target)
+ return true
+ end
+ ::continue::
+ end
+ for name, test_func in pairs(env.test) do
+ if env.config.wrapper then
+ local fn = test_func
+ test_func = function() env.config.wrapper(fn) end
end
+ local test_data = {
+ full_name = short_filename .. ':' .. name,
+ func = test_func,
+ private = env_private,
+ config = env.config,
+ }
+ test_data.name = test_data.full_name:gsub('test/', ''):gsub('.lua', '')
+ table.insert(tests, test_data)
end
return true
end
@@ -520,6 +620,10 @@ local function filter_tests(tests, config)
end
local function run_tests(tests, status, counts, config)
+ wait_for(60000, 'game load', function()
+ local scr = dfhack.gui.getDFViewscreen()
+ return not df.viewscreen_initial_prepst:is_instance(scr)
+ end)
print(('Running %d tests'):format(#tests))
local start_ms = dfhack.getTickCount()
local num_skipped = 0
@@ -529,6 +633,7 @@ local function run_tests(tests, status, counts, config)
goto skip
end
if not MODES[test.config.mode].detect() then
+ print(('Switching to %s mode for test: %s'):format(test.config.mode, test.name))
local ok, err = pcall(MODES[test.config.mode].navigate, config)
if not ok then
MODES[test.config.mode].failed = true
@@ -537,12 +642,13 @@ local function run_tests(tests, status, counts, config)
goto skip
end
end
+ -- pre-emptively mark the test as failed in case we induce a crash
+ status[test.full_name] = TestStatus.FAILED
+ save_test_status(status)
if run_test(test, status, counts) then
status[test.full_name] = TestStatus.PASSED
- else
- status[test.full_name] = TestStatus.FAILED
+ save_test_status(status)
end
- save_test_status(status)
::skip::
end
local elapsed_ms = dfhack.getTickCount() - start_ms
@@ -575,7 +681,7 @@ local function dump_df_state()
enabler = {
fps = df.global.enabler.fps,
gfps = df.global.enabler.gfps,
- fullscreen = df.global.enabler.fullscreen,
+ fullscreen_state = df.global.enabler.fullscreen_state.whole,
},
gps = {
dimx = df.global.gps.dimx,
diff --git a/ci/update-submodules.manifest b/ci/update-submodules.manifest
index e97cae6f3..0c8f03e81 100644
--- a/ci/update-submodules.manifest
+++ b/ci/update-submodules.manifest
@@ -2,6 +2,7 @@ library/xml master
scripts master
plugins/stonesense master
plugins/isoworld dfhack
+depends/clsocket master
depends/libzip dfhack
depends/libexpat dfhack
depends/xlsxio dfhack
diff --git a/conf.py b/conf.py
index 60c3be579..ffcc24b75 100644
--- a/conf.py
+++ b/conf.py
@@ -78,8 +78,7 @@ def write_tool_docs():
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:
- if k[0] != 'search':
- outfile.write(label)
+ outfile.write(label)
outfile.write(include)
diff --git a/data/art/dfhack.png b/data/art/logo.png
similarity index 100%
rename from data/art/dfhack.png
rename to data/art/logo.png
diff --git a/data/art/logo_hovered.png b/data/art/logo_hovered.png
new file mode 100644
index 000000000..fe82cc1ae
Binary files /dev/null and b/data/art/logo_hovered.png differ
diff --git a/data/art/pathable.png b/data/art/pathable.png
new file mode 100644
index 000000000..00f7f831d
Binary files /dev/null and b/data/art/pathable.png differ
diff --git a/data/art/unsuspend.png b/data/art/unsuspend.png
new file mode 100644
index 000000000..f1d1d33da
Binary files /dev/null and b/data/art/unsuspend.png differ
diff --git a/data/blueprints/dreamfort.csv b/data/blueprints/dreamfort.csv
index 93cb4e3f7..22b78230a 100644
--- a/data/blueprints/dreamfort.csv
+++ b/data/blueprints/dreamfort.csv
@@ -20,7 +20,7 @@
"Dreamfort blueprints take care of everything to get the fort up and running. You don't need to clear any extra trees or create any extra buildings or stockpiles (though of course you are free to do so). Blueprints that do require manual steps, like 'assign minecart to hauling route', will leave a message telling you so when you run them. Note that blueprints will designate buildings to build even if you don't have the materials needed to build them. You can use the ""o"" hotkey to automatically create the manager orders for all the needed items when you have a blueprint loaded in gui/quickfort. Make sure your manager is available to validate all the incoming work orders!"
""
"There are some tasks common to all forts that Dreamfort doesn't specifically handle for you. For example, Dreamfort sets up a barracks, but managing squads is up to you. Here are some other common tasks that may need to be done manually (or with some other tool):"
-- Exploratory mining for specific resources like iron
+- Exploratory mining for specific resources like iron (see gui/design for help with this)
"- Filling the well system with water (if you have a light aquifer, see library/aquifer_tap.csv for help with this)"
- Bringing magma up to the industry level to power magma forges/furnaces (see library/pump_stack.csv for help with this)
- Manufacturing trade goods
@@ -42,7 +42,7 @@ interactively."
"# The dreamfort.csv distributed with DFHack is generated with the following command:
for fname in dreamfort*.xlsx; do xlsx2csv -a -p '' ""$fname""; done | sed 's/,*$//'"
#notes label(checklist) command checklist
-"Here is the recommended order for Dreamfort commands. Each line is a blueprint that you run with gui/quickfort (default keybinding: Ctrl-Shift-Q), except where we use other tools as noted. If you set ""dreamfort"" as the filter when you open gui/quickfort, you'll conveniently only see Dreamfort blueprints to choose from. See the walkthroughs (the ""help"" blueprints) for context and details."
+"Here is the recommended order for Dreamfort commands. Each line is a blueprint that you run with gui/quickfort (default keybinding: Ctrl-Shift-Q), except where we use other tools as noted. If you set ""dreamfort"" as the filter when you open gui/quickfort, you'll conveniently only see Dreamfort blueprints to choose from. See the walkthroughs (the ""help"" blueprints) for context and details. You can also copy text from this spreadsheet online and paste it in gui/launcher with Ctrl-V."
"If the checklist indicates that you should generate orders, that means to hit the ""o"" hotkey when the blueprint is loaded in gui/quickfort. You'll get a popup saying which orders were generated. Also remember to read the messages the blueprints display after you run them so you don't miss any important manual steps!"
""
-- Preparation (before you embark!) --
@@ -61,7 +61,7 @@ gui/quickfort,/perimeter,,Run at embark. Don't actually apply the blueprint -- i
-- Dig --
DFHack command,Blueprint,Generate orders,Notes
gui/quickfort,/surface1,,Clear some trees and dig central staircase. Run when you find your center tile. Deconstruct your wagon if it is in the way.
-gui/quickfort,/dig_all,,"Run when you find a suitable (non-aquifer) rock layer for the industry level. It designates digging for industry, services, guildhall, suites, apartments, and the crypt all in one go. This list does not include the farming level, which we'll dig in the uppermost soil layer a bit later. Note that it is more efficient for your miners if you designate your digging before they dig the central stairs past that level since the stairs are dug at a low priority. This keeps your miners focused on one level at a time. If you need to designate your levels individually due to caverns interrupting the sequence or just because it is your preference, run the level-specific dig blueprints (i.e. /industry1, /services1, /guildhall1, /suites1, 3 levels of /apartments1, and /crypt1) instead of running /dig_all."
+gui/quickfort,/dig_all,,"Run when you find a suitable (non-aquifer) rock layer for the industry level. It designates digging for industry, services, guildhall, suites, apartments, and the crypt all in one go. This list does not include the farming level, which we'll designate in the uppermost soil layer once the surface miasma channels are dug. Note that it is more efficient for your miners if you designate the digging for a level before they dig the central stairs past that level. The stairs down on each level are designated at priority 5 instead of the regular priority 4. This lets the miners focus on one z-level at a time and not run up and down the stairs attempting to dig out two blueprints simultaneously. If you need to designate your levels individually due to caverns interrupting the sequence or just because it is your preference, run the level-specific dig blueprints (i.e. /industry1, /services1, /guildhall1, /suites1, 3 levels of /apartments1, and /crypt1) instead of running /dig_all."
""
-- Core fort (should finish at about the third migration wave) --
DFHack command,Blueprint,Generate orders,Notes
@@ -70,7 +70,7 @@ gui/quickfort,/farming1,,Dig out the farming level. Run when channels on the sur
gui/quickfort,/farming2,,Build farming level. Run as soon as the farming level has been completely dug out.
gui/quickfort,/surface3,,Cover the miasma vents and start protecting the central staircase from early invasions. Run right after /farming2.
gui/quickfort,/industry2,,Build industry level. Run as soon as the industry level has been completely dug out.
-prioritize ConstructBuilding,,,"To get those workshops up and running ASAP. You may have to run this several times as the materials for the building construction jobs become ready. As industry workshops are built, you can remove the temporary workshops and stockpiles on the surface. **Be sure that there are no items attached to jobs left in the workshops before deconstructing them, otherwise you'll get canceled jobs!**"
+prioritize ConstructBuilding,,,"To get those workshops up and running ASAP. You may have to run this several times as the materials for the building construction jobs become ready. As industry workshops are built, you can remove the temporary workshops and stockpiles on the surface. Be sure that there are no items attached to jobs left in the surface workshops before deconstructing them, otherwise you'll get canceled jobs!"
gui/quickfort,/surface4,,Finish protecting the staircase and lay flooring for future buildings. Run after the walls and floors around the staircase are built and you have moved production from the surface to the industry level.
gui/quickfort,/industry3,,Build the rest of the industry level. Run once /surface4 is mostly complete.
orders import library/basic,,,"Run after the first migration wave, so you have dwarves to do all the basic tasks. Note that this is the ""orders"" plugin, not the ""quickfort orders"" command."
@@ -81,7 +81,7 @@ gui/quickfort,/surface6,Yes,Build security perimeter. Run once you have linked a
gui/quickfort,/surface7,Yes,Build roof. Run after the surface walls are completed and any marked trees are chopped down. Be sure to give your haulers some time to fill the stonecutter's stockpile with some stone first so your stonecutters won't be hauling it up from the depths by hand.
""
-- Plumbing --
-"If you haven't done it already, this is a good time to fill your well cisterns, either with a bucket brigade or by routing water from a freshwater stream or an aquifer (see the library/aquifer_tap.csv blueprint for help with this)."
+"If you haven't done it already, this is a good time to fill your well cisterns, either with a bucket brigade or by routing water from a freshwater stream or an aquifer (see the aquifer_tap library blueprint for help with this)."
Also consider bringing magma up to your services level so you can replace the forge and furnaces on your industry level with more powerful magma versions. This is especially important if your embark has insufficient trees to convert into charcoal. Keep in mind that moving magma is a tricky process and can take a long time. Don't forget to continue making progress through this checklist!
""
-- Mature fort (fourth migration wave onward) --
@@ -112,7 +112,7 @@ You can save some time by setting up your settings in gui/control-panel before y
""
"On the gui/control-panel ""Autostart"" tab, additionally enable:"
- autobutcher
-- autobutcher target 50 50 14 2 BIRD_GOOSE
+- autobutcher target 10 10 14 2 BIRD_GOOSE
- autochop
- autofarm
- autofish
@@ -121,6 +121,7 @@ You can save some time by setting up your settings in gui/control-panel before y
- ban-cooking all
- buildingplan set boulders false
- buildingplan set logs false
+- dwarfvet
- nestboxes
- prioritize
- seedwatch
@@ -144,9 +145,6 @@ On the work details screen (Labor -> Work details)
In standing orders (Labor -> Standing orders):
" - Change ""Automatically weave all thread"" to ""No automatic weaving"" so the hospital always has thread -- we'll be managing cloth production with automated orders"
"- On the ""Other"" tab, change ""Everybody harvests"" to ""Only farmers harvest"""
-""
-"Finally, in the burrows screen:"
-"- Create a burrow named ""Inside"" and register it as a civilian alert burrow in gui/civ-alert so you can use it to get your civilians to safety during sieges. You will have to periodically expand the burrow area as the fort expands."
"#meta label(dig_all) start(central stairs on industry level) dig industry, services, guildhall, suites, apartments, and crypt levels. does not include farming."
# Note that this blueprint will only work for the unified dreamfort.csv. It won't work for the individual .xlsx files (since #meta blueprints can't cross file boundaries).
""
@@ -248,6 +246,8 @@ Features:
- Optional extended trap hallways (to handle larger sieges with a smaller/no military)
"- Protected trade depot, with separate trade goods stockpiles for organics and inorganics (for easy elven trading)"
- A grid of small farm plots for lucrative surface farming
+"- A burrow named ""Inside+"" that grows with your fort as you dig it out. It is pre-registered as a civilian alert burrow so you can use it to get your civilians to safety during sieges."
+"- A burrow named ""Clearcutting area"" that is automatically registered with autochop (if you have it enabled) to keep the area around your fort clear of trees"
""
Manual steps you have to take:
"- Assign grazing livestock to the large pasture, dogs to the pasture over the central stairs, and male birds to the zone between the rows of nestboxes (DFHack's autonestbox will auto-assign the female egg-laying birds to the nestbox zones)"
@@ -280,11 +280,11 @@ Surface Walkthrough:
Sieges and Prisoner Processing:
Here are some tips and procedures for handling seiges -- including how to clean up afterwards!
""
-"- Ensure your ""Inside"" burrow includes only below-ground areas and safe surface areas of your fort. In particular, don't include the ""atrium"" area (where the ""siege bait"" pasture is) or the trapped hallways."
+"- Your ""Inside+"" burrow will automatically grow with your fort and should include only safe areas of your fort. In particular, it should not include the ""atrium"" area (where the ""siege bait"" pasture is) or the trapped hallways."
""
-"- When a siege begins, set your civilian alert (attach the alert to your ""Inside"" burrow if it isn't already) to ensure all your civilians stay out of danger. Immediately pull the lever to close the outer main gate. It is also wise to close the trade depot and inner main gate as well. That way, if enemies get past the traps, they'll have to go through the soldiers in your barracks (assuming you have a military)."
+"- When a siege begins, set your civilian alert (attach the alert to your ""Inside+"" burrow if it isn't already) to ensure all your civilians stay out of danger. Immediately pull the lever to close the outer main gate. It is also wise to close the trade depot and inner main gate as well. That way, if enemies get past the traps, they'll have to go through the soldiers in your barracks (assuming you have a military)."
""
-"- During a siege, use the levers to control how attackers path through the trapped corridors. If there are more enemies than cage traps, time your lever pulling so that the inner gates snap closed before your last cage trap is sprung. Then the remaining attackers will have to backtrack and go through the other trap-filled hallway."
+"- During a siege, you can use the levers to control how attackers path through the trapped corridors. If there are more enemies than cage traps, time your lever pulling so that the inner gates snap closed before your last cage trap is sprung. Then the remaining attackers will have to backtrack and go through the other trap-filled hallway. You can also choose *not* to use the trap hallways and meet the siege with your military. It's up to you!"
""
"- If your cage traps fill up, ensure your hallways are free of uncaged attackers, then close the trap hallway outer gates and open the inner gates. Clear the civilian alert and allow your dwarves to reset all the traps -- make some extra cages in preparation for this! Then re-enable the civilian alert and open the trap hallway outer gates."
""
@@ -292,13 +292,13 @@ Here are some tips and procedures for handling seiges -- including how to clean
""
"After a siege, you can use the caged prisoners to safely train your military. Here's how:"
""
-"- Once the prisoners are hauled to the ""prisoner quantum"" stockpile, run ""stripcaged all"" in the DFHack gui/launcher."
+"- Once the prisoners are hauled to the ""prisoner quantum"" stockpile in the barracks, run ""stripcaged all"" in DFHack's gui/launcher."
""
"- After all the prisoners' items have been confiscated, bring your military dwarves to the barracks (if they aren't already there)."
""
-- Assign a group prisoners to the pasture that overlaps the prisoner quantum stockpile
+- Assign a group of prisoners to the pasture that overlaps the prisoner quantum stockpile
""
-"- Hauler dwarves will come and release prisoners one by one. Your military dwarves will immediately pounce on the released prisoner and chop them to bits, saving the hauler dwarves from being attacked. Repeat until all prisoners have been ""processed""."
+"- Hauler dwarves will come and release prisoners one by one. Your military dwarves will immediately pounce on the released prisoners and chop them to bits, saving the hauler dwarves from being attacked. Repeat until all prisoners have been ""processed"". Some prisoners are not directly hostile (like cavern-caught gorlaks) and you may need to be target them explicitly to get your soldiers to attack them."
#dig label(central_stairs_odd) start(2;2) hidden() carved spiral stairs odd levels
`,j6,`
u,`,u
@@ -330,6 +330,7 @@ message(Once the central stairs are mined out deeply enough, you should start di
If your wagon is within the fort perimeter, deconstruct it to get it out of the way.
Once the marked trees are all chopped down (if any), continue with /surface2.) clear trees and set up pastures"
clear_small/surface_clear_small
+burrow_start/surface_burrow_start
zones/surface_zones
#>
central_stairs/central_stairs repeat(down 10)
@@ -367,7 +368,8 @@ traps/surface_traps
clear_large/surface_clear_large
""
"#meta label(surface7) start(central stairs (on ground level)) message(Remember to enqueue manager orders for this blueprint.
-For extra security, you can run /surface8 at any time to extend the trap corridors.) build roof"
+For extra security, you can run /surface8 at any time to extend the trap corridors.) expand Inside+ burrow to safe surface areas and build roof"
+burrows/surface_burrows
#<
roof/surface_roof
roof2/surface_roof2
@@ -412,7 +414,41 @@ corridor_traps/surface_corridor_traps
-"#zone label(surface_zones) start(19; 19) hidden() message(Remember to assign your dogs to the pasture surrounding the central stairs, your grazing animals to the large pasture, and your male birds to the zone between the rows of nestboxes.) pastures and training areas"
+#burrow label(surface_burrow_start) start(19; 19) hidden() create safety burrow that will grow with your fort
+
+
+
+,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,`,`,,`,`,,`
+,,,`,,`,,`,,,,,,,,`,a{name=Inside+ create=true civalert=true}(5x5),,,,,,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,`,,~,,~,,`,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,`
+,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,`
+,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`
+
+
+
+"#zone label(surface_zones) start(19; 19) hidden() message(Remember to assign your dogs to the pasture surrounding the central stairs, your grazing animals to the large pasture, and your male birds to the zone between the rows of nestboxes. If your wagon is far away, you can let your animals wander closer to the fort before pasturing them to save hauling time.) pastures and training areas"
@@ -783,7 +819,7 @@ corridor_traps/surface_corridor_traps
,,,`,,`,Cf,Cf,,,,,Cf,,`,,,~,~,~,,,`,Cf,,Cf,,,Cf,,Cf,`,,`
,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,`
,,,`,Cf,Cf,,,,,,,,Cf,Cf,,,,~,,,,Cf,Cf,,,,,,,,Cf,Cf,`
-,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`
+,,,`,`,`,`,`,`,`,`,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,`,`,`,`,`,`,`,`,`,`,`
@@ -834,7 +870,7 @@ You might also want to set the ""trade goods quantum"" stockpile to autotrade.)
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
-,,,`,,`,,,,,,,,,,,,,,,,,"a{name=""Pets/Prisoner feeder"" autotrain=true}(9x5)",,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,"a10{name=""Pets/Prisoner feeder"" autotrain=true}(9x5)",,,,,,,,,`,,`
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
@@ -857,7 +893,6 @@ You might also want to set the ""trade goods quantum"" stockpile to autotrade.)
-#aliases
"#build label(surface_build) start(19; 19) hidden() message(Use autofarm to manage farm crop selection.
Remember to connect the levers to the gates once they are built.) gates, barracks, farm area, and trade area"
@@ -1029,6 +1064,40 @@ t1(37x33)
,,,,,,,,,,,,,Tc,Tc,,,,,,,,Tc,Tc
,,,,,,,,,,,,,Tc,Tc,,,,,,,,Tc,Tc
+#burrow label(surface_burrows) start(19; 19) hidden() extend safety burrow to newly safe surface areas and set up surrounding clearcutting area
+
+
+,,"a{name=""Clearcutting area"" create=true autochop_clear=true}(-10x-10)","a{name=""Clearcutting area"" create=true autochop_clear=true}(32x-10)",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"a{name=""Clearcutting area"" create=true autochop_clear=true}(10x-10)"
+,,"a{name=""Clearcutting area"" create=true autochop_clear=true}(-10x27)",`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,"a{name=""Clearcutting area"" create=true autochop_clear=true}(10x27)"
+,,,`,,`,a{name=Inside+}(25x17),,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,`,`,,`,`,,`
+,,,`,,`,,`,,,,,,,,`,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,`,,~,,~,,`,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,`,`,a{name=Inside+},`,`,`,`,,`,,`,`,`,`,a{name=Inside+}(7x6),,,,,,,`,,`
+,,,`,,`,a{name=Inside+}(7x5),,,,,,,,`,,,,,,,,`,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,`
+,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,`
+,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,`
+,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,`
+,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`
+,,"a{name=""Clearcutting area"" create=true autochop_clear=true}(-10x10)","a{name=""Clearcutting area"" create=true autochop_clear=true}(32x10)",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"a{name=""Clearcutting area"" create=true autochop_clear=true}(10x10)"
+
+
#build label(surface_roof) start(19; 19) hidden() roof hatch and adjacent tiles
@@ -1431,7 +1500,7 @@ doors/farming_doors
,,c,c,c,c,c,c,c,c,c,,,,`,,`,,,,c,c,c,c,c,c,c,c,c
,,c,c,c,c,c,c,c,c,,,c,`,`,`,`,`,c,,,c,c,c,c,c,c,c,c
,,c,c,c,c,c,c,c,c,,"c{name=""Unprepared fish"" take_from=""Starting food""}:+unpreparedfish",c,,,`,,,"c{name=""Rawhides"" take_from=""Starting cloth/trash""}:+rawhides",c,,c,c,c,c,c,c,c,c
-,,c,c,c,c,c,c,c,,,c,c,,"c{name=""Refuse feeder"" give_to=""Rawhides"" take_from=""Starting cloth/trash""}:+cat_refuse/type(1x3)","y{name=""Corpse feeder"" take_from=""Starting cloth/trash""}:+cat_refuse/corpses,bodyparts(2x3)",~,,c,c,,,c,c,c,c,c,c,c
+,,c,c,c,c,c,c,c,,,c,c,,"c{name=""Refuse feeder"" give_to=""Rawhides"" take_from=""Starting cloth/trash""}:+cat_refuse/type(1x3)","y2{name=""Corpse feeder"" take_from=""Starting cloth/trash""}:+cat_refuse/corpses,bodyparts(2x3)",~,,c,c,,,c,c,c,c,c,c,c
,,c,c,c,c,c,c,c,,`,`,`,,~,~,~,,`,`,`,,c,c,c,c,c,c,c
,,c,c,c,c,c,c,c,,`,`,`,,~,~,~,,`,`,`,,c,c,c,c,c,c,c
,,c,c,c,c,c,c,c,,`,`,`,,,`,,,`,`,`,,c,c,c,c,c,c,c
@@ -1687,7 +1756,7 @@ build2/industry_build2
,,w,`,`,`,`,`,`,"w{name=""Wood feeder""}(2x5)",,"g{name=""Goods feeder"" containers=0}:+cat_food/tallow+wax-crafts-goblets(3x3)",,`,,`,`,`,`,`,,"hlS{name=""Cloth/bones feeder"" containers=0}:+cat_refuse/skulls/,bones/,hair/,shells/,teeth/,horns/-adamantinethread(5x5)",,,~,~,`,`,`,`,`,`,c
,,w,`,`,`,`,`,`,~,~,~,~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,c
,,`,`,`,`,`,"c{name=""Goods/wood quantum"" quantum=true give_to=""Pots,Barrels,Jugs,Bags,Seeds feeder""}:+all",`,~,~,~,~,~,,`,,`,,`,,~,~,~,~,~,`,"r{name=""Cloth/bones quantum"" quantum=true}:+all",`,`,`,`,c
-,,"c{name=""Lye"" barrels=2}:+miscliquid",`,`,`,`,`,`,~,~,"u{name=""Furniture feeder""}:-sand(3x2)",~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,c
+,,"c{name=""Lye"" barrels=0}:+miscliquid",`,`,`,`,`,`,~,~,"u2{name=""Furniture feeder""}:-sand(3x2)",~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,c
,,c,`,`,`,`,`,`,~,~,~,~,~,,`,`,`,`,`,,~,~,~,~,~,`,`,`,`,`,`,c
,,c,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,c
,,c,`,`,`,`,`,`,`,`,`,`,`,`,"bnpdz{name=""Bar/military feeder"" containers=0}:-potash+adamantinethread(5x3)",,,,~,`,`,`,`,`,`,`,`,`,`,`,`,c
@@ -1709,7 +1778,7 @@ build2/industry_build2
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
-,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`
+,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"wj{name=""Encruster"" take_from=""Goods/wood quantum,Stoneworker quantum,Gem feeder""}",`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,"wr{name=""Stone craftsdwarf""}",`,`,`,`,`,`,`,wt,`,`,`,`,`,`,`,`,`
@@ -1745,7 +1814,7 @@ build2/industry_build2
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
-,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"wj{name=""Encruster"" take_from=""Goods/wood quantum,Stoneworker quantum,Gem feeder""}",`,`,`,`,`,`,`,`,`,`,`,`,`
+,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,`
@@ -1771,7 +1840,7 @@ build2/industry_build2
,,,,`,`,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
-,,,,`,`,`,`,`,`,`,`,`,eg,`,`,`,wf,`,`,`,ek,`,`,`,`,`,`,`,`,`
+,,,,`,`,`,`,`,`,`,`,`,eg,`,`,`,wf{max_general_orders=9},`,`,`,ek,`,`,`,`,`,`,`,`,`
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`
@@ -2452,7 +2521,7 @@ doors/guildhall_doors
"#build label(guildhall_furnish) start(15; 15; central stairs) hidden() furnish 4 guildhalls, 3 temples, and a library"
-,,`,`,`,`,s,`,`,,,`,s,`,c,`,`,f,,,`,`,s,`,`,`,`
+,,F,`,`,`,s,`,`,,,F,s,`,c,`,`,f,,,`,`,s,`,`,`,F
,,`,`,c,`,`,`,`,,,`,`,h,~a,`,`,f,,,`,`,`,`,c,`,`
,,`,c,t,c,`,`,s,,,`,`,`,`,`,`,`,,,s,`,`,c,t,c,`
,,`,`,c,`,c,`,`,,,`,`,`,`,`,`,`,,,`,`,c,`,c,`,`
@@ -2467,7 +2536,7 @@ doors/guildhall_doors
,,c,~a,`,`,c,c,`,,~,,`,,`,,`,,~,,`,c,c,`,`,~a,c
,,`,h,`,`,c,c,`,~,`,~,`,,,,`,~,`,~,`,c,c,`,`,h,`
,,s,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,s
-,,`,`,`,`,`,`,t,,,,,~,,~,,,,,t,`,`,`,`,`,`
+,,F,`,`,`,`,`,t,,,,,~,,~,,,,,t,`,`,`,`,`,F
,,,,,,,~,,,,,,`,~,`,,,,,,~
,,,,,,,~,,,,,,~,,~,,,,,,~
,,`,`,s,`,`,`,t,,,t,`,`,`,`,`,t,,,t,`,`,`,s,`,`
@@ -2476,7 +2545,7 @@ doors/guildhall_doors
,,`,`,c,`,c,`,`,,,t,c,`,`,`,c,t,,,`,`,c,`,c,`,`
,,`,c,t,c,`,`,s,,,t,c,`,`,`,c,t,,,s,`,`,c,t,c,`
,,`,`,c,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,c,`,`
-,,`,`,`,`,s,`,`,,,h,~c,~c,s,~c,~c,h,,,`,`,s,`,`,`,`
+,,F,`,`,`,s,`,`,,,h,~c,~c,s,~c,~c,h,,,`,`,s,`,`,`,F
#notes label(beds_help)
@@ -2501,37 +2570,37 @@ Apartments Walkthrough:
"#dig label(suites1) start(18; 18; central stairs) message(Once the area is dug out, run /suites2) noble suites"
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
-,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
-,d,d,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,,,d,,,,,,d,,,,d,d,d,,,,d,,,,,,d,,,,d,d
+,d,,,,d,,,,,,,d,,,,d,d,d,,,,d,,,,,,,d,,,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,,d
+,d,,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,,,d,,,,,,,d,,,,d,d,d,,,,d,,,,,,,d,,,,d
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
,d,d,,d,d,d,d,d,d,d,d,d,d,,d,`,~,`,d,,d,d,d,d,d,d,d,d,d,d,,d,d
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
-,d,d,,,,d,,,,,,d,,,,d,d,d,,,,d,,,,,,d,,,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,d,d,d,d,d,,d,d
-,d,d,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,d,d
-,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
+,d,,,,d,,,,,,,d,,,,d,d,d,,,,d,,,,,,,d,,,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,,d
+,d,,,,,,,,,,,,,,,d,d,d,,,,,,,,,,,,,,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,d,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,d,d,d,d,d,,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,,d,d,d,d,d,,d
+,d,,,,d,,,,,,,d,,,,d,d,d,,,,d,,,,,,,d,,,,d
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
"#meta label(suites2) start(central stairs) message(Remember to enqueue manager orders for this blueprint.
@@ -2541,73 +2610,73 @@ build_suites/suites_build
#dig label(suites_traffic) start(18; 18; central stairs) hidden() don't path through other dwarves' rooms
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
-,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,,,or,,,,,,or,,,,oh,`,oh,,,,or,,,,,,or,,,,`,`
+,`,,,,or,,,,,,,or,,,,`,`,`,,,,or,,,,,,,or,,,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,,`
+,`,,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,,,or,,,,,,,or,,,,oh,`,oh,,,,or,,,,,,,or,,,,`
,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`
,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`
,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`
-,`,`,,,,or,,,,,,or,,,,oh,`,oh,,,,or,,,,,,or,,,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,`
-,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,`
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
+,`,,,,or,,,,,,,or,,,,oh,`,oh,,,,or,,,,,,,or,,,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,,`
+,`,,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
+,`,,,,or,,,,,,,or,,,,`,`,`,,,,or,,,,,,,or,,,,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
#build label(suites_build) start(18; 18; central stairs) hidden()
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
-,`,`,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,`,`
-,`,`,,a,r,`,`,h,,h,`,`,r,a,,`,s,`,,a,r,`,`,h,,h,`,`,r,a,,`,`
-,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`
-,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`
-,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,s,`,,c,`,`,`,f,,f,`,`,`,c,,`,`
-,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,`
-,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,`
-,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,n,`,s,`,t,,`,`
-,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,`
-,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`
-,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,h,`,`,`,`,,`,`
-,`,`,,a,r,`,`,h,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,h,`,`,r,a,,`,`
-,`,`,,,,d,,,,,,d,,,,`,`,`,,,,d,,,,,,d,,,,`,`
+,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
+,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
+,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
+,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
+,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
+,`,,t,`,s,`,n,,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,,n,`,s,`,t,,`
+,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
+,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
+,`,,t,`,s,`,n,,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,,n,`,s,`,t,,`
+,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
+,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
+,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
+,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
+,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`
,`,`,,s,`,`,s,`,`,s,`,`,s,,`,`,~,`,`,,s,`,`,s,`,`,s,`,`,s,,`,`
,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`
-,`,`,,,,d,,,,,,d,,,,`,`,`,,,,d,,,,,,d,,,,`,`
-,`,`,,a,r,`,`,h,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,h,`,`,r,a,,`,`
-,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,h,`,`,`,`,,`,`
-,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`
-,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,`
-,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,n,`,s,`,t,,`,`
-,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,`
-,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,`
-,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,s,`,,c,`,`,`,f,,f,`,`,`,c,,`,`
-,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`
-,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`
-,`,`,,a,r,`,`,h,,h,`,`,r,a,,`,s,`,,a,r,`,`,h,,h,`,`,r,a,,`,`
-,`,`,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,`,`
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
+,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
+,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
+,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
+,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
+,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
+,`,,t,`,s,`,n,,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,,n,`,s,`,t,,`
+,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
+,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
+,`,,t,`,s,`,n,,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,,n,`,s,`,t,,`
+,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
+,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
+,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
+,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
+,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
"#dig label(apartments1) start(18; 18; central stairs) message(Once the area is dug out, continue with /apartments2.) apartment complex"
@@ -2700,13 +2769,13 @@ b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,,`,`,`,b(4x5),,,b(4x5),,,b(4x5),,,b
,,,,,,,,,,,,,,,,`,`,`
,h,f,,h,f,,h,f,,h,f,,h,f,,`,`,`,,f,h,,f,h,,f,h,,f,h,,f,h
,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,`
-,`,b,,`,b,,`,b,,`,b,,`,b,,`,,`,,b,`,,b,`,,b,`,,b,`,,b,`
+,`,b,,`,b,,`,b,,`,b,,`,b,,d,,d,,b,`,,b,`,,b,`,,b,`,,b,`
,d,,,d,,,d,,,d,,,d,,,`,`,`,,,d,,,d,,,d,,,d,,,d
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
+,`,`,`,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,`,`,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`
-,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
+,`,`,`,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,`,`,`
,d,,,d,,,d,,,d,,,d,,,`,`,`,,,d,,,d,,,d,,,d,,,d
-,`,b,,`,b,,`,b,,`,b,,`,b,,`,,`,,b,`,,b,`,,b,`,,b,`,,b,`
+,`,b,,`,b,,`,b,,`,b,,`,b,,d,,d,,b,`,,b,`,,b,`,,b,`,,b,`
,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,`
,h,f,,h,f,,h,f,,h,f,,h,f,,`,`,`,,f,h,,f,h,,f,h,,f,h,,f,h
,,,,,,,,,,,,,,,,`,`,`
diff --git a/data/dfhack-config/autonick.txt b/data/dfhack-config/autonick.txt
index 12fbe65e6..bdd40beed 100644
--- a/data/dfhack-config/autonick.txt
+++ b/data/dfhack-config/autonick.txt
@@ -7,6 +7,332 @@
Toady One
Threetoe
+#Dwarven single names taken from Classic Fantasy works
+Balin
+Dwalin
+Fili
+Kili
+Gloin
+Oin
+Bifur
+Bofur
+Bombur
+Ori
+Gimli
+Thrain
+Thror
+Fundin
+Frerin
+Gror
+Ibun
+Khim
+Fimbrethil
+Floi
+Nali
+Thor
+Vili
+Regin
+Fafnir
+Brokkr
+Sindri
+Nordri
+Sudri
+Austri
+Doriath
+Thingol
+Eol
+Mim
+Telchar
+Narvi
+Gundabad
+Muhrak
+Skorri
+Draupnir
+Alaric
+Grimr
+Eitri
+Svidurr
+Thorgar
+Hrungnir
+Galar
+Skirni
+Hreinn
+Dori
+Hreimr
+Hreinir
+Hroaldr
+Groin
+Vestri
+Nori
+Durin
+Dvalin
+Eikinskjaldi
+Bafur
+Snorri
+Fimafeng
+Vitr
+Dvali
+Dain
+Nain
+Nipingr
+Hrodvitnir
+Sveinn
+Ivaldi
+Svein
+Sveinbjorn
+Havardr
+Haki
+Hakon
+Mouri
+Motsognir
+Eberk
+Thromar
+Kragar
+Borgar
+Throk
+Orvald
+Berric
+Rogar
+Urgen
+Morgrim
+Keldar
+Ingvar
+Frandar
+Grimsi
+Hrokur
+Orik
+Rundar
+Bjornar
+Throki
+Dworin
+Thranduil
+Faldar
+Galdor
+Thorkel
+Dorrin
+Borkan
+Gundrik
+Throkir
+Raldor
+Helgrim
+Throgar
+Borin
+Ragnir
+Orvar
+Skalf
+Baldir
+Fror
+Thorgil
+Ulfar
+Grimbold
+Faldur
+Varrin
+Dornir
+Halgrim
+Gundin
+Ulfgar
+Skalfar
+Yngvarr
+Kaldur
+Thrandar
+Keldin
+Rundin
+Skaldur
+Borgin
+Haldur
+Bjornulf
+Orkarn
+Ragnor
+Baldrick
+Thorlin
+Graldor
+Ulfrik
+Fornir
+Egil
+Grimnor
+Roldor
+Ulfgard
+Borgrim
+Faldrik
+Rognir
+Balfor
+Volmar
+Thormund
+Brynhild
+
+#Dwarven composite names taken from Classic Fantasy works
+Gorin Stonehammer
+Brundar Ironfoot
+Haldrek Battlebeard
+Orin Stonefist
+Frida Stormaxe
+Torvald Rockjaw
+Einar Blackforge
+Thorgar Granitebeard
+Ragnir Hammerhelm
+Hilda Ironbrow
+Grimnar Deepdelver
+Ulfrik Ironmane
+Freya Thunderstone
+Ragnar Firebeard
+Gunnar Ironpeak
+Astrid Ironheart
+Bjorn Steelbreaker
+Hrolf Thunderhammer
+Sigrun Stonebreaker
+Eirik Rockbeard
+Helga Frostbeard
+Skaldur Stormguard
+Agnar Stonehand
+Ingrid Mountainmace
+Hjalmar Blackstone
+Solveig Steelhelm
+Rurik Stonegrip
+Freyja Silveraxe
+Thordur Goldbeard
+Gudrun Ironfoot
+Vali Fireforge
+Thora Frostbeard
+Vargr Stoneborn
+Astrid Ironbrow
+Einar Blackstone
+Hilda Hammerheart
+Leif Ironshaper
+Thrain Stormbrow
+Sigrid Steelheart
+Haldor Boulderbreaker
+Ragnhild Strongarm
+Brynjar Ironmantle
+Sigrun Thunderbeard
+Valgard Steelbeard
+Gunnhild Stonefist
+Ingrid Ironrock
+Eirik Frostbane
+Helga Deepforge
+Skaldur Ironshield
+Agnar Stonemace
+Solveig Stormgrip
+Hjalmar Mountainheart
+Gudrun Firebeard
+Thora Thunderstrike
+Vargr Ironhand
+Freyja Stoneguard
+Thordur Blackstone
+Rurik Hammerbeard
+Solveig Ironbreaker
+Astrid Goldhand
+Einar Stormbrew
+Hilda Steelbeard
+Thrain Ironmane
+Sigrid Fireheart
+Haldor Thunderstone
+Ragnhild Ironfoot
+Brynjar Blackhelm
+Sigrun Frostbeard
+Valgard Stoneshield
+Gunnhild Ironheart
+Bjorn Deepdelver
+Ingrid Ironpeak
+Eirik Thunderhammer
+Gormund Stoneforge
+Eovar Broadshield
+Thrunir Hammerstone
+Brunhild Steelbraid
+Garrik Frostbeard
+Haldrek Ironhand
+Astrid Rockrider
+Dagmar Stonefury
+Borgar Thunderhelm
+Ingrid Ironstrike
+Rurik Blackmane
+Fjorn Stoneborn
+Siv Ironbreaker
+Gudrik Stormbeard
+Ulfgar Emberforge
+Eilif Silverstone
+Hilda Stormwarden
+Ormar Ironjaw
+Vali Steelshaper
+Eira Frostbeard
+Torgar Graniteheart
+Brunhild Firebrand
+Haldrek Ironmantle
+Solveig Rockbreaker
+Thrain Thunderaxe
+Brynjar Stoneclaw
+Asa Ironhide
+Grimnar Blackmane
+Ragnvald Hammerfall
+Gudbrand Ironhand
+Astrid Flamebeard
+Ormur Steelbender
+Hjalmar Rockjaw
+Inga Thunderheart
+Valgard Ironbeard
+Eirik Swiftstrike
+Sylvi Stoneguard
+Helge Hammerfist
+Jorunn Fireforge
+Solveig Ironroot
+Thora Stormbeard
+Baldur Stonemane
+Freydis Ironshaper
+Gunnvald Deepstone
+Bjorn Blackstone
+Ingrid Frostmane
+Agnar Steelhammer
+Thordur Ironbeard
+Ylva Goldhand
+Greta Firestone
+Rurik Rockhelm
+Gunnhild Ironsong
+Vali Steelgrip
+Brynhild Stormblade
+Astrid Ironmantle
+Einar Stoneshield
+Hilda Frostbeard
+Ormr Ironheart
+Inga Steelbreaker
+Ulfrik Thunderaxe
+Freyja Stonebeard
+Sigrun Frostfury
+Sylvi Blackmane
+Thorvald Ironhelm
+Eirik Stormstone
+Haldora Deepdelver
+Sigrid Steelshaper
+Gunnar Thunderheart
+Bjorn Ironbrow
+Ingrid Goldmantle
+Agnar Stormforge
+Solveig Ironclaw
+Thora Rockguard
+Grimur Emberstone
+Ragnhild Hammerstrike
+Vali Ironfist
+Brynjar Blackbraid
+Astrid Flameforge
+Einar Stonestorm
+Hilda Frostbane
+Ormur Ironhelm
+Inga Steelshaper
+Gudbrand Thunderbeard
+Freya Stonefist
+Gunnvald Stormmane
+Bjorn Ironhelm
+Ingrid Frostforge
+Agnar Steelgrip
+Thordur Ironhand
+Ylva Flameheart
+Greta Stonemane
+Rurik Ironroot
+Gunnhild Steelbeard
+Vali Thunderstrike
+Thorin Oakenshield
+Dain Ironfoot
+Gamil Zirak
+
+
# animals
Mouse
Otter
diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init
index f677ca301..ce4a8a490 100644
--- a/data/init/dfhack.keybindings.init
+++ b/data/init/dfhack.keybindings.init
@@ -38,7 +38,7 @@ keybinding add Ctrl-V@dwarfmode digv
keybinding add Ctrl-Shift-V@dwarfmode "digv x"
# clean the selected tile of blood etc
-keybinding add Ctrl-C spotclean
+keybinding add Ctrl-C@dwarfmode spotclean
# destroy the selected item
keybinding add Ctrl-K@dwarfmode autodump-destroy-item
@@ -157,7 +157,7 @@ keybinding add Alt-K@dwarfmode toggle-kbd-cursor
#keybinding add Ctrl-Shift-T@dwarfmode|unit|unitlist|joblist|dungeon_monsterstatus|layer_unit_relationship|item|workshop_profile|layer_noblelist|locations|pets|layer_overall_health|textviewer|reportlist|announcelist|layer_military|layer_unit_health|customize_unit "gui/rename unit-profession"
# gui/design
-keybinding add Ctrl-D@dwarfmode gui/design
+keybinding add Ctrl-D@dwarfmode/Default gui/design
diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init
index 38221883a..02169b526 100644
--- a/data/init/dfhack.tools.init
+++ b/data/init/dfhack.tools.init
@@ -80,6 +80,7 @@
# Enable system services
enable buildingplan
+enable burrow
enable confirm
enable logistics
enable overlay
diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt
index 15ff52488..d3cfbb415 100644
--- a/depends/CMakeLists.txt
+++ b/depends/CMakeLists.txt
@@ -4,11 +4,19 @@ add_subdirectory(lua)
add_subdirectory(md5)
add_subdirectory(protobuf)
+if(UNIX)
+ set_target_properties(lua PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-deprecated-enum-enum-conversion")
+ set_target_properties(protoc PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict")
+ set_target_properties(protoc-bin PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict")
+ set_target_properties(protobuf-lite PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict")
+ set_target_properties(protobuf PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict")
+endif()
+
if(UNIX AND NOT APPLE) # remove this once our MSVC build env has been updated
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF)
add_subdirectory(googletest)
if(UNIX)
- set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized -Wno-sign-compare")
+ set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized -Wno-sign-compare -Wno-restrict")
endif()
endif()
@@ -31,8 +39,6 @@ option(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependenc
add_subdirectory(clsocket)
ide_folder(clsocket "Depends")
-install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/luacov/src/luacov/ DESTINATION ${DFHACK_DATA_DESTINATION}/lua/luacov)
-
# set the default values of libexpat options - the descriptions are left empty
# because later option() calls *do* override those
set(EXPAT_BUILD_EXAMPLES OFF CACHE BOOL "")
diff --git a/depends/clsocket b/depends/clsocket
index d5e17c601..8cf949340 160000
--- a/depends/clsocket
+++ b/depends/clsocket
@@ -1 +1 @@
-Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094
+Subproject commit 8cf949340e22001bee1ca25c9d6c1d6a89e8faf2
diff --git a/depends/libzip b/depends/libzip
index 081249cce..fc4a77ea2 160000
--- a/depends/libzip
+++ b/depends/libzip
@@ -1 +1 @@
-Subproject commit 081249cceb59adc857a72d67e60c32047680f787
+Subproject commit fc4a77ea28eb5eba0ecd11443f291335ec3d2aa0
diff --git a/depends/protobuf/google/protobuf/repeated_field.h b/depends/protobuf/google/protobuf/repeated_field.h
index aed4ce9f2..637708254 100644
--- a/depends/protobuf/google/protobuf/repeated_field.h
+++ b/depends/protobuf/google/protobuf/repeated_field.h
@@ -46,6 +46,10 @@
#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__
#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__
+#ifdef __GNUC__
+#pragma GCC system_header
+#endif
+
#include
#include
#include
diff --git a/docs/Core.rst b/docs/Core.rst
index 763858b61..a8991147f 100644
--- a/docs/Core.rst
+++ b/docs/Core.rst
@@ -377,6 +377,23 @@ Other (non-DFHack-specific) variables that affect DFHack:
sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this
should be the case in most UTF-8-capable \*nix terminal emulators already.
+Core preferences
+================
+
+There are a few settings that can be changed dynamically via
+`gui/control-panel` to affect runtime behavior. You can also toggle these from
+the commandline using the `lua` command, e.g.
+``lua dfhack.HIDE_ARMOK_TOOLS=true`` or by editing the generated
+``dfhack-config/init/dfhack.control-panel-preferences.init`` file and
+restarting DF.
+
+- ``dfhack.HIDE_CONSOLE_ON_STARTUP``: Whether to hide the external DFHack
+ terminal window on startup. This, of course, is not useful to change
+ dynamically. You'll have to use `gui/control-panel` or edit the init file
+ directly and restart DF for it to have an effect.
+
+- ``dfhack.HIDE_ARMOK_TOOLS``: Whether to hide "armok" tools in command lists.
+
Miscellaneous notes
===================
This section is for odd but important notes that don't fit anywhere else.
diff --git a/docs/Installing.rst b/docs/Installing.rst
index 8a04b7cb9..14b5a0f93 100644
--- a/docs/Installing.rst
+++ b/docs/Installing.rst
@@ -48,10 +48,6 @@ DF version - see `above ` for details. For example:
* ``dfhack-50.07-r1-Windows-64bit.zip`` supports 64-bit DF on Windows
-In between stable releases, we may create beta releases to test new features.
-These are available via the beta release channel on Steam or from our regular
-Github page as a pre-release tagged with a "beta" suffix.
-
.. warning::
Do *not* download the source code from GitHub, either from the releases page
@@ -60,6 +56,31 @@ Github page as a pre-release tagged with a "beta" suffix.
you want to compile DFHack instead of using a pre-built release, see
`building-dfhack-index` for instructions.)
+Beta releases
+-------------
+
+In between stable releases, we may create beta releases to test new features.
+These are available via the ``beta`` release channel on Steam or from our
+regular Github page as a pre-release tagged with a "beta" or "rc" suffix.
+
+Development builds
+------------------
+
+If you are actively working with the DFHack team on testing a feature, you may
+want to download and install a development build. They are available via the
+``testing`` release channel on Steam or can be downloaded from the build
+artifact list on GitHub for specific repository commits.
+
+To download a development build from GitHub:
+
+- Ensure you are logged into your GitHub account
+- Go to https://github.com/DFHack/dfhack/actions/workflows/build.yml?query=branch%3Adevelop+event%3Apush
+- Click on the first entry that has a green checkmark
+- Click the number under "Artifacts" (or scroll down)
+- Click on the "dfhack-*-build-*" artifact for your platform to download
+
+You can extract this package the same as if you are doing a manual install (see the next section).
+
Installing DFHack
=================
diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst
index f4a022a9c..10349a65f 100644
--- a/docs/Quickstart.rst
+++ b/docs/Quickstart.rst
@@ -45,67 +45,75 @@ Here are some common tasks people use DFHack tools to accomplish:
- Quickly scan the map for visible ores of specific types so you can focus
your mining efforts
-Some tools are one-shot commands. For example, you can run `unforbid all `
-to claim all (reachable) items on the map after a messy siege.
-
-Other tools must be `enabled ` and then they will run in the background.
-For example, `enable seedwatch ` will start monitoring your stocks of
-seeds and prevent your chefs from cooking seeds that you need for planting.
-Tools that are enabled in the context of a fort will save their state with that
-fort, and they will remember that they are enabled the next time you load your save.
-
-A third class of tools add information to the screen or provide new integrated
-functionality via the DFHack `overlay` framework. For example, the `unsuspend`
-tool, in addition to its basic function of unsuspending all building construction
-jobs, can also overlay a marker on suspended buildings to indicate that they are
-suspended (and will use different markers to tell you whether this is a problem).
+Some tools are one-shot commands. For example, you can run
+`unforbid all ` to claim all (reachable) items on the map after a
+messy siege.
+
+Other tools must be `enabled ` once and then they will run in the
+background. For example, once enabled, `seedwatch` will start monitoring your
+stocks of seeds and prevent your chefs from cooking seeds that you need for
+planting. Tools that are enabled in the context of a fort will save their state
+with that fort, and they will remember that they are enabled the next time you
+load your save.
+
+A third class of tools adds information to the screen or provides new integrated
+functionality via the DFHack `overlay` framework. For example, the `sort` tool
+adds widgets to the squad member selection screen that allow you to search,
+sort, and filter the list of military candidates. You don't have to run any
+command to get the benefits of the tool, it appears automatically when you're
+on the relevant screen.
How can I figure out which commands to run?
-------------------------------------------
-There are several ways to scan DFHack tools and find the ones you need right now.
+There are several ways to scan DFHack tools and find the ones you need right
+now.
-The first place to check is the DFHack logo hover hotspot. It's in the upper
-left corner of the screen by default, though you can move it anywhere you want
-with the `gui/overlay` configuration UI.
+The first place to check is the DFHack logo menu. It's in the upper left corner
+of the screen by default, though you can move it anywhere you want with the
+`gui/overlay` configuration UI.
-When you hover the mouse over the logo (or hit the Ctrl-Shift-C keyboard shortcut)
-a list of DFHack tools relevant to the current context comes up. For example, when
-you have a unit selected, the hotspot will show a list of tools that inspect
-units, allow you to edit them, or maybe even teleport them. Next to each tool,
-you'll see the hotkey you can hit to invoke the command without even opening the
-hover list.
+When you click on the logo (or hit the Ctrl-Shift-C keyboard shortcut), a short
+list of popular, relevant DFHack tools comes up. These are the tools that have
+been assigned hotkeys that are active in the current context. For example, when
+you're looking at a fort map, the list will contain fortress design tools like
+`gui/quickfort` and `gui/design`. You can click on the tools in the list, or
+note the hotkeys listed next to them and maybe use them to launch the tool next
+time without even opening the logo menu.
The second place to check is the DFHack control panel: `gui/control-panel`. It
will give you an overview of which tools are currently enabled, and will allow
you to toggle them on or off, see help text for them, or launch their dedicated
configuration UIs. You can open the control panel from anywhere with the
-Ctrl-Shift-E hotkey or by selecting it from the logo hover list.
+Ctrl-Shift-E hotkey or by selecting it from the logo menu list.
In the control panel, you can also select which tools you'd like to be
-automatically enabled when you start a new fort. There are also system settings
-you can change, like whether DFHack windows will pause the game when they come
-up.
-
-Finally, you can explore the full extent of the DFHack catalog in `gui/launcher`,
-which is always listed first in the DFHack logo hover list. You can also bring up
-the launcher by tapping the backtick key (\`) or hitting Ctrl-Shift-D. In the
-launcher, you can quickly autocomplete any command name by selecting it in the
-list on the right side of the window. Commands are ordered by how often you run
-them, so your favorite commands will always be on top. You can also pull full
-commandlines out of your history with Alt-S or by clicking on the "history search"
-hotkey hint.
-
-Once you have typed (or autocompleted, or searched for) a command, other commands
-related to the one you have selected will appear in the right-hand panel. Scanning
-through that list is a great way to learn about new tools that you might find
-useful. You can also see how commands are grouped by running the `tags` command.
+automatically enabled and popular commands you'd like to run when you start a
+new fort. On the "Preferences" tab, there are settings you can change, like
+whether you want to limit DFHack functionality to interface improvements,
+bugfixes, and productivity tools, hiding the god-mode tools ("mortal mode") or
+whether you want DFHack windows to pause the game when they come up.
+
+Finally, you can explore the full extent of the DFHack catalog in
+`gui/launcher`, which is always listed first in the DFHack logo menu list. You
+can also bring up the launcher by tapping the backtick key (\`) or hitting
+Ctrl-Shift-D. In the launcher, you can quickly autocomplete any command name by
+selecting it in the list on the right side of the window. Commands are ordered
+by how often you run them, so your favorite commands will always be on top. You
+can also pull full commandlines out of your history with Alt-S or by clicking
+on the "history search" hotkey hint.
+
+Once you have typed (or autocompleted, or searched for) a command, other
+commands related to the one you have selected will appear in the right-hand
+panel. Scanning through that list is a great way to learn about new tools that
+you might find useful. You can also see how commands are grouped by running the
+`tags` command.
The bottom panel will show the full help text for the command you are running,
-allowing you to refer to the usage documentation and examples when you are typing
-your command. After you run a command, the bottom panel switches to command output
-mode, but you can get back to the help text by hitting Ctrl-T or clicking on the
-``Help`` tab.
+allowing you to refer to the usage documentation and examples when you are
+typing your command. After you run a command, the bottom panel switches to
+command output mode, but you can get back to the help text by hitting Ctrl-T or
+clicking on the ``Help`` tab.
How do DFHack in-game windows work?
-----------------------------------
@@ -122,84 +130,88 @@ you type at the keyboard. Hit Esc or right click to close the window or cancel
the current action. You can click anywhere on the screen that is not a DFHack
window to unfocus the window and let it just sit in the background. It won't
respond to key presses or mouse clicks until you click on it again to give it
-focus. If no DFHack windows are focused, you can right click directly on a window
-to close it without left clicking to focus it first.
+focus. If no DFHack windows are focused, you can right click directly on a
+window to close it without left clicking to focus it first.
DFHack windows are draggable from the title bar or from anywhere on the window
that doesn't have a mouse-clickable widget on it. Many are resizable as well
(if the tool window has components that can reasonably be resized).
-You can generally use DFHack tools without interrupting the game. That is, if the
-game is unpaused, it can continue to run while a DFHack window is open. If configured
-to do so in `gui/control-panel`, tools will initially pause the game to let you
-focus on the task at hand, but you can unpause like normal if you want. You can
-also interact with the map, scrolling it with the keyboard or mouse and selecting
-units, buildings, and items. Some tools will intercept all mouse clicks to allow
-you to select regions on the map. When these tools have focus, you will not be able
-to use the mouse to interact with map elements or pause/unpause the game. Therefore,
-these tools will pause the game when they open, regardless of your settings in
-`gui/control-panel`. You can still unpause with the keyboard (spacebar by default),
-though.
+You can generally use DFHack tools without interrupting the game. That is, if
+the game is unpaused, it can continue to run while a DFHack window is open. If
+configured to do so in `gui/control-panel`, tools will initially pause the game
+to let you focus on the task at hand, but you can unpause like normal if you
+want. You can also interact with the map, scrolling it with the keyboard or
+mouse and selecting units, buildings, and items. Some tools will intercept all
+mouse clicks to allow you to select regions of the map. When these tools have
+focus, you will not be able to use the mouse to interact with map elements or
+pause/unpause the game. Therefore, these tools will pause the game when they
+open, regardless of your settings in `gui/control-panel`. You can still unpause
+with the keyboard (spacebar by default), though.
Where do I go next?
-------------------
To recap:
-You can get to popular, relevant tools for the current context by hovering
-the mouse over the DFHack logo or by hitting Ctrl-Shift-C.
+You can get to popular, relevant tools for the current context by clicking on
+the DFHack logo or by hitting Ctrl-Shift-C.
You can enable DFHack tools and configure settings with `gui/control-panel`,
-which you can access directly with the Ctrl-Shift-E hotkey.
+which you can open from the DFHack logo or access directly with the
+Ctrl-Shift-E hotkey.
You can get to the launcher and its integrated autocomplete, history search,
and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by
-running it from the logo hover list.
+running it from the logo menu list.
With those three interfaces, you have the complete DFHack tool suite at your
-fingertips. So what to run first? Here are a few commands to get you started.
-You can run them all from the launcher.
+fingertips. So what to run first? Here are a few examples to get you started.
First, let's import some useful manager orders to keep your fort stocked with
basic necessities. Run ``orders import library/basic``. If you go to your
-manager orders screen, you can see all the orders that have been created for you.
-Note that you could have imported the orders directly from this screen as well,
-using the DFHack `overlay` widget at the bottom of the manager orders panel.
-
-Next, try setting up `autochop` to automatically designate trees for chopping when
-you get low on usable logs. Run `gui/control-panel` and select ``autochop`` in the
-``Fort`` list. Click on the button to the left of the name or hit Enter to enable
-it. You can then click on the configure button (the gear icon) to launch
-`gui/autochop` if you'd like to customize its settings. If you have the extra
-screen space, you can go ahead and set the `gui/autochop` window to minimal mode
-(click on the hint near the upper right corner of the window or hit Alt-M) and
-click on the map so the window loses keyboard focus. As you play the game, you can
-glance at the live status panel to check on your stocks of wood.
-
-Finally, let's do some fort design copy-pasting. Go to some bedrooms that you have
-set up in your fort. Run `gui/blueprint`, set a name for your blueprint by
-clicking on the name field (or hitting the 'n' hotkey), typing "rooms" (or whatever)
-and hitting Enter to set. Then draw a box around the target area by clicking with
-the mouse. When you select the second corner, the blueprint will be saved to your
-``blueprints`` subfolder.
-
-Now open up `gui/quickfort`. You can search for the blueprint you just created by
-typing its name, but it should be up near the top already. If you copied a dug-out
-area with furniture in it, your blueprint will have two labels: "/dig" and "/build".
-Click on the "/dig" blueprint or select it with the keyboard arrow keys and hit Enter.
-You can rotate or flip the blueprint around if you need to with the transform hotkeys.
-You'll see a preview of where the blueprint will be applied as you move the mouse
-cursor around the map. Red outlines mean that the blueprint may fail to fully apply
-at that location, so be sure to choose a spot where all the preview tiles are shown
-with green diamonds. Click the mouse or hit Enter to apply the blueprint and
-designate the tiles for digging. Your dwarves will come and dig it out as if you
-had designated the tiles yourself.
-
-Once the area is dug out, run `gui/quickfort` again and select the "/build" blueprint
-this time. Apply the blueprint in the dug-out area, and your furniture will be
-designated. It's just that easy! Note that `quickfort` uses `buildingplan` to place
-buildings, so you don't even need to have the relevant furniture or building
-materials in stock. The planned furniture/buildings will get built whenever you are
-able to produce the building materials.
+manager orders screen, you can see all the orders that have been created for
+you. Note that you could have imported the orders directly from this screen as
+well, using the DFHack `overlay` widget at the bottom of the manager orders
+panel.
+
+Next, try setting up `autochop` to automatically designate trees for chopping
+when you get low on usable logs. Run `gui/control-panel` and select
+``autochop`` in the ``Fort`` list. Click on the button to the left of the name
+or hit Enter to enable it. You can then click on the configure button (the gear
+icon) to launch `gui/autochop` if you'd like to customize its settings. If you
+have the extra screen space, you can go ahead and set the `gui/autochop` window
+to minimal mode (click on the hint near the upper right corner of the window or
+hit Alt-M) and click on the map so the window loses keyboard focus. As you play
+the game, you can glance at the live status panel to check on your stocks of
+wood.
+
+Finally, let's do some fort design copy-pasting. Go to some bedrooms that you
+have set up in your fort. Run `gui/blueprint`, set a name for your blueprint by
+clicking on the name field (or hitting the 'n' hotkey), typing "rooms" (or
+whatever) and hitting Enter to set. Then draw a box around the target area by
+clicking with the mouse. When you select the second corner, the blueprint will
+be saved to your ``dfhack-config/blueprints`` subfolder.
+
+Now open up `gui/quickfort`. You can search for the blueprint you just created
+by typing its name, but it should be up near the top already. If you copied a
+dug-out area with furniture in it, your blueprint will have two labels: "/dig"
+and "/build". Click on the "/dig" blueprint or select it with the keyboard
+arrow keys and hit Enter. You can rotate or flip the blueprint around if you
+need to with the transform hotkeys. You'll see a preview of where the blueprint
+will be applied as you move the mouse cursor around the map. Red outlines mean
+that the blueprint may fail to fully apply at that location, so be sure to
+choose a spot where all the preview tiles are shown with green diamonds. Click
+the mouse or hit Enter to apply the blueprint and designate the tiles for
+digging. Your dwarves will come and dig it out as if you had designated the
+tiles yourself.
+
+Once the area is dug out, run `gui/quickfort` again and select your "/build"
+blueprint this time. Hit ``o`` to generate manager orders for the required
+furniture. Apply the blueprint in the dug-out area, and your furniture will be
+designated. It's just that easy! Note that `quickfort` uses `buildingplan` to
+place buildings, so you don't even need to have the relevant furniture or
+building materials in stock yet. The planned furniture/buildings will get built
+whenever you are able to produce the building materials.
There are many, many more tools to explore. Have fun!
diff --git a/docs/Tools.rst b/docs/Tools.rst
index 401276d7f..67d7edc54 100644
--- a/docs/Tools.rst
+++ b/docs/Tools.rst
@@ -3,8 +3,8 @@
DFHack tools
============
-DFHack has **a lot** of tools. This page attempts to make it clearer what they
-are, how they work, and how to find the ones you want.
+DFHack comes with **a lot** of tools. This page attempts to make it clearer
+what they are, how they work, and how to find the ones you want.
.. contents:: Contents
:local:
@@ -36,6 +36,12 @@ 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 `.
+Some tools are part of our back catalog and haven't been updated yet for v50 of
+Dwarf Fortress. These tools are tagged as
+`unavailable `. They will still appear in the
+alphabetical list at the bottom of this page, but unavailable tools will not
+listed in any of the indices.
+
DFHack tools by game mode
-------------------------
diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst
index cf74c6412..27b896f89 100644
--- a/docs/about/Authors.rst
+++ b/docs/about/Authors.rst
@@ -3,7 +3,7 @@ List of authors
The following is a list of people who have contributed to DFHack, in
alphabetical order.
-If you should be here and aren't, please get in touch on IRC or the forums,
+If you should be here and aren't, please get in touch on Discord or the forums,
or make a pull request!
======================= ======================= ===========================
@@ -83,6 +83,7 @@ Herwig Hochleitner bendlas
Hevlikn Hevlikn
Ian S kremlin-
IndigoFenix
+Jacek Konieczny Jajcus
James 20k
James Gilles kazimuth
James Logsdon jlogsdon
@@ -129,6 +130,7 @@ Michael Crouch creidieki
Michon van Dooren MaienM
miffedmap miffedmap
Mike Stewart thewonderidiot
+Mikhail Panov Halifay
Mikko Juola Noeda Adeon
Milo Christiansen milochristiansen
MithrilTuxedo MithrilTuxedo
@@ -137,6 +139,7 @@ moversti moversti
mrrho mrrho
Murad Beybalaev Erquint
Myk Taylor myk002
+Najeeb Al-Shabibi master-spike
napagokc napagokc
Neil Little nmlittle
Nick Rart nickrart comestible
@@ -203,6 +206,7 @@ Sebastian Wolfertz Enkrod
SeerSkye SeerSkye
seishuuu seishuuu
Seth Woodworth sethwoodworth
+shevernitskiy shevernitskiy
Shim Panze Shim-Panze
Silver silverflyone
simon
diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst
index 34d44ab0d..da7d42d37 100644
--- a/docs/about/Removed.rst
+++ b/docs/about/Removed.rst
@@ -10,6 +10,13 @@ work (e.g. links from the `changelog`).
:local:
:depth: 1
+.. _workorder-recheck:
+
+workorder-recheck
+=================
+Tool to set 'Checking' status of the selected work order, allowing conditions to be
+reevaluated. Merged into `orders`.
+
.. _autohauler:
autohauler
@@ -184,6 +191,12 @@ gui/hack-wish
=============
Replaced by `gui/create-item`.
+.. _gui/mechanisms:
+
+gui/mechanisms
+==============
+Linked building interface has been added to the vanilla UI.
+
.. _gui/no-dfhack-init:
gui/no-dfhack-init
@@ -192,13 +205,6 @@ Tool that warned the user when the ``dfhack.init`` file did not exist. Now that
``dfhack.init`` is autogenerated in ``dfhack-config/init``, this warning is no
longer necessary.
-.. _gui/stockpiles:
-
-gui/stockpiles
-==============
-Provided import/export dialogs. Converted to an `overlay` that displays when
-a stockpile is selected.
-
.. _masspit:
masspit
@@ -220,6 +226,12 @@ ruby
Support for the Ruby language in DFHack scripts was removed due to the issues
the Ruby library causes when used as an embedded language.
+.. _search-plugin:
+
+search
+======
+Functionality was merged into `sort`.
+
.. _show-unit-syndromes:
show-unit-syndromes
diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst
index c9665a048..c6553e48c 100644
--- a/docs/builtins/keybinding.rst
+++ b/docs/builtins/keybinding.rst
@@ -33,6 +33,11 @@ The ```` parameter above has the following **case-sensitive** syntax::
where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote
optional parts.
+DFHack commands can advertise the contexts in which they can be usefully run.
+For example, a command that acts on a selected unit can tell `keybinding` that
+it is not "applicable" in the current context if a unit is not actively
+selected.
+
When multiple commands are bound to the same key combination, DFHack selects
the first applicable one. Later ``add`` commands, and earlier entries within one
``add`` command have priority. Commands that are not specifically intended for
diff --git a/docs/changelog.txt b/docs/changelog.txt
index 54c2ba361..6f2e0d8f7 100644
--- a/docs/changelog.txt
+++ b/docs/changelog.txt
@@ -22,6 +22,24 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
spans multiple lines. Three ``]`` characters indicate the end of such a block.
- ``!`` immediately before a phrase set up to be replaced (see gen_changelog.py) stops that occurrence from being replaced.
+Template for new versions:
+
+## New Tools
+
+## New Features
+
+## Fixes
+
+## Misc Improvements
+
+## Documentation
+
+## API
+
+## Lua
+
+## Removed
+
===end
]]]
@@ -33,22 +51,234 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future
+## New Tools
+- `burrow`: (reinstated) automatically expand burrows as you dig
+
+## New Features
+- `prospect`: can now give you an estimate of resources from the embark screen. hover the mouse over a potential embark area and run `prospect`.
+- `burrow`: integrated 3d box fill and 2d/3d flood fill extensions for burrow painting mode
+- `buildingplan`: allow specific mechanisms to be selected when linking levers
+- `sort`: military and burrow membership filters for the burrow assignment screen
+
+## Fixes
+- `stockpiles`: hide configure and help buttons when the overlay panel is minimized
+- `caravan`: price of vermin swarms correctly adjusted down. a stack of 10000 bees is worth 10, not 10000
+- `sort`: when filtering out already-established temples in the location assignment screen, also filter out the "No specific deity" option if a non-denominational temple has already been established
+- RemoteServer: continue to accept connections as long as the listening socket is valid instead of closing the socket after the first disconnect
+
+## Misc Improvements
+- `buildingplan`: display how many items are available on the planner panel
+- `buildingplan`: clarify interface when building single-tile staircases
+- `sort`: allow searching by profession on the squad assignment page
+- `sort`: add search for work animal assignment screen; allow filtering by miltary/squad/civilian/burrow
+- `sort`: on the squad assignment screen, make effectiveness and potential ratings use the same scale so effectiveness is always less than or equal to potential for a unit and so you can tell when units are approaching their maximum potential
+- `sort`: new overlay on the animal assignment screen that shows how many work animals each visible unit already has assigned to them
+- `dreamfort`: Inside+ and Clearcutting burrows now automatically created and managed
+
+## Documentation
+
+## API
+- ``Gui::revealInDwarfmodeMap``: gained ``highlight`` parameter to control setting the tile highlight on the zoom target
+- ``Maps::getWalkableGroup``: get the walkability group of a tile
+- ``Units::getReadableName``: now returns the *untranslated* name
+- ``Burrows::setAssignedUnit``: now properly handles inactive burrows
+- ``Gui::getMousePos``: now takes an optional ``allow_out_of_bounds`` parameter so coordinates can be returned for mouse positions outside of the game map (i.e. in the blank space around the map)
+
+## Lua
+- ``dfhack.gui.revealInDwarfmodeMap``: gained ``highlight`` parameter to control setting the tile highlight on the zoom target
+- ``dfhack.maps.getWalkableGroup``: get the walkability group of a tile
+- ``dfhack.gui.getMousePos``: support new optional ``allow_out_of_bounds`` parameter
+- ``gui.FRAME_THIN``: a panel frame suitable for floating tooltips
+
+## Removed
+
+# 50.11-r2
+
+## New Tools
+- `spectate`: (reinstated) automatically follow dwarves, cycling among interesting ones
+- `preserve-tombs`: keep tombs assigned to units when they die
+
+## New Features
+- `logistics`: ``automelt`` now optionally supports melting masterworks; click on gear icon on `stockpiles` overlay frame
+- `sort`: new search widgets for Info panel tabs, including all "Creatures" subtabs, all "Objects" subtabs, "Tasks", candidate assignment on the "Noble" subtab, and the "Work details" subtab under "Labor"
+- `sort`: new search and filter widgets for the "Interrogate" and "Convict" screens under "Justice"
+- `sort`: new search widgets for location selection screen (when you're choosing what kind of guildhall or temple to dedicate)
+- `sort`: new search widgets for burrow assignment screen and other unit assignment dialogs
+- `sort`: new search widgets for artifacts on the world/raid screen
+- `sort`: new search widgets for slab engraving menu; can filter for only units that need a slab to prevent rising as a ghost
+- `stocks`: hotkey for collapsing all categories on stocks screen
+
+## Fixes
+- `buildingplan`: remove bars of ash, coal, and soap as valid building materials to match v50 rules
+- `buildingplan`: fix incorrect required items being displayed sometimes when switching the planner overlay on and off
+- `zone`: races without specific child or baby names will now get generic child/baby names instead of an empty string
+- `zone`: don't show animal assignment link for cages and restraints linked to dungeon zones (which aren't normally assignable)
+- `sort`: don't count mercenaries as appointed officials in the squad assignment screen
+- `dwarfvet`: fix invalid job id assigned to ``Rest`` job, which could cause crashes on reload
+
+## Misc Improvements
+- `overlay`: allow ``overlay_onupdate_max_freq_seconds`` to be dynamically set to 0 for a burst of high-frequency updates
+- Help icons added to several complex overlays. clicking the icon runs `gui/launcher` with the help text in the help area
+- `orders`: ``recheck`` command now only resets orders that have conditions that can be rechecked
+- `sort`: added help button for squad assignment search/filter/sort
+- `zone`: animals trained for war or hunting are now labeled as such in animal assignment screens
+- `buildingplan`: support filtering cages by whether they are occupied
+- `buildingplan`: show how many items you need to make when planning buildings
+- `tailor`: now adds to existing orders if possilbe instead of creating new ones
+
+## Documentation
+- unavailable tools are no longer listed in the tag indices in the online docs
+
+## API
+- added ``Items::getCapacity``, returns the capacity of an item as a container (reverse-engineered), needed for `combine`
+
+## Lua
+- added ``GRAY`` color aliases for ``GREY`` colors
+- added ``dfhack.items.getCapacity`` to expose the new module API
+- ``utils.search_text``: text search routine (generalized from internal ``widgets.FilteredList`` logic)
+
+## Removed
+- ``FILTER_FULL_TEXT``: moved from ``gui.widgets`` to ``utils``; if your full text search preference is lost, please reset it in `gui/control-panel`
+
+# 50.11-r1
+
+## New Tools
+- `tubefill`: (reinstated) replenishes mined-out adamantine
+
+## Fixes
+- `autolabor`: ensure vanilla work details are reinstated when the fort or the plugin is unloaded
+- ``dfhack.TranslateName()``: fixed crash on certain invalid names, which affected `warn-starving`
+- EventManager: Unit death event no longer misfires on units leaving the map
+
+## Misc Improvements
+- `digtype`: designate only visible tiles by default, and use "auto" dig mode for following veins
+- `digtype`: added options for designating only current z-level, this z-level and above, and this z-level and below
+- `hotkeys`: make the DFHack logo brighten on hover in ascii mode to indicate that it is clickable
+- `hotkeys`: use vertical bars instead of "!" symbols for the DFHack logo in ascii mode to make it easier to read
+- EventManager: guard against potential iterator invalidation if one of the event listeners were to modify the global data structure being iterated over
+- EventManager: for ``onBuildingCreatedDestroyed`` events, changed firing order of events so destroyed events come before created events
+
+## Lua
+- mouse key events are now aligned with internal DF semantics: ``_MOUSE_L`` indicates that the left mouse button has just been pressed and ``_MOUSE_L_DOWN`` indicates that the left mouse button is being held down. similarly for ``_MOUSE_R`` and ``_MOUSE_M``. 3rd party scripts may have to adjust.
+
+# 50.10-r1
+
+## Fixes
+- Linux launcher: allow Steam Overlay and game streaming to function
+- `autobutcher`: don't ignore semi-wild units when marking units for slaughter
+
+## Misc Improvements
+- 'sort': Improve combat skill scale thresholds
+
+# 50.09-r4
+
+## New Features
+- `dig`: new overlay for ASCII mode that visualizes designations for smoothing, engraving, carving tracks, and carving fortifications
+
+## Fixes
+- `buildingplan`: make the construction dimensions readout visible again
+- `seedwatch`: fix a crash when reading data saved by very very old versions of the plugin
+- `gui/mod-manager`: don't continue to display overlay after the raws loading progress bar appears
+
+## Misc Improvements
+- `sort`: add sort option for training need on squad assignment screen
+- `sort`: filter mothers with infants, units with weak mental fortitude, and critically injured units on the squad assignment screen
+- `sort`: display a rating relative to the current sort order next to the visible units on the squad assignment screen
+
+## Documentation
+- add instructions for downloading development builds to the ``Installing`` page
+
+## API
+- `overlay`: overlay widgets can now declare a ``version`` attribute. changing the version of a widget will reset its settings to defaults. this is useful when changing the overlay layout and old saved positions will no longer be valid.
+
+## Lua
+- ``argparse.boolean``: convert arguments to lua boolean values.
+
+# 50.09-r3
+
+## New Features
+- `sort`: search, sort, and filter for squad assignment screen
+- `zone`: advanced unit assignment screens for cages, restraints, and pits/ponds
+- `buildingplan`: one-click magma/fire safety filter for planned buildings
+
+## Fixes
+- Core: reload scripts in mods when a world is unloaded and immediately loaded again
+- Core: fix text getting added to DFHack text entry widgets when Alt- or Ctrl- keys are hit
+- `buildingplan`: ensure selected barrels and buckets are empty (or at least free of lye and milk) as per the requirements of the building
+- `orders`: prevent import/export overlay from appearing on the create workorder screen
+- `caravan`: corrected prices for cages that have units inside of them
+- `tailor`: remove crash caused by clothing items with an invalid ``maker_race``
+- ``dialogs.MessageBox``: fix spacing around scrollable text
+- `seedwatch`: ignore unplantable tree seeds
+- `autobutcher`: fix ``ticks`` commandline option incorrectly rejecting positive integers as valid values
+
+## Misc Improvements
+- Surround DFHack-specific UI elements with square brackets instead of red-yellow blocks for better readability
+- `autobutcher`: don't mark animals for butchering if they are already marked for some kind of training (war, hunt)
+- `hotkeys`: don't display DFHack logo in legends mode since it covers up important interface elements. the Ctrl-Shift-C hotkey to bring up the menu and the mouseover hotspot still function, though.
+- `sort`: animals are now sortable by race on the assignment screens
+- `createitem`: support creating items inside of bags
+
+## API
+- ``Items::getValue()``: remove ``caravan_buying`` parameter since the identity of the selling party doesn't actually affect the item value
+- `RemoteFortressReader`: add a ``force_reload`` option to the GetBlockList RPC API to return blocks regardless of whether they have changed since the last request
+- ``Units``: new animal propery check functions ``isMarkedForTraining(unit)``, ``isMarkedForTaming(unit)``, ``isMarkedForWarTraining(unit)``, and ``isMarkedForHuntTraining(unit)``
+- ``Gui``: ``getAnyStockpile`` and ``getAnyCivzone`` (along with their ``getSelected`` variants) now work through layers of ZScreens. This means that they will still return valid results even if a DFHack tool window is in the foereground.
+
+## Lua
+- ``new()``: improved error handling so that certain errors that were previously uncatchable (creating objects with members with unknown vtables) are now catchable with ``pcall()``
+- ``dfhack.items.getValue()``: remove ``caravan_buying`` param as per C++ API change
+- ``widgets.BannerPanel``: panel with distinctive border for marking DFHack UI elements on otherwise vanilla screens
+- ``widgets.Panel``: new functions to override instead of setting corresponding properties (useful when subclassing instead of just setting attributes): ``onDragBegin``, ``onDragEnd``, ``onResizeBegin``, ``onResizeEnd``
+- ``dfhack.screen.readTile()``: now populates extended tile property fields (like ``top_of_text``) in the returned ``Pen`` object
+- ``dfhack.units``: new animal propery check functions ``isMarkedForTraining(unit)``, ``isMarkedForTaming(unit)``, ``isMarkedForWarTraining(unit)``, and ``isMarkedForHuntTraining(unit)``
+- ``dfhack.gui``: new ``getAnyCivZone`` and ``getAnyStockpile`` functions; also behavior of ``getSelectedCivZone`` and ``getSelectedStockpile`` functions has changes as per the related API notes
+
+# 50.09-r2
+
## New Plugins
+- `3dveins`: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels
+- `zone`: new searchable, sortable, filterable screen for assigning units to pastures
+- `dwarfvet`: reinstated and updated for v50's new hospital mechanics; allow your animals to have their wounds treated at hospitals
+- `dig`: new ``dig.asciiwarmdamp`` overlay that highlights warm and damp tiles when in ASCII mode. there is no effect in graphics mode since the tiles are already highlighted there
## Fixes
-- RemoteServer: fix accept continue to accept connections as long as the listening socket is valid and logs errors
+- Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing
+- `logistics`: don't autotrain domestic animals brought by invaders (they'll get attacked by friendly creatures as soon as you let them out of their cage)
+- `logistics`: don't bring trade goods to depot if the only caravans present are tribute caravans
+- `gui/create-item`: when choosing a citizen to create the chosen items, avoid choosing a dead citizen
+- `logistics`: fix potential crash when removing stockpiles or turning off stockpile features
## Misc Improvements
+- `stockpiles`: include exotic pets in the "tameable" filter
+- `logistics`: bring an autotraded bin to the depot if any item inside is tradeable instead of marking all items within the bin as untradeable if any individual item is untradeable
+- `autonick`: add more variety to nicknames based on famous literary dwarves
+- ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V
+- Suppress DF keyboard events when a DFHack keybinding is matched. This prevents, for example, a backtick from appearing in a textbox as text when you launch `gui/launcher` from the backtick keybinding.
+- Dreamfort: give noble suites double-thick walls and add apartment doors
## Documentation
+- `misery`: rewrite the documentation to clarify the actual effects of the plugin
## API
+- ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role
+- ``Items::markForTrade()``, ``Items::isRequestedTradeGood()``, ``Items::getValue``: see Lua notes below
## Internals
+- Price calculations fixed for many item types
## Lua
+- ``dfhack.units.getUnitByNobleRole``, ``dfhack.units.getUnitsByNobleRole``: unit lookup API by role
+- ``dfhack.items.markForTrade``: mark items for trade
+- ``dfhack.items.isRequestedTradeGood``: discover whether an item is named in a trade agreement with an active caravan
+- ``dfhack.items.getValue``: gained optional ``caravan`` and ``caravan_buying`` parameters for prices that take trader races and agreements into account
+- ``widgets.TextButton``: wraps a ``HotkeyLabel`` and decorates it to look more like a button
-## Removed
+# 50.09-r1
+
+## Internals
+
+- Core: update SDL interface from SDL1 to SDL2
# 50.08-r4
diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst
index d07cb045e..fa7094b40 100644
--- a/docs/dev/Lua API.rst
+++ b/docs/dev/Lua API.rst
@@ -306,7 +306,32 @@ All types and the global object have the following features:
* ``type._identity``
Contains a lightuserdata pointing to the underlying
- ``DFHack::type_instance`` object.
+ ``DFHack::type_identity`` object.
+
+All compound types (structs, classes, unions, and the global object) support:
+
+* ``type._fields``
+
+ Contains a table mapping field names to descriptions of the type's fields,
+ including data members and functions. Iterating with ``pairs()`` returns data
+ fields in the order they are defined in the type. Functions and globals may
+ appear in an arbitrary order.
+
+ Each entry contains the following fields:
+
+ * ``name``: the name of the field (matches the ``_fields`` table key)
+ * ``offset``: for data members, the position of the field relative to the start of the type, in bytes
+ * ``count``: for arrays, the number of elements
+ * ``mode``: implementation detail. See ``struct_field_info::Mode`` in ``DataDefs.h``.
+
+ Each entry may also contain the following fields, depending on its type:
+
+ * ``type_name``: present for most fields; a string representation of the field's type
+ * ``type``: the type object matching the field's type; present if such an object exists
+ (e.g. present for DF types, absent for primitive types)
+ * ``type_identity``: present for most fields; a lightuserdata pointing to the field's underlying ``DFHack::type_identity`` object
+ * ``index_enum``, ``ref_target``: the type object corresponding to the field's similarly-named XML attribute, if present
+ * ``union_tag_field``, ``union_tag_attr``, ``original_name``: the string value of the field's similarly-named XML attribute, if present
Types excluding the global object also support:
@@ -659,7 +684,7 @@ Persistent configuration storage
--------------------------------
This api is intended for storing configuration options in the world itself.
-It probably should be restricted to data that is world-dependent.
+It is intended for data that is world-dependent.
Entries are identified by a string ``key``, but it is also possible to manage
multiple entries with the same key; their identity is determined by ``entry_id``.
@@ -692,10 +717,8 @@ Every entry has a mutable string ``value``, and an array of 7 mutable ``ints``.
otherwise the existing one is simply updated.
Returns *entry, did_create_new*
-Since the data is hidden in data structures owned by the DF world,
-and automatically stored in the save game, these save and retrieval
-functions can just copy values in memory without doing any actual I/O.
-However, currently every entry has a 180+-byte dead-weight overhead.
+The data is kept in memory, so no I/O occurs when getting or saving keys. It is
+all written to a json file in the game save directory when the game is saved.
It is also possible to associate one bit per map tile with an entry,
using these two methods:
@@ -985,41 +1008,26 @@ General-purpose selections
~~~~~~~~~~~~~~~~~~~~~~~~~~
* ``dfhack.gui.getSelectedWorkshopJob([silent])``
-
- When a job is selected in :kbd:`q` mode, returns the job, else
- prints error unless silent and returns *nil*.
-
* ``dfhack.gui.getSelectedJob([silent])``
-
- Returns the job selected in a workshop or unit/jobs screen.
-
* ``dfhack.gui.getSelectedUnit([silent])``
-
- Returns the unit selected via :kbd:`v`, :kbd:`k`, unit/jobs, or
- a full-screen item view of a cage or suchlike.
-
* ``dfhack.gui.getSelectedItem([silent])``
-
- Returns the item selected via :kbd:`v` ->inventory, :kbd:`k`, :kbd:`t`, or
- a full-screen item view of a container. Note that in the
- last case, the highlighted *contained item* is returned, not
- the container itself.
-
* ``dfhack.gui.getSelectedBuilding([silent])``
-
- Returns the building selected via :kbd:`q`, :kbd:`t`, :kbd:`k` or :kbd:`i`.
-
* ``dfhack.gui.getSelectedCivZone([silent])``
-
- Returns the zone currently selected via :kbd:`z`
-
+* ``dfhack.gui.getSelectedStockpile([silent])``
* ``dfhack.gui.getSelectedPlant([silent])``
- Returns the plant selected via :kbd:`k`.
+ Returns the currently selected in-game object or the indicated thing
+ associated with the selected in-game object. For example, Calling
+ ``getSelectedJob`` when a building is selected will return the job associated
+ with the building (e.g. the ``ConstructBuilding`` job). If ``silent`` is
+ ommitted or set to ``false`` and a selected object cannot be found, then an
+ error is printed to the console.
* ``dfhack.gui.getAnyUnit(screen)``
* ``dfhack.gui.getAnyItem(screen)``
* ``dfhack.gui.getAnyBuilding(screen)``
+* ``dfhack.gui.getAnyCivZone(screen)``
+* ``dfhack.gui.getAnyStockpile(screen)``
* ``dfhack.gui.getAnyPlant(screen)``
Similar to the corresponding ``getSelected`` functions, but operate on the
@@ -1045,11 +1053,13 @@ Fortress mode
Same as ``resetDwarfmodeView``, but also recenter if position is valid. If ``pause`` is false, skip pausing. Respects
``RECENTER_INTERFACE_SHUTDOWN_MS`` in DF's ``init.txt`` (the delay before input is recognized when a recenter occurs.)
-* ``dfhack.gui.revealInDwarfmodeMap(pos[,center])``
- ``dfhack.gui.revealInDwarfmodeMap(x,y,z[,center])``
+* ``dfhack.gui.revealInDwarfmodeMap(pos[,center[,highlight]])``
+ ``dfhack.gui.revealInDwarfmodeMap(x,y,z[,center[,highlight]])``
- Centers the view on the given coordinates. If ``center`` is true, make sure the
- position is in the exact center of the view, else just bring it on screen.
+ Centers the view on the given coordinates. If ``center`` is true, make sure
+ the position is in the exact center of the view, else just bring it on screen.
+ If ``highlight`` is true, then mark the target tile with a pulsing highlight
+ until the player clicks somewhere else.
``pos`` can be a ``df.coord`` instance or a table assignable to a ``df.coord`` (see `lua-api-table-assignment`),
e.g.::
@@ -1130,10 +1140,12 @@ Announcements
If you want a guaranteed announcement without parsing, use ``dfhack.gui.showAutoAnnouncement`` instead.
-* ``dfhack.gui.getMousePos()``
+* ``dfhack.gui.getMousePos([allow_out_of_bounds])``
Returns the map coordinates of the map tile the mouse is over as a table of
- ``{x, y, z}``. If the cursor is not over the map, returns ``nil``.
+ ``{x, y, z}``. If the cursor is not over a valid tile, returns ``nil``. To
+ allow the function to return coordinates outside of the map, set
+ ``allow_out_of_bounds`` to ``true``.
Other
~~~~~
@@ -1355,11 +1367,16 @@ Units module
* ``dfhack.units.isTame(unit)``
* ``dfhack.units.isTamable(unit)``
* ``dfhack.units.isDomesticated(unit)``
+* ``dfhack.units.isMarkedForTraining(unit)``
+* ``dfhack.units.isMarkedForTaming(unit)``
+* ``dfhack.units.isMarkedForWarTraining(unit)``
+* ``dfhack.units.isMarkedForHuntTraining(unit)``
* ``dfhack.units.isMarkedForSlaughter(unit)``
* ``dfhack.units.isMarkedForGelding(unit)``
* ``dfhack.units.isGeldable(unit)``
* ``dfhack.units.isGelded(unit)``
* ``dfhack.units.isEggLayer(unit)``
+* ``dfhack.units.isEggLayerRace(unit)``
* ``dfhack.units.isGrazer(unit)``
* ``dfhack.units.isMilkable(unit)``
@@ -1438,10 +1455,22 @@ Units module
Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to
the arguments required by this function.
+* ``dfhack.units.getUnitByNobleRole(role_name)``
+
+ Returns the unit assigned to the given noble role, if any. ``role_name`` must
+ be one of the position codes associated with the active fort or civilization
+ government. For example: ``CAPTAIN_OF_THE_GUARD``, ``MAYOR``, or ``BARON``.
+ Note that if more than one unit has the role, only the first will be
+ returned. See ``getUnitsByNobleRole`` below for retrieving all units with a
+ particular role.
+
+* ``dfhack.units.getUnitsByNobleRole(role_name)``
+
+ Returns a list of units (possibly empty) assigned to the given noble role.
+
* ``dfhack.units.getCitizens([ignore_sanity])``
- Returns a table (list) of all citizens, which you would otherwise have to loop over all
- units in world and test against ``isCitizen()`` to discover.
+ Returns a list of all living citizens.
* ``dfhack.units.teleport(unit, pos)``
@@ -1487,6 +1516,11 @@ Units module
Computes the effective attribute value, including curse effect.
+* ``dfhack.units.casteFlagSet(race, caste, flag)``
+
+ Returns whether the given ``df.caste_raw_flags`` flag is set for the given
+ race and caste.
+
* ``dfhack.units.getMiscTrait(unit, type[, create])``
Finds (or creates if requested) a misc trait object with the given id.
@@ -1576,6 +1610,12 @@ Units module
Currently only one dream per unit is supported by Dwarf Fortress.
Support for multiple dreams may be added in future versions of Dwarf Fortress.
+* ``dfhack.units.getReadableName(unit)``
+
+ Returns a string that includes the language name of the unit (if any), the
+ race of the unit, whether it is trained for war or hunting, and any
+ syndrome-given descriptions (such as "necromancer").
+
* ``dfhack.units.getStressCategory(unit)``
Returns a number from 0-6 indicating stress. 0 is most stressed; 6 is least.
@@ -1755,9 +1795,17 @@ Items module
Calculates the base value for an item of the specified type and material.
-* ``dfhack.items.getValue(item)``
+* ``dfhack.items.getValue(item[, caravan_state])``
+
+ Calculates the value of an item. If a ``df.caravan_state`` object is given
+ (from ``df.global.plotinfo.caravans`` or
+ ``df.global.main_interface.trade.mer``), then the value is modified by civ
+ properties and any trade agreements that might be in effect.
- Calculates the Basic Value of an item, as seen in the View Item screen.
+* ``dfhack.items.isRequestedTradeGood(item[, caravan_state])``
+
+ Returns whether a caravan will pay extra for the given item. If caravan_state
+ is not given, checks all active caravans.
* ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)``
@@ -1773,7 +1821,16 @@ Items module
* ``dfhack.items.canTradeWithContents(item)``
- Checks whether the item and all items it contains, if any, can be traded.
+ Returns false if the item or any contained items cannot be traded.
+
+* ``canTradeAnyWithContents(item)``
+
+ Returns true if the item is empty and can be traded or if the item contains
+ any item that can be traded.
+
+* ``dfhack.items.markForTrade(item, depot)``
+
+ Marks the given item for trade at the given depot.
* ``dfhack.items.isRouteVehicle(item)``
@@ -1857,10 +1914,11 @@ Maps module
Returns the plant struct that owns the tile at the specified position.
-* ``dfhack.maps.canWalkBetween(pos1, pos2)``
+* ``dfhack.maps.getWalkableGroup(pos)``
- Checks if a dwarf may be able to walk between the two tiles,
- using a pathfinding cache maintained by the game.
+ Returns the walkability group for the given tile position. A return value of
+ ``0`` indicates that the tile is not walkable. The data comes from a
+ pathfinding cache maintained by DF.
.. note::
This cache is only updated when the game is unpaused, and thus
@@ -1869,6 +1927,10 @@ Maps module
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.
+* ``dfhack.maps.canWalkBetween(pos1, pos2)``
+
+ Checks if both positions are walkable and also share a walkability group.
+
* ``dfhack.maps.hasTileAssignment(tilemask)``
Checks if the tile_bitmask object is not *nil* and contains any set bits; returns *true* or *false*.
@@ -1889,9 +1951,11 @@ Maps module
Burrows module
--------------
-* ``dfhack.burrows.findByName(name)``
+* ``dfhack.burrows.findByName(name[, ignore_final_plus])``
- Returns the burrow pointer or *nil*.
+ Returns the burrow pointer or *nil*. if ``ignore_final_plus`` is ``true``,
+ then ``+`` characters at the end of the names are ignored, both for the
+ specified ``name`` and the names of the burrows that it matches against.
* ``dfhack.burrows.clearUnits(burrow)``
@@ -2474,10 +2538,10 @@ Supported callbacks and fields are:
Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience.
``_MOUSE_L, _MOUSE_R, _MOUSE_M``
- If the left, right, and/or middle mouse button is being pressed.
+ If the left, right, and/or middle mouse button was just pressed.
``_MOUSE_L_DOWN, _MOUSE_R_DOWN, _MOUSE_M_DOWN``
- If the left, right, and/or middle mouse button was just pressed.
+ If the left, right, and/or middle mouse button is being held down.
If this method is omitted, the screen is dismissed on reception of the ``LEAVESCREEN`` key.
@@ -2527,6 +2591,67 @@ a ``dfhack.penarray`` instance to cache their output.
``bufferx`` and ``buffery`` default to 0.
+
+Textures module
+---------------
+
+In order for the game to render a particular tile (graphic), it needs to know the
+``texpos`` - the position in the vector of the registered game textures (also the
+graphical tile id passed as the ``tile`` field in a `Pen `).
+Adding new textures to the vector is not difficult, but the game periodically
+deletes textures that are in the vector, and that's a problem since it
+invalidates the ``texpos`` value that used to point to that texture.
+The ``textures`` module solves this problem by providing a stable handle instead of a
+raw ``texpos``. When we need to draw a particular tile, we can look up the current
+``texpos`` value via the handle.
+Texture module can register textures in two ways: to reserved and dynamic ranges.
+Reserved range is a limit buffer in a game texture vector, that will never be wiped.
+It is good for static assets, which need to be loaded at the very beginning and will be used during the process running.
+In other cases, it is better to use dynamic range.
+If reserved range buffer limit has been reached, dynamic range will be used by default.
+
+* ``loadTileset(file, tile_px_w, tile_px_h[, reserved])``
+
+ Loads a tileset from the image ``file`` with give tile dimensions in pixels. The
+ image will be sliced in row major order. Returns an array of ``TexposHandle``.
+ ``reserved`` is optional boolean argument, which indicates texpos range.
+ ``true`` - reserved, ``false`` - dynamic (default).
+
+ Example usage::
+
+ local logo_textures = dfhack.textures.loadTileset('hack/data/art/dfhack.png', 8, 12)
+ local first_texposhandle = logo_textures[1]
+
+* ``getTexposByHandle(handle)``
+
+ Get the current ``texpos`` for the given ``TexposHandle``. Always use this method to
+ get the ``texpos`` for your texture. ``texpos`` can change when game textures are
+ reset, but the handle will be the same.
+
+* ``createTile(pixels, tile_px_w, tile_px_h[, reserved])``
+
+ Create and register a new texture with the given tile dimensions and an array of
+ ``pixels`` in row major order. Each pixel is an integer representing color in packed
+ RBGA format (for example, #0022FF11). Returns a ``TexposHandle``.
+ ``reserved`` is optional boolean argument, which indicates texpos range.
+ ``true`` - reserved, ``false`` - dynamic (default).
+
+* ``createTileset(pixels, texture_px_w, texture_px_h, tile_px_w, tile_px_h[, reserved])``
+
+ Create and register a new texture with the given texture dimensions and an array of
+ ``pixels`` in row major order. Then slice it into tiles with the given tile
+ dimensions. Each pixel is an integer representing color in packed RBGA format (for
+ example #0022FF11). Returns an array of ``TexposHandle``.
+ ``reserved`` is optional boolean argument, which indicates texpos range.
+ ``true`` - reserved, ``false`` - dynamic (default).
+
+* ``deleteHandle(handle)``
+
+ ``handle`` here can be single ``TexposHandle`` or an array of ``TexposHandle``.
+ Deletes all metadata and texture(s) related to the given handle(s). The handles
+ become invalid after this call.
+
+
Filesystem module
-----------------
@@ -2697,6 +2822,11 @@ and are only documented here for completeness:
The oldval, newval or delta arguments may be used to specify additional constraints.
Returns: *found_index*, or *nil* if end reached.
+* ``dfhack.internal.cxxDemangle(mangled_name)``
+
+ Decodes a mangled C++ symbol name. Returns the demangled name on success, or
+ ``nil, error_message`` on failure.
+
* ``dfhack.internal.getDir(path)``
Lists files/directories in a directory.
@@ -2815,6 +2945,19 @@ and are only documented here for completeness:
Returns 0 if the address is not found.
Requires a heap snapshot.
+* ``dfhack.internal.getClipboardTextCp437()``
+
+ Gets the system clipboard text (and converts text to CP437 encoding).
+
+* ``dfhack.internal.setClipboardTextCp437(text)``
+
+ Sets the system clipboard text from a CP437 string.
+
+* ``dfhack.internal.getSuppressDuplicateKeyboardEvents()``
+* ``dfhack.internal.setSuppressDuplicateKeyboardEvents(suppress)``
+
+ Gets and sets the flag for whether to suppress DF key events when a DFHack
+ keybinding is matched and a command is launched.
.. _lua-core-context:
@@ -2950,6 +3093,9 @@ environment by the mandatory init file dfhack.lua:
COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED,
COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE
+ ``COLOR_GREY`` and ``COLOR_DARKGREY`` can also be spelled ``COLOR_GRAY`` and
+ ``COLOR_DARKGRAY``.
+
* State change event codes, used by ``dfhack.onStateChange``
Available only in the `core context `, as is the event itself:
@@ -3206,6 +3352,20 @@ utils
Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``.
+* ``utils.search_text(text,search_tokens)``
+
+ Returns true if all the search tokens are found within ``text``. The text and
+ search tokens are normalized to lower case and special characters (e.g. ``A``
+ with a circle on it) are converted to their "basic" forms (e.g. ``a``).
+ ``search_tokens`` can be a string or a table of strings. If it is a string,
+ it is split into space-separated tokens before matching. The search tokens
+ are treated literally, so any special regular expression characters do not
+ need to be escaped. If ``utils.FILTER_FULL_TEXT`` is ``true``, then the
+ search tokens can match any part of ``text``. If it is ``false``, then the
+ matches must happen at the beginning of words within ``text``. You can change
+ the value of ``utils.FILTER_FULL_TEXT`` in `gui/control-panel` on the
+ "Preferences" tab.
+
* ``utils.call_with_string(obj,methodname,...)``
Allocates a temporary string object, calls ``obj:method(tmp,...)``, and
@@ -3416,6 +3576,13 @@ parameters.
``tonumber(arg)``. If ``arg_name`` is specified, it is used to make error
messages more useful.
+* ``argparse.boolean(arg, arg_name)``
+
+ Converts ``string.lower(arg)`` from "yes/no/on/off/true/false/etc..." to a lua
+ boolean. Throws if the value can't be converted, otherwise returns
+ ``true``/``false``. If ``arg_name`` is specified, it is used to make error
+ messages more useful.
+
dumper
======
@@ -3812,6 +3979,14 @@ Misc
of keycodes to *true* or *false*. For instance, it is possible to use the
table passed as argument to ``onInput``.
+ You can send mouse clicks as will by setting the ``_MOUSE_L`` key or other
+ mouse-related pseudo-keys documented with the ``screen:onInput(keys)``
+ function above. Note that if you are simulating a click at a specific spot on
+ the screen, you must set ``df.global.gps.mouse_x`` and
+ ``df.global.gps.mouse_y`` if you are clicking on the interface layer or
+ ``df.global.gps.precise_mouse_x`` and ``df.global.gps.precise_mouse_y`` if
+ you are clicking on the map.
+
* ``mkdims_xy(x1,y1,x2,y2)``
Returns a table containing the arguments as fields, and also ``width`` and
@@ -4412,6 +4587,10 @@ There are the following predefined frame style tables:
A frame suitable for overlay widget panels.
+* ``FRAME_THIN``
+
+ A frame suitable for floating tooltip panels that need the DFHack signature.
+
* ``FRAME_BOLD``
A frame suitable for a non-draggable panel meant to capture the user's focus,
@@ -4511,7 +4690,7 @@ Has attributes:
* ``drag_anchors = {}`` (default: ``{title=true, frame=false/true, body=true}``)
* ``drag_bound = 'frame' or 'body'`` (default: ``'frame'``)
* ``on_drag_begin = function()`` (default: ``nil``)
-* ``on_drag_end = function(bool)`` (default: ``nil``)
+* ``on_drag_end = function(success, new_frame)`` (default: ``nil``)
If ``draggable`` is set to ``true``, then the above attributes come into play
when the panel is dragged around the screen, either with the mouse or the
@@ -4525,13 +4704,15 @@ Has attributes:
otherwise. Dragging can be canceled by right clicking while dragging with the
mouse, hitting :kbd:`Esc` (while dragging with the mouse or keyboard), or by
calling ``Panel:setKeyboaredDragEnabled(false)`` (while dragging with the
- keyboard).
+ keyboard). If it is more convenient to do so, you can choose to override the
+ ``panel:onDragBegin`` and/or the ``panel:onDragEnd`` methods instead of
+ setting the ``on_drag_begin`` and/or ``on_drag_end`` attributes.
* ``resizable = bool`` (default: ``false``)
* ``resize_anchors = {}`` (default: ``{t=false, l=true, r=true, b=true}``
* ``resize_min = {}`` (default: w and h from the ``frame``, or ``{w=5, h=5}``)
* ``on_resize_begin = function()`` (default: ``nil``)
-* ``on_resize_end = function(bool)`` (default: ``nil``)
+* ``on_resize_end = function(success, new_frame)`` (default: ``nil``)
If ``resizable`` is set to ``true``, then the player can click the mouse on
any edge specified in ``resize_anchors`` and drag the border to resize the
@@ -4545,6 +4726,9 @@ Has attributes:
Dragging can be canceled by right clicking while resizing with the mouse,
hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling
``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard).
+ If it is more convenient to do so, you can choose to override the
+ ``panel:onResizeBegin`` and/or the ``panel:onResizeEnd`` methods instead of
+ setting the ``on_resize_begin`` and/or ``on_resize_end`` attributes.
* ``autoarrange_subviews = bool`` (default: ``false``)
* ``autoarrange_gap = int`` (default: ``0``)
@@ -4589,6 +4773,15 @@ Has functions:
commit the new window size or :kbd:`Esc` to cancel. If resizing is canceled,
then the window size from before the resize operation is restored.
+* ``panel:onDragBegin()``
+* ``panel:onDragEnd(success, new_frame)``
+* ``panel:onResizeBegin()``
+* ``panel:onResizeEnd(success, new_frame)``
+
+The default implementations of these methods call the associated attribute (if
+set). You can override them in a subclass if that is more convenient than
+setting the attributes.
+
Double clicking:
If the panel is resizable and the user double-clicks on the top edge (the frame
@@ -4688,6 +4881,12 @@ following keyboard hotkeys:
- Ctrl-B/Ctrl-F: move the cursor one word back or forward.
- Ctrl-A/Ctrl-E: move the cursor to the beginning/end of the text.
+The widget also supports integration with the system clipboard:
+
+- Ctrl-C: copy current text to the system clipboard
+- Ctrl-X: copy current text to the system clipboard and clear text in widget
+- Ctrl-V: paste text from the system clipboard (text is converted to cp437)
+
The ``EditField`` class also provides the following functions:
* ``editfield:setCursor([cursor_pos])``
@@ -5016,13 +5215,50 @@ The CycleHotkeyLabel widget implements the following methods:
selected option if no index is given. If an option was defined as just a
string, then this function will return ``nil`` for that option.
-ToggleHotkeyLabel
------------------
+ToggleHotkeyLabel class
+-----------------------
This is a specialized subclass of CycleHotkeyLabel that has two options:
``On`` (with a value of ``true``) and ``Off`` (with a value of ``false``). The
``On`` option is rendered in green.
+HelpButton class
+----------------
+
+A 3x1 tile button with a question mark on it, intended to represent a help
+icon. Clicking on the icon will launch `gui/launcher` with a given command
+string, showing the help text for that command.
+
+It has the following attributes:
+
+:command: The command to load in `gui/launcher`.
+
+ConfigureButton class
+---------------------
+
+A 3x1 tile button with a gear mark on it, intended to represent a configure
+icon. Clicking on the icon will run the given callback.
+
+It has the following attributes:
+
+:on_click: The function on run when the icon is clicked.
+
+BannerPanel class
+-----------------
+
+This is a Panel subclass that prints a distinctive banner along the far left
+and right columns of the widget frame. Note that this is not a "proper" frame
+since it doesn't have top or bottom borders. Subviews of this panel should
+inset their frames one tile from the left and right edges.
+
+TextButton class
+----------------
+
+This is a BannerPanel subclass that wraps a HotkeyLabel with some decorators on
+the sides to make it look more like a button, suitable for both graphics and
+ASCII modes. All HotkeyLabel parameters passed to the constructor are passed
+through to the wrapped HotkeyLabel.
+
List class
----------
@@ -5109,12 +5345,11 @@ FilteredList class
------------------
This widget combines List, EditField and Label into a combo-box like
-construction that allows filtering the list by subwords of its items.
+construction that allows filtering the list.
In addition to passing through all attributes supported by List, it
supports:
-:case_sensitive: If ``true``, matching is case sensitive. Defaults to ``false``.
:edit_pen: If specified, used instead of ``cursor_pen`` for the edit field.
:edit_below: If true, the edit field is placed below the list instead of above.
:edit_key: If specified, the edit field is disabled until this key is pressed.
@@ -5163,9 +5398,9 @@ Filter behavior:
By default, the filter matches substrings that start at the beginning of a word
(or after any punctuation). You can instead configure filters to match any
-substring with a command like::
+substring across the full text with a command like::
- :lua require('gui.widgets').FILTER_FULL_TEXT=true
+ :lua require('utils').FILTER_FULL_TEXT=true
TabBar class
------------
@@ -5219,6 +5454,31 @@ The parent widget owns the range values, and can control them independently (e.g
:on_left_change: Callback executed when moving the left handle.
:on_right_change: Callback executed when moving the right handle.
+
+gui.textures
+============
+
+This module contains convenience methods for accessing default DFHack graphic assets.
+Pass the ``offset`` in tiles (in row major position) to get a particular tile from the
+asset. ``offset`` 0 is the first tile.
+
+* ``tp_green_pin(offset)`` tileset: ``hack/data/art/green-pin.png``
+* ``tp_red_pin(offset)`` tileset: ``hack/data/art/red-pin.png``
+* ``tp_icons(offset)`` tileset: ``hack/data/art/icons.png``
+* ``tp_on_off(offset)`` tileset: ``hack/data/art/on-off.png``
+* ``tp_control_panel(offset)`` tileset: ``hack/data/art/control-panel.png``
+* ``tp_border_thin(offset)`` tileset: ``hack/data/art/border-thin.png``
+* ``tp_border_medium(offset)`` tileset: ``hack/data/art/border-medium.png``
+* ``tp_border_bold(offset)`` tileset: ``hack/data/art/border-bold.png``
+* ``tp_border_panel(offset)`` tileset: ``hack/data/art/border-panel.png``
+* ``tp_border_window(offset)`` tileset: ``hack/data/art/order-window.png``
+
+Example usage::
+
+ local textures = require('gui.textures')
+ local first_border_texpos = textures.tp_border_thin(1)
+
+
.. _lua-plugins:
=======
@@ -5356,51 +5616,6 @@ Native functions provided by the `buildingplan` plugin:
* ``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
-=======
-
-The `burrows` plugin implements extended burrow manipulations.
-
-Events:
-
-* ``onBurrowRename.foo = function(burrow)``
-
- Emitted when a burrow might have been renamed either through
- the game UI, or ``renameBurrow()``.
-
-* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)``
-
- Emitted when a tile might have been dug out. Only tracked if the
- auto-growing burrows feature is enabled.
-
-Native functions:
-
-* ``renameBurrow(burrow,name)``
-
- Renames the burrow, emitting ``onBurrowRename`` and updating auto-grow state properly.
-
-* ``findByName(burrow,name)``
-
- Finds a burrow by name, using the same rules as the plugin command line interface.
- Namely, trailing ``'+'`` characters marking auto-grow burrows are ignored.
-
-* ``copyUnits(target,source,enable)``
-
- Applies units from ``source`` burrow to ``target``. The ``enable``
- parameter specifies if they are to be added or removed.
-
-* ``copyTiles(target,source,enable)``
-
- Applies tiles from ``source`` burrow to ``target``. The ``enable``
- parameter specifies if they are to be added or removed.
-
-* ``setTilesByKeyword(target,keyword,enable)``
-
- Adds or removes tiles matching a predefined keyword. The keyword
- set is the same as used by the command line.
-
-The lua module file also re-exports functions from ``dfhack.burrows``.
-
.. _cxxrandom-api:
cxxrandom
diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst
index 22b9a7b1a..5e605e391 100644
--- a/docs/dev/compile/Compile.rst
+++ b/docs/dev/compile/Compile.rst
@@ -88,9 +88,9 @@ assistance.
All Platforms
=============
-Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this,
-but from command line is the usual way to do this; thought the Windows section below points out some
-Windows batch files that can be used to avoid opening a terminal/command-prompt.
+Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this
+for you, but it's more common to do it from the command line. Windows developers can refer to the
+Windows section below for batch files that can be used to avoid opening a terminal/command-prompt.
You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See
the `build-options` page for help finding the DFHack build options relevant to you.
diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst
index 4ec226d30..b5b6cf0e3 100644
--- a/docs/dev/overlay-dev-guide.rst
+++ b/docs/dev/overlay-dev-guide.rst
@@ -90,6 +90,10 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes:
This will be filled in with the display name of your widget, in case you
have multiple widgets with the same implementation but different
configurations.
+- ``version``
+ You can set this to any string. If the version string of a loaded widget
+ does not match the saved settings for that widget, then the configuration
+ for the widget (position, enabled status) will be reset to defaults.
- ``default_pos`` (default: ``{x=-2, y=-2}``)
Override this attribute with your desired default widget position. See
the `overlay` docs for information on what positive and negative numbers
@@ -131,7 +135,10 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes:
seconds) that your widget can take to react to changes in information and
not annoy the player. Set to 0 to be called at the maximum rate. Be aware
that running more often than you really need to will impact game FPS,
- especially if your widget can run while the game is unpaused.
+ especially if your widget can run while the game is unpaused. If you change
+ the value of this attribute dynamically, it may not be noticed until the
+ previous timeout expires. However, if you need a burst of high-frequency
+ updates, set it to ``0`` and it will be noticed immediately.
Registering a widget with the overlay framework
***********************************************
diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst
index 38117503c..297e8482a 100644
--- a/docs/guides/modding-guide.rst
+++ b/docs/guides/modding-guide.rst
@@ -45,6 +45,7 @@ this::
info.txt
graphics/...
objects/...
+ blueprints/...
scripts_modactive/example-mod.lua
scripts_modactive/internal/example-mod/...
scripts_modinstalled/...
@@ -58,6 +59,9 @@ Let's go through that line by line.
- Modifications to the game raws (potentially with custom raw tokens) go in
the :file:`graphics/` and :file:`objects/` folders. You can read more about
the files that go in these directories on the :wiki:`Modding` wiki page.
+- Any `quickfort` blueprints included with your mod go in the
+ :file:`blueprints` folder. Note that your mod can *just* be blueprints and
+ nothing else if you like.
- A control script in :file:`scripts_modactive/` directory that handles
system-level event hooks (e.g. reloading state when a world is loaded),
registering `overlays `, and
diff --git a/docs/guides/quickfort-user-guide.rst b/docs/guides/quickfort-user-guide.rst
index 9dda52454..e4b337cc4 100644
--- a/docs/guides/quickfort-user-guide.rst
+++ b/docs/guides/quickfort-user-guide.rst
@@ -84,7 +84,7 @@ Feature summary
- Configurable zone/location settings, such as the pit/pond toggle or
hospital supply quantities
-- Build mode
+- Build mode
- Integrated with DFHack `buildingplan`: you can place buildings before
manufacturing building materials and you can use the `buildingplan` UI
@@ -108,6 +108,10 @@ Feature summary
- Set building properties (such as a name)
- Can attach and configure track stops as part of hauling routes
+- Burrow mode
+
+ - Supports creating, adding to, and subtracting from burrows.
+
Introduction to blueprints
--------------------------
@@ -866,6 +870,36 @@ names an existing route, the stop will be added to that route::
These two track stops (which do not dump their contents) simply exist on a
common route at the ends of a connected carved track.
+#burrow mode
+------------
+
+``#burrow`` mode can create, extend, and remove tiles from burrows.
+
+Burrow designation syntax
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The syntax should look familiar by now::
+
+ symbol{properties}(expansion)
+
+See the `#burrow mode reference`_ for symbol and property definitions.
+
+Here's how to create (or add to, if a burrow by that name already exists) a
+5x5 burrow named ``Inside+``. It will also register this burrow with
+`gui/civ-alert` if no burrow has yet been registered::
+
+ #burrow
+ a{create=true name=Inside+ civalert=true}(5x5)
+
+Why the trailing ``+``? That's to indicate to the `burrow` plugin that the
+burrow should grow as adjacent tiles are dug out.
+
+Similarly, here is how to erase a tile from all burrows that currently include
+it::
+
+ #burrow
+ e
+
.. _quickfort-modeline:
Modeline markers
@@ -1992,10 +2026,10 @@ All stockpiles support the following properties:
Property Description
================ ===========
``name`` the name of the stockpile
-``take_from`` comma-separated list of names of stockpiles or workshops that
- the stockpile takes from
-``give_to`` comma-separated list of names of stockpiles or workshops that
- the stockpile gives to
+``take_from`` comma-separated list of names or building ids of stockpiles
+ or workshops that the stockpile takes from
+``give_to`` comma-separated list of names or building ids of stockpiles
+ or workshops that the stockpile gives to
``links_only`` if set to ``true`` then the stockpile will only take from
links
``barrels`` the number of desired barrels
@@ -2014,6 +2048,12 @@ Property Description
feature
================ ===========
+Note that specifying building IDs in ``take_from`` or ``give_to`` lists is
+primarily useful when dynamically generating `quickfort` blueprints and
+applying them via the API. You will not generally know the ID of a stockpile or
+building when writing a blueprint by hand or when preparing a blueprint to
+apply in a different fort.
+
#build mode reference
~~~~~~~~~~~~~~~~~~~~~
@@ -2023,8 +2063,8 @@ accept the ``name`` property.
Moreover, all workshops and furnaces accept the ``max_general_orders``
property, which sets the maximum number of general workorders that the building
can accept, and the ``take_from`` and ``give_to`` properties, which are
-comma-separated lists of names (the same as the correponding stockpile
-properties above).
+comma-separated lists of names or building ids (the same as the correponding
+stockpile properties above).
================= ============================= ==========
Symbol Type Properties
@@ -2141,7 +2181,8 @@ Symbol Type Properties
route stop on this track stop
and make it take from the given
comma-separated list of
- stockpile names. ``route``: add
+ stockpile names or stockpile
+ building ids. ``route``: add
this route stop to the named
route. if no route of this name
exists, it will be created. If
@@ -2192,3 +2233,25 @@ Symbol Type Properties
``trackrampSEW`` track ramp tee to the S, E, W
``trackrampNSEW`` track ramp cross
================= ============================= ==========
+
+#burrow mode reference
+~~~~~~~~~~~~~~~~~~~~~~
+
+====== ======= ==========
+Symbol Meaning Properties
+====== ======= ==========
+``a`` add ``name``: if set, will add to an existing burrow of this name.
+ ``create``: if set to ``true``, will create a burrow with the
+ specified ``name`` if it doesn't already exist.
+ ``civalert``: if set to ``true``, will register this burrow with
+ `gui/civ-alert` if no burrow has already been registered.
+ ``autochop_clear``: if set to ``true``, register the burrow with
+ `autochop` so that all trees in the burrow are immediately
+ chopped down, regardless of how many logs are in stock.
+ ``autochop_chop``: if set to ``true``, register the burrow with
+ ``autochop`` so that woodcutting activity is constrained to this
+ burrow (and others marked for ``chop``).
+``e`` erase ``name``: if set, will only affect the first burrow of the given
+ name. if not set, will affect all burrows that cover the given
+ tiles.
+====== ======= ==========
diff --git a/docs/images/gm-editor.png b/docs/images/gm-editor.png
index 24546a710..117cb7031 100644
Binary files a/docs/images/gm-editor.png and b/docs/images/gm-editor.png differ
diff --git a/docs/images/search-stockpile.png b/docs/images/search-stockpile.png
deleted file mode 100644
index a0e837875..000000000
Binary files a/docs/images/search-stockpile.png and /dev/null differ
diff --git a/docs/images/search.png b/docs/images/search.png
deleted file mode 100644
index 384c3c533..000000000
Binary files a/docs/images/search.png and /dev/null differ
diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst
index 3112934ab..96fbcf836 100644
--- a/docs/plugins/3dveins.rst
+++ b/docs/plugins/3dveins.rst
@@ -3,7 +3,7 @@
.. dfhack-tool::
:summary: Rewrite layer veins to expand in 3D space.
- :tags: unavailable fort gameplay map
+ :tags: fort gameplay map
Existing, flat veins are removed and new 3D veins that naturally span z-levels
are generated in their place. The transformation preserves the mineral counts
diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst
index 7e5b6b024..4a0d64ae1 100644
--- a/docs/plugins/autobutcher.rst
+++ b/docs/plugins/autobutcher.rst
@@ -70,7 +70,7 @@ Usage
``autobutcher list_export``
Print commands required to set the current settings in another fort.
-To see a list of all races, run this command:
+To see a list of all races, run this command::
devel/query --table df.global.world.raws.creatures.all --search ^creature_id --maxdepth 1
diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst
index 6135b39d2..5beb7260e 100644
--- a/docs/plugins/autogems.rst
+++ b/docs/plugins/autogems.rst
@@ -3,7 +3,7 @@ autogems
.. dfhack-tool::
:summary: Automatically cut rough gems.
- :tags: unavailable fort auto workorders
+ :tags: unavailable
:no-command:
.. dfhack-command:: autogems-reload
diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst
index b45b23be4..ae38cb3d9 100644
--- a/docs/plugins/building-hacks.rst
+++ b/docs/plugins/building-hacks.rst
@@ -3,7 +3,7 @@ building-hacks
.. dfhack-tool::
:summary: Provides a Lua API for creating powered workshops.
- :tags: unavailable fort gameplay buildings
+ :tags: unavailable
:no-command:
See `building-hacks-api` for more details.
diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst
index 9cc7e68a2..b1e77a80d 100644
--- a/docs/plugins/buildingplan.rst
+++ b/docs/plugins/buildingplan.rst
@@ -19,11 +19,11 @@ periodically scan for appropriate items and attach them to the planned
building. Once all items are attached, the construction job will be unsuspended
and a dwarf will come and build the building. If you have the `unsuspend`
overlay enabled (it is enabled by default), then buildingplan-suspended
-buildings will appear with a ``P`` marker on the main map, as opposed to the
-usual ``x`` marker for "regular" suspended buildings. If you have
-`suspendmanager` running, then buildings will be left suspended when their
-items are all attached and ``suspendmanager`` will unsuspend them for
-construction when it is safe to do so.
+buildings will be tagged with a clock graphic in graphics mode or a ``P``
+marker in ASCII mode, as opposed to the ``x`` marker for "regular" suspended
+buildings. If you have `suspendmanager` running, then buildings will be left
+suspended when their items are all attached and ``suspendmanager`` will
+unsuspend them for construction when it is safe to do so.
If you want to impose restrictions on which items are chosen for the buildings,
buildingplan has full support for quality and material filters (see `below
@@ -45,10 +45,10 @@ from the buildingplan placement UI.
One way to integrate buildingplan into your gameplay is to create manager
workorders to ensure you always have a few blocks/doors/beds/etc. available. You
-can then place as many of each building as you like. Produced items will be used
-to build the planned buildings as they are produced, with minimal space
-dedicated to stockpiles. The DFHack `orders` library can help with setting
-these manager workorders up for you.
+can then place as many of each building as you like. Items will be used to
+build the planned buildings as they are produced, with minimal space dedicated
+to stockpiles. The DFHack `orders` library can help with setting these manager
+workorders up for you.
If you don't want to use the ``buildingplan`` interface for the building you're
currently trying to place, you can hit :kbd:`Alt`:kbd:`M` or click on the
@@ -125,16 +125,17 @@ tiles selected in the construction area are not appropriate for building. For
example, if you want to fill an area with flooring, you can select the entire
area, and any tiles with existing buildings or walls will simply be skipped.
-For weapon and spike traps, you can choose how many weapons will be included
-on this panel.
+Some building types will have other options available as well, such as a
+selector for how many weapons you want in weapon traps or whether you want your
+built cages to not have any occupants.
Setting quality and material filters
++++++++++++++++++++++++++++++++++++
If you want to set restrictions on the items chosen to complete the planned
-building, you can click on the "filter" button next to the item name or select
-the item with the :kbd:`*` and :kbd:`/` keys and hit :kbd:`f` to bring up the
-filter dialog.
+building, you can click on the "[any material]" link next to the item name or
+select the item with the :kbd:`q` or :kbd:`Q` keys and hit :kbd:`f` to bring up
+the filter dialog.
You can select whether the item must be decorated, and you can drag the ends of
the "Item quality" slider to set your desired quality range. Note that blocks,
@@ -147,32 +148,33 @@ You can click on specific materials to allow only items of those materials when
building the current type of building. You can also allow or disallow entire
categories of materials by clicking on the "Type" options on the left. Note
that it is perfectly fine to choose materials that currently show zero quantity.
-`buildingplan` will patiently watch for items made of materials you have
-selected.
+`buildingplan` will patiently wait for items made of materials you have
+selected to become available.
Choosing specific items
+++++++++++++++++++++++
-If you want to choose specific items, click on the "Choose from items" toggle
-or hit :kbd:`i` before placing the building. When you click to place the
-building, a dialog will come up that allows you choose which items to use. The
-list is sorted by most recently used materials for that building type by
-default, but you can change to sort by name or by available quantity by
-clicking on the "Sort by" selector or hitting :kbd:`R`. The configuration for
-whether you would like to choose specific items is saved per building type and
-will be restored when you plan more of that building type.
+If you want to choose specific items instead of using the filters, click on the
+"Choose items" selector or hit :kbd:`z` before placing the building. You can
+choose to be prompted for every item ("Manually") or you can have it
+automatically select the type of item that you last chose for this building
+type. The list you are prompted with is sorted by most recently used materials
+for that building type by default, but you can change to sort by name or by
+available quantity by clicking on the "Sort by" selector or hitting :kbd:`R`.
+The configuration for whether you would like to choose specific items is saved
+per building type and will be restored when you plan more of that building type.
You can select the maximum quantity of a specified item by clicking on the item
name or selecting it with the arrow keys and hitting :kbd:`Enter`. You can
instead select items one at a time by Ctrl-clicking (:kbd:`Shift`:kbd:`Right`)
to increment or Ctrl-Shift-clicking (:kbd:`Shift`:kbd:`Left`) to decrement.
-Once you are satisfied with your choices, click on the "Confirm" button or hit
+Once you are satisfied with your choices, click on the large green button or hit
:kbd:`C` to continue building. Note that you don't have to select all the items
that the building needs. Any remaining items will be automatically chosen from
-other available items (or future items if not all items are available yet). If
-there are multiple item types to choose for the current building, one dialog
-will appear per item type.
+other available items (or from items produced in the future if not all items
+are available yet). If there are multiple item types to choose for the current
+building, one dialog will appear per item type.
Building status
---------------
@@ -180,10 +182,18 @@ Building status
When viewing a planned building, a separate `overlay` widget appears on the
building info sheet, showing you which items have been attached and which items
are still pending. For a pending item, you can see its position in the
-fulfillment queue. If there is a particular building that you need built ASAP,
+fulfillment queue. You need to manufacture these items for them to be attached
+to the building. If there is a particular building that you need built ASAP,
you can click on the "make top priority" button (or hit :kbd:`Ctrl`:kbd:`T`) to
bump the items for this building to the front of their respective queues.
Note that each item type and filter configuration has its own queue, so even if
an item is in queue position 1, there may be other queues that snag the needed
item first.
+
+Lever linking
+-------------
+
+When linking levers, `buildingplan` extends the vanilla panel by offering
+control over which mechanisms are chosen for installation at the lever and at
+the target. Heat safety filters are provided for convenience.
diff --git a/docs/plugins/burrow.rst b/docs/plugins/burrow.rst
new file mode 100644
index 000000000..f84f02989
--- /dev/null
+++ b/docs/plugins/burrow.rst
@@ -0,0 +1,115 @@
+burrow
+======
+
+.. dfhack-tool::
+ :summary: Quickly adjust burrow tiles and units.
+ :tags: fort auto design productivity units
+
+This tool has two modes. When enabled, it monitors burrows with names that end
+in ``+``. If a wall at the edge of such a burrow is dug out, the burrow will be
+automatically extended to include the newly-revealed adjacent walls. If a miner
+digs into an open space, such as a cavern, the open space will *not* be
+included in the burrow.
+
+When run as a command, it can quickly adjust which tiles and/or units are
+associated with the burrow.
+
+Usage
+-----
+
+::
+
+ enable burrow
+ burrow tiles|units clear [ ...] []
+ burrow tiles|units set|add|remove [...] []
+ burrow tiles box-add|box-remove [] [] []
+ burrow tiles flood-add|flood-remove []
+
+The burrows can be referenced by name or by the internal numeric burrow ID. If
+referenced by name, the first burrow that matches the name (case sensitive)
+will be targeted. If a burrow name ends in ``+`` (to indicate that it should be
+auto-expanded), the final ``+`` does not need to be specified on the
+commandline.
+
+For ``set``, ``add``, or ``remove`` commands, instead of a burrow, you can
+specify one of the following all-caps keywords:
+
+- ``ABOVE_GROUND``
+- ``SUBTERRANEAN``
+- ``INSIDE``
+- ``OUTSIDE``
+- ``LIGHT``
+- ``DARK``
+- ``HIDDEN``
+- ``REVEALED``
+
+to add or remove tiles with the corresponding properties.
+
+Flood fill selects tiles spreading out from a starting tile if they:
+
+- match the inside/outside and hidden/revealed properties of the starting tile
+- match the walkability group of the starting tile OR (if the starting tile is
+ walkable) is adjacent to a tile with the same walkability group as the
+ starting tile
+
+When flood adding, the flood fill will also stop at any tiles that have already
+been added to the burrow. Similarly for flood removing, the flood will also
+stop at tiles that are not in the burrow.
+
+Examples
+--------
+
+``enable burrow``
+ Start monitoring burrows that have names ending in '+' and automatically
+ expand them when walls that border the burrows are dug out.
+``burrow tiles clear Safety``
+ Remove all tiles from the burrow named ``Safety`` (in preparation for
+ adding new tiles elsewhere, presumably).
+``burrow units clear Farmhouse Workshops``
+ Remove all units from the burrows named ``Farmhouse`` and ``Workshops``.
+``multicmd burrow tiles set Inside INSIDE; burrow tiles remove Inside HIDDEN``
+ Reset the burrow named ``Inside`` to include all the currently revealed,
+ interior tiles.
+``burrow units set "Core Fort" Peasants Skilled``
+ Clear all units from the burrow named ``Core Fort``, then add units
+ currently assigned to the ``Peasants`` and ``Skilled`` burrows.
+``burrow tiles box-add Safety 0,0,0``
+ Add all tiles to the burrow named ``Safety`` that are within the volume of
+ the box starting at coordinate 0, 0, 0 (the upper left corner of the bottom
+ level) and ending at the current location of the keyboard cursor.
+``burrow tiles flood-add Safety --cur-zlevel``
+ Flood-add the tiles on the current z-level with the same properties as the
+ tile under the keyboard cursor to the burrow named ``Safety``.
+
+Options
+-------
+
+``-c``, ``--cursor ``
+ Indicate the starting position of the box or flood fill. If not specified,
+ the position of the keyboard cursor is used.
+``-z``, ``--cur-zlevel``
+ Restricts a flood fill operation to the currently visible z-level.
+
+Note
+----
+
+If you are auto-expanding a burrow (whose name ends in a ``+``) and the miner
+who is digging to expand the burrow is assigned to that burrow, then 1-wide
+corridors that expand the burrow will have very slow progress. This is because
+the burrow is expanded to include the next dig job only after the miner has
+chosen a next tile to dig, which may be far away. 2-wide cooridors are much
+more efficient when expanding a burrow since the "next" tile to dig will still
+be nearby.
+
+Overlay
+-------
+
+When painting burrows in the vanilla UI, a few extra mouse operations are
+supported. If you box select across multiple z-levels, you will be able to
+select the entire volume instead of just the selected area on the z-level that
+you are currently looking at.
+
+In addition, double-clicking will start a flood fill from the target tile.
+
+The box and flood fill actions respect the UI setting for whether the burrow is
+being added to or erased.
diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst
deleted file mode 100644
index 299656cf5..000000000
--- a/docs/plugins/burrows.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-burrows
-=======
-
-.. dfhack-tool::
- :summary: Auto-expand burrows as you dig.
- :tags: unavailable fort auto design productivity map units
- :no-command:
-
-.. dfhack-command:: burrow
- :summary: Quickly add units/tiles to burrows.
-
-When a wall inside a burrow with a name ending in ``+`` is dug out, the burrow
-will be extended to newly-revealed adjacent walls.
-
-Usage
------
-
-``burrow enable auto-grow``
- When a wall inside a burrow with a name ending in '+' is dug out, the burrow
- will be extended to newly-revealed adjacent walls. This final '+' may be
- omitted in burrow name args of other ``burrow`` commands. Note that digging
- 1-wide corridors with the miner inside the burrow is SLOW.
-``burrow disable auto-grow``
- Disables auto-grow processing.
-``burrow clear-unit [ ...]``
- Remove all units from the named burrows.
-``burrow clear-tiles [ ...]``
- Remove all tiles from the named burrows.
-``burrow set-units target-burrow [ ...]``
- Clear all units from the target burrow, then add units from the named source
- burrows.
-``burrow add-units target-burrow [ ...]``
- Add units from the source burrows to the target.
-``burrow remove-units target-burrow [ ...]``
- Remove units in source burrows from the target.
-``burrow set-tiles target-burrow [ ...]``
- Clear target burrow tiles and 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 [ ...]``
- Remove tiles in source burrows from the target.
-
-In place of a source burrow, you can use one of the following keywords:
-
-- ``ABOVE_GROUND``
-- ``SUBTERRANEAN``
-- ``INSIDE``
-- ``OUTSIDE``
-- ``LIGHT``
-- ``DARK``
-- ``HIDDEN``
-- ``REVEALED``
-
-to add tiles with the given properties.
diff --git a/docs/plugins/design.rst b/docs/plugins/design.rst
new file mode 100644
index 000000000..3cb48343c
--- /dev/null
+++ b/docs/plugins/design.rst
@@ -0,0 +1,10 @@
+design
+======
+
+.. dfhack-tool::
+ :summary: Draws designations in shapes.
+ :tags: fort design dev map
+ :no-command:
+
+This plugin provides a Lua API, but no direct commands. See `gui/design` for
+the user interface.
diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst
index a10db97df..2055a7572 100644
--- a/docs/plugins/dig.rst
+++ b/docs/plugins/dig.rst
@@ -50,7 +50,7 @@ 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] [-z]``
+``digtype [] [-p] [--zup|-u] [--zdown|-zu] [--cur-zlevel|-z] [--hidden|-h] [--no-auto|-a]``
Designate all vein tiles of the same type as the selected tile. See the
`digtype`_ section below for options.
``digexp [] [] [-p]``
@@ -119,9 +119,11 @@ the last selected parameters.
digtype
-------
-For every tile on the map of the same vein type as the selected tile, this
-command designates it to have the same designation as the selected tile. If the
-selected tile has no designation, they will be dig designated.
+For every tile on the map of the same vein type as the selected tile, this command
+designates it to have the same designation as the selected tile. If the selected
+tile has no designation, they will be dig designated. By default, only designates
+visible tiles, and in the case of dig designation, applies automatic mining to them
+(designates uncovered neighbouring tiles of the same type to be dug).
If an argument is given, the designation of the selected tile is ignored, and
all appropriate tiles are set to the specified designation.
@@ -143,9 +145,18 @@ 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.
+Other options:
+
+``-d``, ``--zdown``
+ Only designates tiles on the cursor's z-level and below.
+``-u``, ``--zup``
+ Only designates tiles on the cursor's z-level and above.
+``-z``, ``--cur-zlevel``
+ Only designates tiles on the same z-level as the cursor.
+``-h``, ``--hidden``
+ Allows designation of hidden tiles, and picking a hidden tile as the target type.
+``-a``, ``--no-auto``
+ No automatic mining mode designation - useful if you want to avoid dwarves digging where you don't want them.
digexp
------
@@ -179,3 +190,23 @@ Filters:
Take current designation and apply the selected pattern to it.
After you have a pattern set, you can use ``expdig`` to apply it again.
+
+Overlay
+-------
+
+This tool also provides two overlays that are managed by the `overlay`
+framework. Both have no effect when in graphics mode, but when in ASCII mode,
+they display useful highlights that are otherwise missing from the ASCII mode
+interface.
+
+The ``dig.asciiwarmdamp`` overlay highlights warm tiles red and damp tiles in
+blue. Box selection characters and the keyboard cursor will also
+change color as appropriate when over the warm or damp tile.
+
+The ``dig.asciicarve`` overlay highlights tiles that are designated for
+smoothing, engraving, track carving, or fortification carving. The designations
+blink so you can still see what is underneath them.
+
+Note that due to the limitations of the ASCII mode screen buffer, the
+designation highlights may show through other interface elements that overlap
+the designated area.
diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst
index ee502371a..e98b65382 100644
--- a/docs/plugins/digFlood.rst
+++ b/docs/plugins/digFlood.rst
@@ -3,7 +3,7 @@ digFlood
.. dfhack-tool::
:summary: Digs out veins as they are discovered.
- :tags: unavailable fort auto map
+ :tags: unavailable
Once you register specific vein types, this tool will automatically designate
tiles of those types of veins for digging as your miners complete adjacent
diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst
index 001636709..46db313c6 100644
--- a/docs/plugins/diggingInvaders.rst
+++ b/docs/plugins/diggingInvaders.rst
@@ -3,7 +3,7 @@ diggingInvaders
.. dfhack-tool::
:summary: Invaders dig and destroy to get to your dwarves.
- :tags: unavailable fort gameplay military units
+ :tags: unavailable
Usage
-----
diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst
index 45a5d8f85..cd8867ee5 100644
--- a/docs/plugins/dwarfmonitor.rst
+++ b/docs/plugins/dwarfmonitor.rst
@@ -3,7 +3,7 @@ dwarfmonitor
.. dfhack-tool::
:summary: Report on dwarf preferences and efficiency.
- :tags: unavailable fort inspection jobs units
+ :tags: unavailable
It can also show heads-up display widgets with live fort statistics.
diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst
index b4dfe0ada..12d77fef8 100644
--- a/docs/plugins/dwarfvet.rst
+++ b/docs/plugins/dwarfvet.rst
@@ -2,22 +2,34 @@ dwarfvet
========
.. dfhack-tool::
- :summary: Allows animals to be treated at animal hospitals.
- :tags: unavailable fort gameplay animals
+ :summary: Allow animals to be treated at hospitals.
+ :tags: fort gameplay animals
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
+dwarfvet, injured animals will be treated at a hospital. Dwarfs with the Animal
+Caretaker labor enabled will come to the hospital to treat the animals. Normal
medical skills are used (and trained), but no experience is given to the Animal
Caretaker skill itself.
+You can enable ``dwarfvet`` in `gui/control-panel`, and you can choose to start
+``dwarfvet`` automatically in new forts in the ``Autostart`` tab.
+
Usage
-----
-``enable dwarfvet``
- Enables the plugin.
-``dwarfvet report``
- Reports all zones that the game considers animal hospitals.
-``dwarfvet report-usage``
- Reports on animals currently being treated.
+::
+
+ enable dwarfvet
+ dwarfvet [status]
+ dwarfvet now
+
+Examples
+--------
+
+``dwarfvet``
+ Report on how many animals are being treated and how many are in need of
+ treatment.
+
+``dwarfvet now``
+ Assign injured animals to a free floor spot in a nearby hospital,
+ regardless of whether the plugin is enabled.
diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst
index 282d4b122..88c4ffccd 100644
--- a/docs/plugins/embark-assistant.rst
+++ b/docs/plugins/embark-assistant.rst
@@ -3,7 +3,7 @@ embark-assistant
.. dfhack-tool::
:summary: Embark site selection support.
- :tags: unavailable embark fort interface
+ :tags: unavailable
Run this command while the pre-embark screen is displayed to show extended (and
reasonably correct) resource information for the embark rectangle as well as
diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst
index f320706ff..37a7bf43e 100644
--- a/docs/plugins/embark-tools.rst
+++ b/docs/plugins/embark-tools.rst
@@ -3,7 +3,7 @@ embark-tools
.. dfhack-tool::
:summary: Extend the embark screen functionality.
-
+ :tags: unavailable
Usage
-----
diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst
index 6ba01b712..5bf504fdc 100644
--- a/docs/plugins/fix-unit-occupancy.rst
+++ b/docs/plugins/fix-unit-occupancy.rst
@@ -3,7 +3,7 @@ fix-unit-occupancy
.. dfhack-tool::
:summary: Fix phantom unit occupancy issues.
- :tags: unavailable fort bugfix map
+ :tags: unavailable
If you see "unit blocking tile" messages that you can't account for
(:bug:`3499`), this tool can help.
diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst
index 552c5f8c3..49b2046b6 100644
--- a/docs/plugins/fixveins.rst
+++ b/docs/plugins/fixveins.rst
@@ -3,7 +3,7 @@ fixveins
.. dfhack-tool::
:summary: Restore missing mineral inclusions.
- :tags: unavailable fort bugfix map
+ :tags: unavailable
This tool can also remove invalid references to mineral inclusions if you broke
your embark with tools like `tiletypes`.
diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst
index e72f79ce0..21b3411a9 100644
--- a/docs/plugins/follow.rst
+++ b/docs/plugins/follow.rst
@@ -3,7 +3,7 @@ follow
.. dfhack-tool::
:summary: Make the screen follow the selected unit.
- :tags: unavailable fort interface units
+ :tags: unavailable
Once you exit from the current menu or cursor mode, the screen will stay
centered on the unit. Handy for watching dwarves running around. Deactivated by
diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst
index 2565d12c3..18661195a 100644
--- a/docs/plugins/forceequip.rst
+++ b/docs/plugins/forceequip.rst
@@ -3,7 +3,7 @@ forceequip
.. dfhack-tool::
:summary: Move items into a unit's inventory.
- :tags: unavailable adventure fort animals items military units
+ :tags: unavailable
This tool is typically used to equip specific clothing/armor items onto a dwarf,
but can also be used to put armor onto a war animal or to add unusual items
diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst
index ea386eacf..fc2734d07 100644
--- a/docs/plugins/generated-creature-renamer.rst
+++ b/docs/plugins/generated-creature-renamer.rst
@@ -3,7 +3,7 @@ generated-creature-renamer
.. dfhack-tool::
:summary: Automatically renames generated creatures.
- :tags: unavailable adventure fort legends units
+ :tags: unavailable
:no-command:
.. dfhack-command:: list-generated
diff --git a/docs/plugins/hotkeys.rst b/docs/plugins/hotkeys.rst
index 6b2ef3f0c..561a34382 100644
--- a/docs/plugins/hotkeys.rst
+++ b/docs/plugins/hotkeys.rst
@@ -19,14 +19,17 @@ Usage
Menu overlay widget
-------------------
-The in-game hotkeys menu is registered with the `overlay` framework and can be
-enabled as a hotspot in the upper-left corner of the screen. You can bring up
-the menu by hovering the mouse cursor over the hotspot and can select a command
-to run from the list by clicking on it with the mouse or by using the keyboard
-to select a command with the arrow keys and hitting :kbd:`Enter`.
+The in-game hotkeys menu is registered with the `overlay` framework and appears
+as a DFHack logo in the upper-left corner of the screen. You can bring up the
+menu by clicking on the logo or by hitting the global :kbd:`Ctrl`:kbd:`Shift`:kbd:`c` hotkey. You can select a command to run from
+the list by clicking on it with the mouse or by using the keyboard to select a
+command with the arrow keys and hitting :kbd:`Enter`.
+
+The menu closes automatically when an action is taken or when you click or
+right click anywhere else on the screen.
A short description of the command will appear in a nearby textbox. If you'd
like to see the full help text for the command or edit the command before
-running, you can open it for editing in `gui/launcher` by right clicking on the
+running, you can open it for editing in `gui/launcher` by shift clicking on the
command, left clicking on the arrow to the left of the command, or by pressing
the right arrow key while the command is selected.
diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst
index 789709c9f..0c14804af 100644
--- a/docs/plugins/infiniteSky.rst
+++ b/docs/plugins/infiniteSky.rst
@@ -3,7 +3,7 @@ infiniteSky
.. dfhack-tool::
:summary: Automatically allocate new z-levels of sky
- :tags: unavailable fort auto design map
+ :tags: unavailable
If enabled, this plugin will automatically allocate new z-levels of sky at the
top of the map as you build up. Or it can allocate one or many additional levels
diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst
index b792cb649..82748e36e 100644
--- a/docs/plugins/isoworldremote.rst
+++ b/docs/plugins/isoworldremote.rst
@@ -3,7 +3,7 @@ isoworldremote
.. dfhack-tool::
:summary: Provides a remote API used by Isoworld.
- :tags: unavailable dev graphics
+ :tags: unavailable
:no-command:
See `remote` for related remote APIs.
diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst
index 674c6897a..5e8125585 100644
--- a/docs/plugins/jobutils.rst
+++ b/docs/plugins/jobutils.rst
@@ -5,7 +5,7 @@ jobutils
.. dfhack-tool::
:summary: Provides commands for interacting with jobs.
- :tags: unavailable fort inspection jobs
+ :tags: unavailable
:no-command:
.. dfhack-command:: job
diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst
index 731f706b8..255de05c4 100644
--- a/docs/plugins/labormanager.rst
+++ b/docs/plugins/labormanager.rst
@@ -3,7 +3,7 @@ labormanager
.. dfhack-tool::
:summary: Automatically manage dwarf labors.
- :tags: unavailable fort auto labors
+ :tags: unavailable
Labormanager is derived from `autolabor` but uses a completely different
approach to assigning jobs to dwarves. While autolabor tries to keep as many
diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst
index dff142390..d9aeee067 100644
--- a/docs/plugins/logistics.rst
+++ b/docs/plugins/logistics.rst
@@ -17,6 +17,9 @@ For autotrade, items will be marked for trading only when a caravan is
approaching or is already at the trade depot. Items (or bins that contain
items) of which a noble has forbidden export will not be marked for trade.
+Stockpiles can be registered for ``logistics`` features by toggling the options
+in the `stockpiles` overlay that comes up when you select a stockpile in the UI.
+
Usage
-----
@@ -69,3 +72,7 @@ Options
Causes the command to act upon stockpiles with the given names or numbers
instead of the stockpile that is currently selected in the UI. Note that
the numbers are the stockpile numbers, not the building ids.
+``-m``, ``--melt-masterworks``
+ If specified with a ``logistics add melt`` command, will configure the
+ stockpile to allow melting of masterworks. By default, masterworks are not
+ marked for melting, even if they are in an automelt stockpile.
diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst
index 734800e77..c1268bda5 100644
--- a/docs/plugins/manipulator.rst
+++ b/docs/plugins/manipulator.rst
@@ -3,7 +3,7 @@ manipulator
.. dfhack-tool::
:summary: An in-game labor management interface.
- :tags: unavailable fort productivity labors
+ :tags: unavailable
:no-command:
It is equivalent to the popular Dwarf Therapist utility.
diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst
index 4f3c6ba72..5dc07c45e 100644
--- a/docs/plugins/map-render.rst
+++ b/docs/plugins/map-render.rst
@@ -3,7 +3,7 @@ map-render
.. dfhack-tool::
:summary: Provides a Lua API for re-rendering portions of the map.
- :tags: unavailable dev graphics
+ :tags: unavailable
:no-command:
See `map-render-api` for details.
diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst
index 8a65d8419..3bcd5588a 100644
--- a/docs/plugins/misery.rst
+++ b/docs/plugins/misery.rst
@@ -2,11 +2,12 @@ misery
======
.. dfhack-tool::
- :summary: Increase the intensity of your citizens' negative thoughts.
+ :summary: Make citizens more miserable.
:tags: fort gameplay units
-When enabled, negative thoughts that your citizens have will multiply by the
-specified factor. This makes it more challenging to keep them happy.
+When enabled, all of your citizens receive a negative thought about a
+particularly nasty soapy bath. You can vary the strength of this negative
+thought to increase or decrease the difficulty of keeping your citizens happy.
Usage
-----
@@ -18,18 +19,18 @@ Usage
misery
misery clear
-The default misery factor is ``2``, meaning that your dwarves will become
-miserable twice as fast.
+The default misery factor is ``2``, which will result in a moderate hit to your
+dwarves' happiness. Larger numbers increase the challenge.
Examples
--------
``enable misery``
- Start multiplying bad thoughts for your citizens!
+ Start adding bad thoughts about nasty soapy baths to your citizens!
``misery 5``
- Make dwarves become unhappy 5 times faster than normal -- this is quite
- challenging to handle!
+ Change the strength of the soapy bath negative thought to something quite
+ large -- this is very challenging to handle!
``misery clear``
Clear away negative thoughts added by ``misery``. Note that this will not
diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst
index 061e3a548..35964b783 100644
--- a/docs/plugins/mode.rst
+++ b/docs/plugins/mode.rst
@@ -3,7 +3,7 @@ mode
.. dfhack-tool::
:summary: See and change the game mode.
- :tags: unavailable armok dev gameplay
+ :tags: unavailable
.. warning::
diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst
index bff110f0e..38e9266e3 100644
--- a/docs/plugins/mousequery.rst
+++ b/docs/plugins/mousequery.rst
@@ -3,7 +3,7 @@ mousequery
.. dfhack-tool::
:summary: Adds mouse controls to the DF interface.
- :tags: unavailable fort productivity interface
+ :tags: unavailable
Adds mouse controls to the DF interface. For example, with ``mousequery`` you
can click on buildings to configure them, hold the mouse button to draw dig
diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst
index 46a004081..32708b4c8 100644
--- a/docs/plugins/orders.rst
+++ b/docs/plugins/orders.rst
@@ -17,6 +17,14 @@ Usage
manager orders. It will not clear the orders that already exist.
``orders clear``
Deletes all manager orders in the current embark.
+``orders recheck [this]``
+ Sets the status to ``Checking`` (from ``Active``) for all work orders that
+ have conditions that can be re-checked. If the "this" option is passed,
+ only sets the status for the workorder whose condition details page is
+ open. This makes the manager reevaluate its conditions. This is especially
+ useful for an order that had its conditions met when it was started, but
+ the requisite items have since disappeared and the workorder is now
+ generating job cancellation spam.
``orders sort``
Sorts current manager orders by repeat frequency so repeating orders don't
prevent one-time orders from ever being completed. The sorting order is:
diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst
index 4f6ea4160..2aef993d0 100644
--- a/docs/plugins/petcapRemover.rst
+++ b/docs/plugins/petcapRemover.rst
@@ -3,7 +3,7 @@ petcapRemover
.. dfhack-tool::
:summary: Modify the pet population cap.
- :tags: unavailable fort auto animals
+ :tags: unavailable
In vanilla DF, pets will not reproduce unless the population is below 50 and the
number of children of that species is below a certain percentage. This plugin
diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst
index 281b295cf..d35579e1d 100644
--- a/docs/plugins/plants.rst
+++ b/docs/plugins/plants.rst
@@ -5,7 +5,7 @@ plants
.. dfhack-tool::
:summary: Provides commands that interact with plants.
- :tags: unavailable adventure fort armok map plants
+ :tags: unavailable
:no-command:
.. dfhack-command:: plant
diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst
index f3a76c60a..d6d4c8272 100644
--- a/docs/plugins/power-meter.rst
+++ b/docs/plugins/power-meter.rst
@@ -3,7 +3,7 @@ power-meter
.. dfhack-tool::
:summary: Allow pressure plates to measure power.
- :tags: unavailable fort gameplay buildings
+ :tags: unavailable
:no-command:
If you run `gui/power-meter` while building a pressure plate, the pressure
diff --git a/docs/plugins/preserve-tombs.rst b/docs/plugins/preserve-tombs.rst
new file mode 100644
index 000000000..2f01162c6
--- /dev/null
+++ b/docs/plugins/preserve-tombs.rst
@@ -0,0 +1,23 @@
+preserve-tombs
+==============
+
+.. dfhack-tool::
+ :summary: Preserve tomb assignments when assigned units die.
+ :tags: fort bugfix
+
+If you find that the tombs you assign to units get unassigned from them when
+they die (e.g. your nobles), this tool can help fix that.
+
+Usage
+-----
+
+``enable preserve-tombs``
+ enable the plugin
+``preserve-tombs [status]``
+ check the status of the plugin, and if the plugin is enabled,
+ lists all currently tracked tomb assignments
+``preserve-tombs now``
+ forces an immediate update of the tomb assignments. This plugin
+ automatically updates the tomb assignments once every 100 ticks.
+
+This tool runs in the background.
diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst
index 4628d11c8..68ec3d9b1 100644
--- a/docs/plugins/prospector.rst
+++ b/docs/plugins/prospector.rst
@@ -11,8 +11,8 @@ prospector
.. dfhack-command:: prospect
:summary: Shows a summary of resources that exist on the map.
-It can also calculate an estimate of resources available in the selected embark
-area.
+It can also calculate an estimate of resources available in the currently
+highlighted embark area.
Usage
-----
diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst
index 903a1a1d4..a002fc4e4 100644
--- a/docs/plugins/rename.rst
+++ b/docs/plugins/rename.rst
@@ -3,7 +3,7 @@ rename
.. dfhack-tool::
:summary: Easily rename things.
- :tags: unavailable adventure fort productivity buildings stockpiles units
+ :tags: unavailable
Use `gui/rename` for an in-game interface.
diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst
index 91faa6f5c..d09014cb2 100644
--- a/docs/plugins/rendermax.rst
+++ b/docs/plugins/rendermax.rst
@@ -3,7 +3,7 @@ rendermax
.. dfhack-tool::
:summary: Modify the map lighting.
- :tags: unavailable adventure fort gameplay graphics
+ :tags: unavailable
This plugin provides a collection of OpenGL lighting filters that affect how the
map is drawn to the screen.
diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst
deleted file mode 100644
index c1493f992..000000000
--- a/docs/plugins/search.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-.. _search-plugin:
-
-search
-======
-
-.. dfhack-tool::
- :summary: Adds search capabilities to the UI.
- :tags: unavailable fort productivity interface
- :no-command:
-
-Search options are added to the Stocks, Animals, Trading, Stockpile, Noble
-assignment candidates), Military (position candidates), Burrows (unit list),
-Rooms, Announcements, Job List, and Unit List screens all get hotkeys that allow
-you to dynamically filter the displayed lists.
-
-Usage
------
-
-::
-
- enable search
-
-.. image:: ../images/search.png
-
-Searching works the same way as the search option in :guilabel:`Move to Depot`.
-You will see the Search option displayed on screen with a hotkey
-(usually :kbd:`s`). Pressing it lets you start typing a query and the relevant
-list will start filtering automatically.
-
-Pressing :kbd:`Enter`, :kbd:`Esc` or the arrow keys will return you to browsing
-the now filtered list, which still functions as normal. You can clear the filter
-by either going back into search mode and backspacing to delete it, or pressing
-the "shifted" version of the search hotkey while browsing the list (e.g. if the
-hotkey is :kbd:`s`, then hitting :kbd:`Shift`:kbd:`s` will clear any filter).
-
-Leaving any screen automatically clears the filter.
-
-In the Trade screen, the actual trade will always only act on items that are
-actually visible in the list; the same effect applies to the Trade Value numbers
-displayed by the screen. Because of this, the :kbd:`t` key is blocked while
-search is active, so you have to reset the filters first. Pressing
-:kbd:`Alt`:kbd:`C` will clear both search strings.
-
-In the stockpile screen the option only appears if the cursor is in the
-rightmost list:
-
-.. image:: ../images/search-stockpile.png
-
-Note that the 'Permit XXX'/'Forbid XXX' keys conveniently operate only on items
-actually shown in the rightmost list, so it is possible to select only fat or
-tallow by forbidding fats, then searching for fat/tallow, and using Permit Fats
-again while the list is filtered.
diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst
index 57693fa11..4707ad5e8 100644
--- a/docs/plugins/siege-engine.rst
+++ b/docs/plugins/siege-engine.rst
@@ -3,7 +3,7 @@ siege-engine
.. dfhack-tool::
:summary: Extend the functionality and usability of siege engines.
- :tags: unavailable fort gameplay buildings
+ :tags: unavailable
:no-command:
Siege engines in DF haven't been updated since the game was 2D, and can only aim
diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst
index 067e188fd..eb3e20663 100644
--- a/docs/plugins/sort.rst
+++ b/docs/plugins/sort.rst
@@ -2,59 +2,159 @@ sort
====
.. dfhack-tool::
- :summary: Sort lists shown in the DF interface.
- :tags: unavailable fort productivity interface
+ :summary: Search and sort lists shown in the DF interface.
+ :tags: fort productivity interface
:no-command:
-.. dfhack-command:: sort-items
- :summary: Sort the visible item list.
+The ``sort`` tool provides search and sort functionality for lists displayed in
+the DF interface.
-.. dfhack-command:: sort-units
- :summary: Sort the visible unit list.
+Searching and sorting functionality is provided by `overlay` widgets, and
+widgets for individual lists can be moved via `gui/overlay` or turned on or off
+via `gui/control-panel`.
-Usage
------
+Squad assignment overlay
+------------------------
-::
+You can search for a dwarf by name by typing in the Search field. The search
+field is always focused, so any lowercase letter you type will appear there.
- sort-items [ ...]
- sort-units [ ...]
+The squad assignment screen can be sorted by name, by arrival order, by stress,
+by various military-related skills, or by long-term military potential.
-Both commands sort the visible list using the given sequence of comparisons.
-Each property can be prefixed with a ``<`` or ``>`` character to indicate
-whether elements that don't have the given property defined go first or last
-(respectively) in the sorted list.
+If sorted by "melee effectiveness" (the default), then the citizens are sorted
+according to how well they will perform in battle when using the weapon they
+have the most skill in. The effectiveness rating also takes into account
+physical and mental attributes as well as general fighting (non-weapon) skills.
-Examples
---------
+The "ranged effectiveness" sort order does a similar sort for expected
+effectiveness with a crossbow. This sort also takes into account relevant
+physical and mental attributes.
-``sort-items material type quality``
- Sort a list of items by material, then by type, then by quality
-``sort-units profession name``
- Sort a list of units by profession, then by name
+The "effectiveness" sorts are the ones you should be using if you need the best
+squad you can make right now. The numbers to the left of the unit list indicate
+exactly how effective that dwarf is expected to be. Light green numbers
+indicate the best of the best, while red numbers indicate dwarves that will not
+be effective in the military in their current state (though see "melee
+potential" and "ranged potential" sorts below for predictions about future
+effectiveness).
-Properties
-----------
+The "arrival order" sorts your citizens according to the most recent time they
+entered your map. The numbers on the left indicate the relative arrival order,
+and the numbers for the group of dwarves that most recently entered the map
+will be at the top and be colored bright green. If you run this sort after you
+get a new group of migrants, the migrant wave will be colored bright green.
+Dwarves that arrived earlier will have numbers in yellow, and your original
+dwarves (if any still survive and have never left and re-entered the map) will
+have numbers in red.
-Items can be sorted by the following properties:
+The "stress" sort order will bring your most stressed dwarves to the top, ready
+for addition to a :wiki:`therapy squad ` to
+help improve their mood.
-- ``type``
-- ``description``
-- ``base_quality``
-- ``quality``
-- ``improvement``
-- ``wear``
-- ``material``
+Similarly, sorting by "need for training" will show you the dwarves that are
+feeling the most unfocused because they are having their military training
+needs unmet.
-Units can be sorted by the following properties:
+Both "stress" and "need for training" sorts use the dwarf happiness indicators
+to show how dire the dwarf's situation is and how much their mood might be
+improved if you add them to an appropriate squad.
-- ``name``
-- ``age``
-- ``arrival``
-- ``noble``
-- ``profession``
-- ``profession_class``
-- ``race``
-- ``squad``
-- ``squad_position``
-- ``happiness``
+If sorting is done by "melee potential", then citizens are arranged based on
+genetic predispositions in physical and mental attributes, as well as body
+size. Dwarves (and other humanoid creatures) with higher ratings are expected
+to be more effective in melee combat if they train their attributes to their
+genetic maximum.
+
+Similarly, the "ranged potential" sort orders citizens by genetic
+predispositions in physical and mental attributes that are relevant to ranged
+combat. Dwarves (and other humanoid creatures) with higher rating are expected
+to be more effective in ranged combat if they train their attributes to the
+maximum.
+
+The squad assignment panel also offers options for filtering which dwarves are
+shown. Each filter option can by cycled through "Include", "Only", and
+"Exclude" settings. "Include" does no filtering, "Only" shows only units that
+match the filter, and "Exclude" shows only units that do *not* match the filter.
+
+The following filters are provided:
+
+- Units that are assigned to other squads
+- Elected and appointed officials (e.g. mayor, priests, tavern keepers, etc.)
+- Nobility (e.g. monarch, barons, counts, etc.)
+- Mothers with infants (you may not want mothers using their babies as shields)
+- Weak mental fortitude (units that have facets and values that indicate that
+ they will react poorly to the stresses of battle)
+- Critically injured (units that have lost their ability to see, grasp weapons,
+ or walk)
+
+"Melee skill effectiveness", "ranged skill effectiveness", "melee combat potential"
+and "ranged combat potential" are explained in detail here:
+https://www.reddit.com/r/dwarffortress/comments/163kczo/enhancing_military_candidate_selection_part_3/
+"Mental stability" is explained here:
+https://www.reddit.com/r/dwarffortress/comments/1617s11/enhancing_military_candidate_selection_part_2/
+
+Info tabs overlay
+-----------------
+
+The Info overlay adds search support to many of the fort-wide "Info" panels
+(e.g. "Creatures", "Tasks", etc.). When searching for units, you can search by
+name (with either English or native language last names), profession, or
+special status (like "necromancer"). If there is text in the second column, you
+can search for that text as well. This is often a job name or a status, like
+"caged". The work animal assignment page can also filter by squad or burrow
+membership.
+
+Work animals overlay
+--------------------
+
+In addition to the search and filter widgets provided by the Info tabs overlay,
+the work animal assignment screen has an additional overlay that annotates each
+visible unit with the number of work animals that unit already has.
+
+Interrogation overlay
+---------------------
+
+In the interrogation and conviction screens under the "Justice" tab, you can
+search for units by name. You can also filter by the classification of the
+unit. The classification groups are ordered by how likely a member of that
+group is to be involved in a plot. The groups are: All, Risky visitors, Other
+visitors, Residents, Citizens, Animals, Deceased, and Others. "Risky" visitors are those who are especially likely to be involved in plots, such as criminals,
+necromancers, necromancer experiments, and intelligent undead.
+
+On the interrogations screen, you can also filter units by whether they have
+already been interrogated.
+
+Candidates overlay
+------------------
+
+When you select the button to choose a candidate to assign to a noble role on
+the nobles screen, you can search for units by name, profession, or any of the
+skills in which they have achieved at least "novice" level. For example, when
+assigning a broker, you can search for "appraisal" to find candidates that have
+at least some appraisal skill.
+
+Location selection overlay
+--------------------------
+
+When choosing the type of guildhall or temple to dedicate, you can search for
+the relevant profession, religion, or deity by name. For temples, you can also
+search for the "spheres" associated with the deity or religion, such as
+"wealth" or "lies".
+
+You can also choose whether to filter out temple or guildhall types that you
+have already established.
+
+Slab engraving overlay
+----------------------
+
+When choosing a unit to engrave a slab for, you can search for units by name,
+either in their native language or in English (though only their native name
+will be displayed). This overlay also adds a filter for showing only units that
+would need a slab in order to prevent them rising as a ghost.
+
+World overlay
+-------------
+
+Searching is supported for the Artifacts list when viewing the world map (where
+you can initiate raids).
diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst
index 95ce852ae..f54d68142 100644
--- a/docs/plugins/spectate.rst
+++ b/docs/plugins/spectate.rst
@@ -3,7 +3,7 @@ spectate
.. dfhack-tool::
:summary: Automatically follow productive dwarves.
- :tags: unavailable fort interface
+ :tags: fort interface
Usage
-----
diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst
index 532b311d0..5ef4ab873 100644
--- a/docs/plugins/steam-engine.rst
+++ b/docs/plugins/steam-engine.rst
@@ -3,7 +3,7 @@ steam-engine
.. dfhack-tool::
:summary: Allow modded steam engine buildings to function.
- :tags: unavailable fort gameplay buildings
+ :tags: unavailable
:no-command:
The steam-engine plugin detects custom workshops with the string
diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst
index 29b7838fc..95eba213c 100644
--- a/docs/plugins/stockflow.rst
+++ b/docs/plugins/stockflow.rst
@@ -3,7 +3,7 @@ stockflow
.. dfhack-tool::
:summary: Queue manager jobs based on free space in stockpiles.
- :tags: unavailable fort auto stockpiles workorders
+ :tags: unavailable
With this plugin, the fortress bookkeeper can tally up free space in specific
stockpiles and queue jobs through the manager to produce items to fill the free
diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst
index da9b77dc5..7d4ec050b 100644
--- a/docs/plugins/stockpiles.rst
+++ b/docs/plugins/stockpiles.rst
@@ -95,6 +95,19 @@ file are:
specific item types, and any toggles the category might have (like Prepared
meals for the Food category).
+Overlay
+-------
+
+This plugin provides a panel that appears when you select a stockpile via an
+`overlay` widget. You can use it to easily toggle `logistics` plugin features
+like autotrade, automelt, or autotrain. There are also buttons along the top frame for:
+
+- minimizing the panel (if it is in the way of the vanilla stockpile
+ configuration widgets)
+- showing help for the overlay widget in `gui/launcher` (this page)
+- configuring advanced settings for the stockpile, such as whether automelt
+ will melt masterworks
+
.. _stockpiles-library:
The stockpiles settings library
diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst
index f8db4ee67..af5228b12 100644
--- a/docs/plugins/stocks.rst
+++ b/docs/plugins/stocks.rst
@@ -3,7 +3,7 @@ stocks
.. dfhack-tool::
:summary: Enhanced fortress stock management interface.
- :tags: unavailable fort productivity items
+ :tags: unavailable
When the plugin is enabled, two new hotkeys become available:
diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst
index 12e814c55..f99be26eb 100644
--- a/docs/plugins/strangemood.rst
+++ b/docs/plugins/strangemood.rst
@@ -12,10 +12,10 @@ Usage
strangemood []
-Examples
---------
+Example
+-------
-``strangemood -force -unit -type secretive -skill armorsmith``
+``strangemood --force --unit --type secretive --skill armorsmith``
Trigger a strange mood for the selected unit that will cause them to become
a legendary armorsmith.
diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst
index ee4068547..b6a250e73 100644
--- a/docs/plugins/title-folder.rst
+++ b/docs/plugins/title-folder.rst
@@ -3,7 +3,7 @@ title-folder
.. dfhack-tool::
:summary: Displays the DF folder name in the window title bar.
- :tags: unavailable interface
+ :tags: unavailable
:no-command:
Usage
diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst
index 041bcac2c..d2011387e 100644
--- a/docs/plugins/trackstop.rst
+++ b/docs/plugins/trackstop.rst
@@ -3,7 +3,7 @@ trackstop
.. dfhack-tool::
:summary: Add dynamic configuration options for track stops.
- :tags: unavailable fort gameplay buildings
+ :tags: unavailable
:no-command:
When enabled, this plugin adds a :kbd:`q` menu for track stops, which is
diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst
index a8684c765..ff668f8df 100644
--- a/docs/plugins/tubefill.rst
+++ b/docs/plugins/tubefill.rst
@@ -3,9 +3,13 @@ tubefill
.. dfhack-tool::
:summary: Replenishes mined-out adamantine.
- :tags: unavailable fort armok map
+ :tags: fort armok map
-Veins that were originally hollow will be left alone.
+This tool replaces mined-out tiles of adamantine spires with fresh, undug
+adamantine walls, ready to be re-harvested. Empty tiles within the spire that
+used to contain special gemstones, obsidian, water, or magma will also be
+replaced with fresh adamantine. Adamantine spires that were originally hollow
+will be left hollow. See below for more details.
Usage
-----
diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst
index f9467e333..69c93bb60 100644
--- a/docs/plugins/tweak.rst
+++ b/docs/plugins/tweak.rst
@@ -3,7 +3,7 @@ tweak
.. dfhack-tool::
:summary: A collection of tweaks and bugfixes.
- :tags: unavailable adventure fort armok bugfix fps interface
+ :tags: unavailable
Usage
-----
diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst
index c95054c0e..323dd6ed4 100644
--- a/docs/plugins/workflow.rst
+++ b/docs/plugins/workflow.rst
@@ -3,7 +3,7 @@ workflow
.. dfhack-tool::
:summary: Manage automated item production rules.
- :tags: unavailable fort auto jobs
+ :tags: unavailable
Manage repeat jobs according to stock levels. `gui/workflow` provides a simple
front-end integrated in the game UI.
diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst
index af6ee2b5f..26e170a58 100644
--- a/docs/plugins/zone.rst
+++ b/docs/plugins/zone.rst
@@ -3,7 +3,7 @@ zone
.. dfhack-tool::
:summary: Manage activity zones, cages, and the animals therein.
- :tags: unavailable fort productivity animals buildings
+ :tags: unavailable
Usage
-----
@@ -157,3 +157,32 @@ cages and then place one pen/pasture activity zone above them, covering all
cages you want to use. Then use ``zone set`` (like with ``assign``) and run
``zone tocages ``. ``tocages`` can be used together with ``nick`` or
``remnick`` to adjust nicknames while assigning to cages.
+
+Overlay
+-------
+
+Advanced unit selection is available via an `overlay` widget that appears when
+you select a cage, restraint, pasture zone, or pit/pond zone.
+
+In the window that pops up when you click the hotkey hint or hit the hotkey on your keyboard, you can:
+
+- search for units by name
+- sort or filter by status (Assigned here, Pastured elsewhere, On restraint, On
+ display in cage, In movable cage, or Roaming)
+- sort or filter by disposition (Pet, Domesticated, Partially trained, Wild
+ (trainable), Wild (untrainable), or Hostile)
+- sort by gender
+- sort by name
+- filter by whether the unit lays eggs
+- filter by whether the unit needs a grazing area
+
+The window is fully navigatable via keyboard or mouse. Hit Enter or click on a
+unit to assign/unassign it to the currently selected zone or building. Shift
+click to assign/unassign a range of units.
+
+You can also keep the window open and click around on different cages,
+restraints, pastures, or pit/ponds, so you can manage multiple buildings/zones
+without having to close and reopen the window.
+
+Just like all other overlays, you can disable this one in `gui/control-panel` on
+the Overlays tab if you don't want the option of using it.
diff --git a/docs/sphinx_extensions/dfhack/changelog.py b/docs/sphinx_extensions/dfhack/changelog.py
index dd59fa9d5..a981bf984 100644
--- a/docs/sphinx_extensions/dfhack/changelog.py
+++ b/docs/sphinx_extensions/dfhack/changelog.py
@@ -16,11 +16,12 @@ CHANGELOG_PATHS = (
CHANGELOG_PATHS = (os.path.join(DFHACK_ROOT, p) for p in CHANGELOG_PATHS)
CHANGELOG_SECTIONS = [
- 'New Plugins',
- 'New Scripts',
- 'New Tweaks',
+ 'New Tools',
+ 'New Plugins', # deprecated
+ 'New Scripts', # deprecated
+ 'New Tweaks', # deprecated
'New Features',
- 'New Internal Commands',
+ 'New Internal Commands', # deprecated
'Fixes',
'Misc Improvements',
'Removed',
diff --git a/docs/sphinx_extensions/dfhack/tool_docs.py b/docs/sphinx_extensions/dfhack/tool_docs.py
index 836bab217..6c65e5918 100644
--- a/docs/sphinx_extensions/dfhack/tool_docs.py
+++ b/docs/sphinx_extensions/dfhack/tool_docs.py
@@ -140,7 +140,8 @@ class DFHackToolDirectiveBase(sphinx.directives.ObjectDescription):
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)
+ if 'unavailable' not in tags:
+ self.env.domaindata['all']['objects'].append(indexdata)
for tag in tags:
self.env.domaindata[tag]['objects'].append(indexdata)
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 8681c9c90..e9ec8f717 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -9,8 +9,11 @@ if(UNIX)
option(CONSOLE_NO_CATCH "Make the console not catch 'CTRL+C' events for easier debugging." OFF)
endif()
-include_directories(proto)
-include_directories(include)
+# Generation
+set(CODEGEN_OUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
+
+file(GLOB GENERATE_INPUT_SCRIPTS ${dfapi_SOURCE_DIR}/xml/*.pm ${dfapi_SOURCE_DIR}/xml/*.xslt)
+file(GLOB GENERATE_INPUT_XMLS ${dfapi_SOURCE_DIR}/xml/df.*.xml)
execute_process(COMMAND ${PERL_EXECUTABLE} xml/list.pl xml ${dfapi_SOURCE_DIR}/include/df ";"
WORKING_DIRECTORY ${dfapi_SOURCE_DIR}
@@ -18,6 +21,32 @@ execute_process(COMMAND ${PERL_EXECUTABLE} xml/list.pl xml ${dfapi_SOURCE_DIR}/i
set_source_files_properties(${GENERATED_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE GENERATED TRUE)
+add_custom_command(
+ OUTPUT ${CODEGEN_OUT}
+ BYPRODUCTS ${GENERATED_HDRS}
+ COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/xml/codegen.pl
+ ${CMAKE_CURRENT_SOURCE_DIR}/xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/include/df
+ MAIN_DEPENDENCY ${dfapi_SOURCE_DIR}/xml/codegen.pl
+ COMMENT "Generating codegen.out.xml and df/headers"
+ DEPENDS ${GENERATE_INPUT_XMLS} ${GENERATE_INPUT_SCRIPTS}
+)
+
+if(NOT("${CMAKE_GENERATOR}" STREQUAL Ninja))
+ # use BYPRODUCTS instead under Ninja to avoid rebuilds
+ list(APPEND CODEGEN_OUT ${GENERATED_HDRS})
+endif()
+
+add_custom_target(generate_headers DEPENDS ${CODEGEN_OUT})
+
+include_directories(include)
+
+add_subdirectory(xml)
+
+if(BUILD_LIBRARY)
+
+include_directories(proto)
+
set(MAIN_HEADERS
include/Internal.h
include/DFHack.h
@@ -60,6 +89,7 @@ set(MAIN_SOURCES
ColorText.cpp
CompilerWorkAround.cpp
DataDefs.cpp
+ DataIdentity.cpp
Debug.cpp
Error.cpp
VTableInterpose.cpp
@@ -69,7 +99,6 @@ set(MAIN_SOURCES
LuaApi.cpp
DataStatics.cpp
DataStaticsCtor.cpp
- DataStaticsFields.cpp
MiscUtils.cpp
Types.cpp
PluginManager.cpp
@@ -184,8 +213,7 @@ foreach(GROUP other a b c d e f g h i j k l m n o p q r s t u v w x y z)
set(STATIC_FIELDS_INC_FILENAME "df/static.fields-${GROUP}.inc")
endif()
file(WRITE ${STATIC_FIELDS_FILENAME}.tmp
- "#define STATIC_FIELDS_GROUP\n"
- "#include \"../DataStaticsFields.cpp\"\n"
+ "#include \"DataStaticsFields.inc\"\n"
"#include \"${STATIC_FIELDS_INC_FILENAME}\"\n"
)
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
@@ -262,45 +290,20 @@ add_custom_target(generate_proto_core DEPENDS ${PROJECT_PROTO_TMP_FILES})
# Merge headers into sources
set_source_files_properties( ${PROJECT_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE )
list(APPEND PROJECT_SOURCES ${PROJECT_HEADERS})
-
-# Generation
list(APPEND PROJECT_SOURCES ${GENERATED_HDRS})
-file(GLOB GENERATE_INPUT_SCRIPTS ${dfapi_SOURCE_DIR}/xml/*.pm ${dfapi_SOURCE_DIR}/xml/*.xslt)
-file(GLOB GENERATE_INPUT_XMLS ${dfapi_SOURCE_DIR}/xml/df.*.xml)
-
-set(CODEGEN_OUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
-if(NOT("${CMAKE_GENERATOR}" STREQUAL Ninja))
- # use BYPRODUCTS instead under Ninja to avoid rebuilds
- list(APPEND CODEGEN_OUT ${GENERATED_HDRS})
-endif()
-
-add_custom_command(
- OUTPUT ${CODEGEN_OUT}
- BYPRODUCTS ${GENERATED_HDRS}
- COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/xml/codegen.pl
- ${CMAKE_CURRENT_SOURCE_DIR}/xml
- ${CMAKE_CURRENT_SOURCE_DIR}/include/df
- MAIN_DEPENDENCY ${dfapi_SOURCE_DIR}/xml/codegen.pl
- COMMENT "Generating codegen.out.xml and df/headers"
- DEPENDS ${GENERATE_INPUT_XMLS} ${GENERATE_INPUT_SCRIPTS}
-)
-
-add_custom_target(generate_headers
- DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
-
if(REMOVE_SYMBOLS_FROM_DF_STUBS)
if(UNIX)
# Don't produce debug info for generated stubs
- set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
+ set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "-g0 -O1")
else(WIN32)
- set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
+ set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
endif()
else()
if(WIN32)
- set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
+ set_source_files_properties(DataStatics.cpp DataStaticsCtor.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/Od /bigobj")
endif()
endif()
@@ -431,10 +434,6 @@ if(UNIX)
install(TARGETS dfhooks
LIBRARY DESTINATION .
RUNTIME DESTINATION .)
-else()
- # On windows, copy SDL.dll so DF can still run.
- install(PROGRAMS ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}/SDL.dll
- DESTINATION ${DFHACK_LIBRARY_DESTINATION})
endif()
# install the main lib
@@ -442,20 +441,22 @@ install(TARGETS dfhack
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})
-# install the offset file
-install(FILES xml/symbols.xml
- DESTINATION ${DFHACK_DATA_DESTINATION})
-
install(TARGETS dfhack-run dfhack-client binpatch
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})
-install(DIRECTORY lua/
- DESTINATION ${DFHACK_LUA_DESTINATION}
- FILES_MATCHING PATTERN "*.lua")
+endif(BUILD_LIBRARY)
-install(DIRECTORY ${dfhack_SOURCE_DIR}/patches
- DESTINATION ${DFHACK_DATA_DESTINATION}
- FILES_MATCHING PATTERN "*.dif")
+# install the offset file
+if(INSTALL_DATA_FILES)
+ install(FILES xml/symbols.xml
+ DESTINATION ${DFHACK_DATA_DESTINATION})
-add_subdirectory(xml)
+ install(DIRECTORY lua/
+ DESTINATION ${DFHACK_LUA_DESTINATION}
+ FILES_MATCHING PATTERN "*.lua")
+
+ install(DIRECTORY ${dfhack_SOURCE_DIR}/patches
+ DESTINATION ${DFHACK_DATA_DESTINATION}
+ FILES_MATCHING PATTERN "*.dif")
+endif()
diff --git a/library/Core.cpp b/library/Core.cpp
index 431cd2c9f..1d4f52a56 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -84,7 +84,7 @@ using namespace DFHack;
#include
#include "md5wrapper.h"
-#include "SDL_events.h"
+#include
#ifdef LINUX_BUILD
#include
@@ -1033,7 +1033,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, s
}
else if (first == "die")
{
+#ifdef WIN32
+ TerminateProcess(GetCurrentProcess(),666);
+#else
std::_Exit(666);
+#endif
}
else if (first == "kill-lua")
{
@@ -1249,7 +1253,14 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, s
}
else if (res == CR_NEEDS_CONSOLE)
con.printerr("%s needs an interactive console to work.\n"
- "Please run this command from the DFHack terminal.\n", first.c_str());
+ "Please run this command from the DFHack console.\n\n"
+#ifdef WIN32
+ "You can show the console with the 'show' command."
+#else
+ "The console is accessible when you run DF from the commandline\n"
+ "via the './dfhack' script."
+#endif
+ "\n", first.c_str());
return res;
}
@@ -1404,7 +1415,7 @@ Core::~Core()
}
Core::Core() :
- d(dts::make_unique()),
+ d(std::make_unique()),
script_path_mutex{},
HotkeyMutex{},
HotkeyCond{},
@@ -1423,6 +1434,7 @@ Core::Core() :
memset(&(s_mods), 0, sizeof(s_mods));
// set up hotkey capture
+ suppress_duplicate_keyboard_events = true;
hotkey_set = NO;
last_world_data_ptr = NULL;
last_local_map_ptr = NULL;
@@ -1430,7 +1442,6 @@ Core::Core() :
top_viewscreen = NULL;
color_ostream::log_errors_to_stderr = true;
-
};
void Core::fatal (std::string output)
@@ -1448,12 +1459,9 @@ void Core::fatal (std::string output)
con.print("\n");
}
fprintf(stderr, "%s\n", out.str().c_str());
-#ifndef LINUX_BUILD
- out << "Check file stderr.log for details\n";
- MessageBox(0,out.str().c_str(),"DFHack error!", MB_OK | MB_ICONERROR);
-#else
+ out << "Check file stderr.log for details.\n";
std::cout << "DFHack fatal error: " << out.str() << std::endl;
-#endif
+ DFSDL::DFSDL_ShowSimpleMessageBox(0x10 /* SDL_MESSAGEBOX_ERROR */, "DFHack error!", out.str().c_str(), NULL);
bool is_headless = bool(getenv("DFHACK_HEADLESS"));
if (is_headless)
@@ -1471,6 +1479,10 @@ std::string Core::getHackPath()
#endif
}
+df::viewscreen * Core::getTopViewscreen() {
+ return getInstance().top_viewscreen;
+}
+
bool Core::InitMainThread() {
Filesystem::init();
@@ -1491,13 +1503,19 @@ bool Core::InitMainThread() {
std::cerr << "DFHack build: " << Version::git_description() << "\n"
<< "Starting with working directory: " << Filesystem::getcwd() << std::endl;
+ std::cerr << "Binding to SDL.\n";
+ if (!DFSDL::init(con)) {
+ fatal("cannot bind SDL libraries");
+ return false;
+ }
+
// find out what we are...
#ifdef LINUX_BUILD
const char * path = "hack/symbols.xml";
#else
const char * path = "hack\\symbols.xml";
#endif
- auto local_vif = dts::make_unique();
+ auto local_vif = std::make_unique();
std::cerr << "Identifying DF version.\n";
try
{
@@ -1513,7 +1531,7 @@ bool Core::InitMainThread() {
return false;
}
vif = std::move(local_vif);
- auto local_p = dts::make_unique(*vif);
+ auto local_p = std::make_unique(*vif);
local_p->ValidateDescriptionOS();
vinfo = local_p->getDescriptor();
@@ -1677,11 +1695,6 @@ bool Core::InitSimulationThread()
return false;
}
- std::cerr << "Binding to SDL.\n";
- if (!DFSDL::init(con)) {
- fatal("cannot bind SDL libraries");
- return false;
- }
if (DFSteam::init(con)) {
std::cerr << "Found Steam.\n";
DFSteam::launchSteamDFHackIfNecessary(con);
@@ -1855,6 +1868,11 @@ void *Core::GetData( std::string key )
}
}
+Core& Core::getInstance() {
+ static Core instance;
+ return instance;
+}
+
bool Core::isSuspended(void)
{
return ownerThread.load() == std::this_thread::get_id();
@@ -1887,8 +1905,8 @@ void Core::doUpdate(color_ostream &out)
strict_virtual_cast(screen);
// save data (do this before updating last_world_data_ptr and triggering unload events)
- if ((df::global::game->main_interface.options.do_manual_save && !d->last_manual_save_request) ||
- (df::global::plotinfo->main.autosave_request && !d->last_autosave_request) ||
+ if ((df::global::game && df::global::game->main_interface.options.do_manual_save && !d->last_manual_save_request) ||
+ (df::global::plotinfo && df::global::plotinfo->main.autosave_request && !d->last_autosave_request) ||
(is_load_save && !d->was_load_save && strict_virtual_cast(screen)))
{
doSaveData(out);
@@ -1950,8 +1968,10 @@ void Core::doUpdate(color_ostream &out)
// Execute per-frame handlers
onUpdate(out);
- d->last_autosave_request = df::global::plotinfo->main.autosave_request;
- d->last_manual_save_request = df::global::game->main_interface.options.do_manual_save;
+ if (df::global::game && df::global::plotinfo) {
+ d->last_autosave_request = df::global::plotinfo->main.autosave_request;
+ d->last_manual_save_request = df::global::game->main_interface.options.do_manual_save;
+ }
d->was_load_save = is_load_save;
out << std::flush;
@@ -2206,9 +2226,6 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
}
}
break;
- case SC_VIEWSCREEN_CHANGED:
- Textures::init(out);
- break;
default:
break;
}
@@ -2347,101 +2364,78 @@ bool Core::DFH_ncurses_key(int key)
return ncurses_wgetch(key, dummy);
}
-int UnicodeAwareSym(const SDL::KeyboardEvent& ke)
-{
- // Assume keyboard layouts don't change the order of numbers:
- if( '0' <= ke.ksym.sym && ke.ksym.sym <= '9') return ke.ksym.sym;
- if(SDL::K_F1 <= ke.ksym.sym && ke.ksym.sym <= SDL::K_F12) return ke.ksym.sym;
-
- // These keys are mapped to the same control codes as Ctrl-?
- switch (ke.ksym.sym)
- {
- case SDL::K_RETURN:
- case SDL::K_KP_ENTER:
- case SDL::K_TAB:
- case SDL::K_ESCAPE:
- case SDL::K_DELETE:
- return ke.ksym.sym;
- default:
- break;
- }
-
- int unicode = ke.ksym.unicode;
-
- // convert Ctrl characters to their 0x40-0x5F counterparts:
- if (unicode < ' ')
- {
- unicode += 'A' - 1;
- }
-
- // convert A-Z to their a-z counterparts:
- if('A' <= unicode && unicode <= 'Z')
- {
- unicode += 'a' - 'A';
- }
-
- // convert various other punctuation marks:
- if('\"' == unicode) unicode = '\'';
- if('+' == unicode) unicode = '=';
- if(':' == unicode) unicode = ';';
- if('<' == unicode) unicode = ',';
- if('>' == unicode) unicode = '.';
- if('?' == unicode) unicode = '/';
- if('{' == unicode) unicode = '[';
- if('|' == unicode) unicode = '\\';
- if('}' == unicode) unicode = ']';
- if('~' == unicode) unicode = '`';
+bool Core::getSuppressDuplicateKeyboardEvents() {
+ return suppress_duplicate_keyboard_events;
+}
- return unicode;
+void Core::setSuppressDuplicateKeyboardEvents(bool suppress) {
+ DEBUG(keybinding).print("setting suppress_duplicate_keyboard_events to %s\n",
+ suppress ? "true" : "false");
+ suppress_duplicate_keyboard_events = suppress;
}
// returns true if the event is handled
-bool Core::DFH_SDL_Event(SDL::Event* ev)
+bool Core::DFH_SDL_Event(SDL_Event* ev)
{
+ static std::map hotkey_states;
+
// do NOT process events before we are ready.
- if(!started || !ev)
+ if (!started || !ev)
return false;
- if(ev->type == SDL::ET_ACTIVEEVENT && ev->active.gain)
- {
+ if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
// clear modstate when gaining focus in case alt-tab was used when
// losing focus and modstate is now incorrectly set
modstate = 0;
return false;
}
- if(ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP)
- {
- auto ke = (SDL::KeyboardEvent *)ev;
+ if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
+ auto &ke = ev->key;
+ auto &sym = ke.keysym.sym;
- if (ke->ksym.sym == SDL::K_LSHIFT || ke->ksym.sym == SDL::K_RSHIFT)
- modstate = (ev->type == SDL::ET_KEYDOWN) ? modstate | DFH_MOD_SHIFT : modstate & ~DFH_MOD_SHIFT;
- else if (ke->ksym.sym == SDL::K_LCTRL || ke->ksym.sym == SDL::K_RCTRL)
- modstate = (ev->type == SDL::ET_KEYDOWN) ? modstate | DFH_MOD_CTRL : modstate & ~DFH_MOD_CTRL;
- else if (ke->ksym.sym == SDL::K_LALT || ke->ksym.sym == SDL::K_RALT)
- modstate = (ev->type == SDL::ET_KEYDOWN) ? modstate | DFH_MOD_ALT : modstate & ~DFH_MOD_ALT;
- else if(ke->state == SDL::BTN_PRESSED && !hotkey_states[ke->ksym.sym])
+ if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT)
+ modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_SHIFT : modstate & ~DFH_MOD_SHIFT;
+ else if (sym == SDLK_LCTRL || sym == SDLK_RCTRL)
+ modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_CTRL : modstate & ~DFH_MOD_CTRL;
+ else if (sym == SDLK_LALT || sym == SDLK_RALT)
+ modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_ALT : modstate & ~DFH_MOD_ALT;
+ else if (ke.state == SDL_PRESSED && !hotkey_states[sym])
{
- hotkey_states[ke->ksym.sym] = true;
-
- // Use unicode so Windows gives the correct value for the
- // user's Input Language
- if(ke->ksym.unicode && ((ke->ksym.unicode & 0xff80) == 0))
- {
- int key = UnicodeAwareSym(*ke);
- SelectHotkey(key, modstate);
- }
- else
- {
- // Pretend non-ascii characters don't happen:
- SelectHotkey(ke->ksym.sym, modstate);
+ // the check against hotkey_states[sym] ensures we only process keybindings once per keypress
+ DEBUG(keybinding).print("key down: sym=%d (%c)\n", sym, sym);
+ bool handled = SelectHotkey(sym, modstate);
+ if (handled) {
+ hotkey_states[sym] = true;
+ if (modstate & (DFH_MOD_CTRL | DFH_MOD_ALT)) {
+ DEBUG(keybinding).print("modifier key detected; not inhibiting SDL key down event\n");
+ return false;
+ }
+ DEBUG(keybinding).print("%sinhibiting SDL key down event\n",
+ suppress_duplicate_keyboard_events ? "" : "not ");
+ return suppress_duplicate_keyboard_events;
}
}
- else if(ke->state == SDL::BTN_RELEASED)
+ else if (ke.state == SDL_RELEASED)
{
- hotkey_states[ke->ksym.sym] = false;
+ DEBUG(keybinding).print("key up: sym=%d (%c)\n", sym, sym);
+ hotkey_states[sym] = false;
}
}
+ else if (ev->type == SDL_TEXTINPUT) {
+ auto &te = ev->text;
+ DEBUG(keybinding).print("text input: '%s' (modifiers: %s%s%s)\n",
+ te.text,
+ modstate & DFH_MOD_SHIFT ? "Shift" : "",
+ modstate & DFH_MOD_CTRL ? "Ctrl" : "",
+ modstate & DFH_MOD_ALT ? "Alt" : "");
+ if (strlen(te.text) == 1 && hotkey_states[te.text[0]]) {
+ DEBUG(keybinding).print("%sinhibiting SDL text event\n",
+ suppress_duplicate_keyboard_events ? "" : "not ");
+ return suppress_duplicate_keyboard_events;
+ }
+ }
+
return false;
}
@@ -2455,12 +2449,12 @@ bool Core::SelectHotkey(int sym, int modifiers)
while (screen->child)
screen = screen->child;
- if (sym == SDL::K_KP_ENTER)
- sym = SDL::K_RETURN;
+ if (sym == SDLK_KP_ENTER)
+ sym = SDLK_RETURN;
std::string cmd;
- DEBUG(keybinding).print("checking hotkeys for sym=%d, modifiers=%x\n", sym, modifiers);
+ DEBUG(keybinding).print("checking hotkeys for sym=%d (%c), modifiers=%x\n", sym, sym, modifiers);
{
std::lock_guard lock(HotkeyMutex);
@@ -2497,7 +2491,7 @@ bool Core::SelectHotkey(int sym, int modifiers)
if (cmd.empty()) {
// Check the hotkey keybindings
- int idx = sym - SDL::K_F1;
+ int idx = sym - SDLK_F1;
if(idx >= 0 && idx < 8)
{
/* TODO: understand how this changes for v50
@@ -2555,22 +2549,22 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string
}
if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') {
- *psym = SDL::K_a + (keyspec[0]-'A');
+ *psym = SDLK_a + (keyspec[0]-'A');
return true;
} else if (keyspec.size() == 1 && keyspec[0] == '`') {
- *psym = SDL::K_BACKQUOTE;
+ *psym = SDLK_BACKQUOTE;
return true;
} else if (keyspec.size() == 1 && keyspec[0] >= '0' && keyspec[0] <= '9') {
- *psym = SDL::K_0 + (keyspec[0]-'0');
+ *psym = SDLK_0 + (keyspec[0]-'0');
return true;
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
- *psym = SDL::K_F1 + (keyspec[1]-'1');
+ *psym = SDLK_F1 + (keyspec[1]-'1');
return true;
} else if (keyspec.size() == 3 && keyspec.substr(0, 2) == "F1" && keyspec[2] >= '0' && keyspec[2] <= '2') {
- *psym = SDL::K_F10 + (keyspec[2]-'0');
+ *psym = SDLK_F10 + (keyspec[2]-'0');
return true;
} else if (keyspec == "Enter") {
- *psym = SDL::K_RETURN;
+ *psym = SDLK_RETURN;
return true;
} else
return false;
diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp
index f376edc6a..cd261d50f 100644
--- a/library/DataDefs.cpp
+++ b/library/DataDefs.cpp
@@ -213,12 +213,12 @@ std::string pointer_identity::getFullName()
std::string container_identity::getFullName(type_identity *item)
{
- return "<" + (item ? item->getFullName() : std::string("void")) + ">";
+ return '<' + (item ? item->getFullName() : std::string("void")) + '>';
}
std::string ptr_container_identity::getFullName(type_identity *item)
{
- return "<" + (item ? item->getFullName() : std::string("void")) + "*>";
+ return '<' + (item ? item->getFullName() : std::string("void")) + std::string("*>");
}
std::string bit_container_identity::getFullName(type_identity *)
diff --git a/library/DataStaticsFields.cpp b/library/DataIdentity.cpp
similarity index 60%
rename from library/DataStaticsFields.cpp
rename to library/DataIdentity.cpp
index 8318b523d..c041b009d 100644
--- a/library/DataStaticsFields.cpp
+++ b/library/DataIdentity.cpp
@@ -1,24 +1,26 @@
#include
+#include
#include
-
-#ifndef STATIC_FIELDS_GROUP
-#include "DataDefs.h"
-#endif
+#include
+#include
+#include
#include "DataFuncs.h"
+#include "DataIdentity.h"
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Winvalid-offsetof"
-#endif
+// the space after the uses of "type" in OPAQUE_IDENTITY_TRAITS_NAME is _required_
+// without it the macro generates a syntax error when type is a template specification
namespace df {
#define NUMBER_IDENTITY_TRAITS(category, type, name) \
category##_identity identity_traits::identity(name);
#define INTEGER_IDENTITY_TRAITS(type, name) NUMBER_IDENTITY_TRAITS(integer, type, name)
#define FLOAT_IDENTITY_TRAITS(type) NUMBER_IDENTITY_TRAITS(float, type, #type)
+#define OPAQUE_IDENTITY_TRAITS_NAME(type, name) \
+ opaque_identity identity_traits::identity(sizeof(type), allocator_noassign_fn, name)
+#define STL_OPAQUE_IDENTITY_TRAITS(type) OPAQUE_IDENTITY_TRAITS_NAME(std::type, #type)
-#ifndef STATIC_FIELDS_GROUP
INTEGER_IDENTITY_TRAITS(char, "char");
INTEGER_IDENTITY_TRAITS(signed char, "int8_t");
INTEGER_IDENTITY_TRAITS(unsigned char, "uint8_t");
@@ -42,25 +44,12 @@ namespace df {
stl_bit_vector_identity identity_traits >::identity;
bit_array_identity identity_traits >::identity;
- static void *fstream_allocator_fn(void *out, const void *in) {
- if (out) { /* *(T*)out = *(const T*)in;*/ return NULL; }
- else if (in) { delete (std::fstream*)in; return (std::fstream*)in; }
- else return new std::fstream();
- }
- opaque_identity identity_traits::identity(
- sizeof(std::fstream), fstream_allocator_fn, "fstream");
+ STL_OPAQUE_IDENTITY_TRAITS(condition_variable);
+ STL_OPAQUE_IDENTITY_TRAITS(fstream);
+ STL_OPAQUE_IDENTITY_TRAITS(mutex);
+ STL_OPAQUE_IDENTITY_TRAITS(future);
+ STL_OPAQUE_IDENTITY_TRAITS(function);
+ STL_OPAQUE_IDENTITY_TRAITS(optional >);
buffer_container_identity buffer_container_identity::base_instance;
-#endif
-#undef NUMBER_IDENTITY_TRAITS
-#undef INTEGER_IDENTITY_TRAITS
-#undef FLOAT_IDENTITY_TRAITS
}
-
-#define TID(type) (&identity_traits< type >::identity)
-
-#define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name)
-#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
-#define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name)
-#define METHOD_N(mode, func, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::func)
-#define FLD_END struct_field_info::END
diff --git a/library/Debug.cpp b/library/Debug.cpp
index 9b13af168..dafbeb5ce 100644
--- a/library/Debug.cpp
+++ b/library/Debug.cpp
@@ -26,6 +26,7 @@ redistribute it freely, subject to the following restrictions:
#include "Debug.h"
#include "DebugManager.h"
+#include
#include
#include
#include
diff --git a/library/Hooks.cpp b/library/Hooks.cpp
index 4e339e768..31a8ec749 100644
--- a/library/Hooks.cpp
+++ b/library/Hooks.cpp
@@ -48,7 +48,7 @@ DFhackCExport void dfhooks_prerender() {
// called from the main thread for each SDL event. if true is returned, then
// the event has been consumed and further processing shouldn't happen
-DFhackCExport bool dfhooks_sdl_event(SDL::Event* event) {
+DFhackCExport bool dfhooks_sdl_event(SDL_Event* event) {
if (disabled)
return false;
return DFHack::Core::getInstance().DFH_SDL_Event(event);
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 0a737875a..a8e901a6d 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -161,6 +161,26 @@ static bool get_int_field(lua_State *L, T *pf, int idx, const char *name, int de
return !nil;
}
+template
+static bool get_int_or_closure_field(lua_State *L, T *pf, int idx, const char *name, int defval)
+{
+ lua_getfield(L, idx, name);
+ bool nil = lua_isnil(L, -1);
+ if (nil) {
+ *pf = T(defval);
+ } else if (lua_isnumber(L, -1)) {
+ *pf = T(lua_tointeger(L, -1));
+ } else if (lua_isfunction(L, -1)) {
+ lua_call(L, 0, 1);
+ *pf = T(lua_tointeger(L, -1));
+ lua_pop(L, 1);
+ } else {
+ luaL_error(L, "Field %s is not a number or closure function.", name);
+ }
+ lua_pop(L, 1);
+ return !nil;
+}
+
static bool get_char_field(lua_State *L, char *pf, int idx, const char *name, char defval)
{
lua_getfield(L, idx, name);
@@ -207,7 +227,7 @@ static void decode_pen(lua_State *L, Pen &pen, int idx)
else pen.bold = lua_toboolean(L, -1);
lua_pop(L, 1);
- get_int_field(L, &pen.tile, idx, "tile", 0);
+ get_int_or_closure_field(L, &pen.tile, idx, "tile", 0);
bool tcolor = get_int_field(L, &pen.tile_fg, idx, "tile_fg", 7);
tcolor = get_int_field(L, &pen.tile_bg, idx, "tile_bg", 0) || tcolor;
@@ -844,6 +864,23 @@ static void make_pen_table(lua_State *L, Pen &pen)
lua_pushboolean(L, false); lua_setfield(L, -2, "tile_color");
break;
}
+
+ if (pen.keep_lower) {
+ lua_pushboolean(L, true);
+ lua_setfield(L, -2, "keep_lower");
+ }
+ if (pen.write_to_lower) {
+ lua_pushboolean(L, true);
+ lua_setfield(L, -2, "write_to_lower");
+ }
+ if (pen.top_of_text) {
+ lua_pushboolean(L, true);
+ lua_setfield(L, -2, "top_of_text");
+ }
+ if (pen.bottom_of_text) {
+ lua_pushboolean(L, true);
+ lua_setfield(L, -2, "bottom_of_text");
+ }
}
}
@@ -1331,8 +1368,8 @@ static CommandHistory * ensureCommandHistory(std::string id,
static int getCommandHistory(lua_State *state)
{
- std::string id = lua_tostring(state, 1);
- std::string src_file = lua_tostring(state, 2);
+ std::string id = luaL_checkstring(state, 1);
+ std::string src_file = luaL_checkstring(state, 2);
std::vector entries;
ensureCommandHistory(id, src_file)->getEntries(entries);
Lua::PushVector(state, entries);
@@ -1360,6 +1397,13 @@ static void OpenModule(lua_State *state, const char *mname,
lua_pop(state, 1);
}
+static void OpenModule(lua_State *state, const char *mname, const luaL_Reg *reg2)
+{
+ luaL_getsubtable(state, lua_gettop(state), mname);
+ luaL_setfuncs(state, reg2, 0);
+ lua_pop(state, 1);
+}
+
#define WRAPM(module, function) { #function, df::wrap_function(module::function,true) }
#define WRAP(function) { #function, df::wrap_function(function,true) }
#define WRAPN(name, function) { #name, df::wrap_function(function,true) }
@@ -1461,8 +1505,9 @@ static int gui_getDwarfmodeViewDims(lua_State *state)
static int gui_getMousePos(lua_State *L)
{
- auto pos = Gui::getMousePos();
- if (pos.isValid())
+ bool allow_out_of_bounds = lua_toboolean(L, 1);
+ df::coord pos = Gui::getMousePos(allow_out_of_bounds);
+ if ((allow_out_of_bounds && pos.z >= 0) || pos.isValid())
Lua::Push(L, pos);
else
lua_pushnil(L);
@@ -1483,6 +1528,8 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getAnyUnit),
WRAPM(Gui, getAnyItem),
WRAPM(Gui, getAnyBuilding),
+ WRAPM(Gui, getAnyCivZone),
+ WRAPM(Gui, getAnyStockpile),
WRAPM(Gui, getAnyPlant),
WRAPM(Gui, writeToGamelog),
WRAPM(Gui, makeAnnouncement),
@@ -1614,11 +1661,19 @@ static int gui_revealInDwarfmodeMap(lua_State *state)
switch (lua_gettop(state))
{
default:
+ case 5:
+ rv = Gui::revealInDwarfmodeMap(CheckCoordXYZ(state, 1, false), lua_toboolean(state, 4), lua_toboolean(state, 5));
+ break;
case 4:
rv = Gui::revealInDwarfmodeMap(CheckCoordXYZ(state, 1, false), lua_toboolean(state, 4));
break;
case 3:
- rv = Gui::revealInDwarfmodeMap(CheckCoordXYZ(state, 1, false));
+ if (lua_isboolean(state, 3)) {
+ Lua::CheckDFAssign(state, &p, 1);
+ rv = Gui::revealInDwarfmodeMap(p, lua_toboolean(state, 2), lua_toboolean(state, 3));
+ }
+ else
+ rv = Gui::revealInDwarfmodeMap(CheckCoordXYZ(state, 1, false));
break;
case 2:
Lua::CheckDFAssign(state, &p, 1);
@@ -1709,18 +1764,76 @@ static const luaL_Reg dfhack_job_funcs[] = {
/***** Textures module *****/
-static const LuaWrapper::FunctionReg dfhack_textures_module[] = {
- WRAPM(Textures, getDfhackLogoTexposStart),
- WRAPM(Textures, getGreenPinTexposStart),
- WRAPM(Textures, getRedPinTexposStart),
- WRAPM(Textures, getIconsTexposStart),
- WRAPM(Textures, getOnOffTexposStart),
- WRAPM(Textures, getControlPanelTexposStart),
- WRAPM(Textures, getThinBordersTexposStart),
- WRAPM(Textures, getMediumBordersTexposStart),
- WRAPM(Textures, getBoldBordersTexposStart),
- WRAPM(Textures, getPanelBordersTexposStart),
- WRAPM(Textures, getWindowBordersTexposStart),
+static int textures_loadTileset(lua_State *state)
+{
+ std::string file = luaL_checkstring(state, 1);
+ auto tile_w = luaL_checkint(state, 2);
+ auto tile_h = luaL_checkint(state, 3);
+ bool reserved = lua_isboolean(state, 4) ? lua_toboolean(state, 4) : false;
+ auto handles = Textures::loadTileset(file, tile_w, tile_h, reserved);
+ Lua::PushVector(state, handles);
+ return 1;
+}
+
+static int textures_getTexposByHandle(lua_State *state)
+{
+ auto handle = luaL_checkunsigned(state, 1);
+ auto texpos = Textures::getTexposByHandle(handle);
+ if (texpos == -1) {
+ lua_pushnil(state);
+ } else {
+ Lua::Push(state, texpos);
+ }
+ return 1;
+}
+
+static int textures_deleteHandle(lua_State *state)
+{
+ if (lua_isinteger(state,1)) {
+ auto handle = luaL_checkunsigned(state, 1);
+ Textures::deleteHandle(handle);
+ } else if (lua_istable(state,1)) {
+ std::vector handles;
+ Lua::GetVector(state, handles);
+ for (auto& handle: handles) {
+ Textures::deleteHandle(handle);
+ }
+ }
+ return 0;
+}
+
+static int textures_createTile(lua_State *state)
+{
+ std::vector pixels;
+ Lua::GetVector(state, pixels);
+ auto tile_w = luaL_checkint(state, 2);
+ auto tile_h = luaL_checkint(state, 3);
+ bool reserved = lua_isboolean(state, 4) ? lua_toboolean(state, 4) : false;
+ auto handle = Textures::createTile(pixels, tile_w, tile_h, reserved);
+ Lua::Push(state, handle);
+ return 1;
+}
+
+static int textures_createTileset(lua_State *state)
+{
+ std::vector pixels;
+ Lua::GetVector(state, pixels);
+ auto texture_w = luaL_checkint(state, 2);
+ auto texture_h = luaL_checkint(state, 3);
+ auto tile_w = luaL_checkint(state, 4);
+ auto tile_h = luaL_checkint(state, 5);
+ bool reserved = lua_isboolean(state, 6) ? lua_toboolean(state, 6) : false;
+ auto handles = Textures::createTileset(pixels, texture_w, texture_h, tile_w, tile_h, reserved);
+ Lua::PushVector(state, handles);
+ return 1;
+}
+
+static const luaL_Reg dfhack_textures_funcs[] = {
+ { "loadTileset", textures_loadTileset },
+ { "getTexposByHandle", textures_getTexposByHandle },
+ { "deleteHandle", textures_deleteHandle },
+ { "createTile", textures_createTile },
+ { "createTileset", textures_createTileset },
{ NULL, NULL }
};
@@ -1759,11 +1872,16 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isTame),
WRAPM(Units, isTamable),
WRAPM(Units, isDomesticated),
+ WRAPM(Units, isMarkedForTraining),
+ WRAPM(Units, isMarkedForTaming),
+ WRAPM(Units, isMarkedForWarTraining),
+ WRAPM(Units, isMarkedForHuntTraining),
WRAPM(Units, isMarkedForSlaughter),
WRAPM(Units, isMarkedForGelding),
WRAPM(Units, isGeldable),
WRAPM(Units, isGelded),
WRAPM(Units, isEggLayer),
+ WRAPM(Units, isEggLayerRace),
WRAPM(Units, isGrazer),
WRAPM(Units, isMilkable),
WRAPM(Units, isForest),
@@ -1797,6 +1915,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getNemesis),
WRAPM(Units, getPhysicalAttrValue),
WRAPM(Units, getMentalAttrValue),
+ WRAPM(Units, casteFlagSet),
WRAPM(Units, getMiscTrait),
WRAPM(Units, getAge),
WRAPM(Units, getKillCount),
@@ -1822,6 +1941,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getRaceBabyNameById),
WRAPM(Units, getRaceChildName),
WRAPM(Units, getRaceChildNameById),
+ WRAPM(Units, getReadableName),
WRAPM(Units, getMainSocialActivity),
WRAPM(Units, getMainSocialEvent),
WRAPM(Units, getStressCategory),
@@ -1832,6 +1952,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, multiplyGroupActionTimers),
WRAPM(Units, setActionTimers),
WRAPM(Units, setGroupActionTimers),
+ WRAPM(Units, getUnitByNobleRole),
{ NULL, NULL }
};
@@ -1920,6 +2041,14 @@ static int units_getCitizens(lua_State *L) {
return 0;
}
+static int units_getUnitsByNobleRole(lua_State *L) {
+ std::string role_name = luaL_checkstring(L, -1);
+ std::vector units;
+ Units::getUnitsByNobleRole(units, role_name);
+ Lua::PushVector(L, units);
+ return 1;
+}
+
static int units_getStressCutoffs(lua_State *L)
{
lua_newtable(L);
@@ -1934,6 +2063,7 @@ static const luaL_Reg dfhack_units_funcs[] = {
{ "getNoblePositions", units_getNoblePositions },
{ "getUnitsInBox", units_getUnitsInBox },
{ "getCitizens", units_getCitizens },
+ { "getUnitsByNobleRole", units_getUnitsByNobleRole},
{ "getStressCutoffs", units_getStressCutoffs },
{ NULL, NULL }
};
@@ -2009,12 +2139,16 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getSubtypeDef),
WRAPM(Items, getItemBaseValue),
WRAPM(Items, getValue),
+ WRAPM(Items, isRequestedTradeGood),
WRAPM(Items, createItem),
WRAPM(Items, checkMandates),
WRAPM(Items, canTrade),
WRAPM(Items, canTradeWithContents),
+ WRAPM(Items, canTradeAnyWithContents),
+ WRAPM(Items, markForTrade),
WRAPM(Items, isRouteVehicle),
WRAPM(Items, isSquadEquipment),
+ WRAPM(Items, getCapacity),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToInventory, items_moveToInventory),
@@ -2106,6 +2240,7 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, enableBlockUpdates),
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
+ WRAPM(Maps, getWalkableGroup),
WRAPM(Maps, canWalkBetween),
WRAPM(Maps, spawnFlow),
WRAPN(hasTileAssignment, hasTileAssignment),
@@ -2602,14 +2737,29 @@ static int screen_doSimulateInput(lua_State *L)
int sz = lua_rawlen(L, 2);
std::set keys;
+ char str = '\0';
for (int j = 1; j <= sz; j++)
{
lua_rawgeti(L, 2, j);
- keys.insert((df::interface_key)lua_tointeger(L, -1));
+ df::interface_key k = (df::interface_key)lua_tointeger(L, -1);
+ if (!str && k > df::interface_key::STRING_A000 && k <= df::interface_key::STRING_A255)
+ str = Screen::keyToChar(k);
+ keys.insert(k);
lua_pop(L, 1);
}
+ // if we're injecting a text keybinding, ensure it is reflected in the enabler text buffer
+ std::string prev_input;
+ if (str) {
+ prev_input = (const char *)&df::global::enabler->last_text_input[0];
+ df::global::enabler->last_text_input[0] = str;
+ df::global::enabler->last_text_input[1] = '\0';
+ }
+
screen->feed(&keys);
+
+ if (str)
+ strcpy((char *)&df::global::enabler->last_text_input[0], prev_input.c_str());
return 0;
}
@@ -2635,6 +2785,7 @@ static int screen_charToKey(lua_State *L)
return 1;
}
+/*
static int screen_zoom(lua_State *L)
{
using df::global::enabler;
@@ -2651,6 +2802,7 @@ static int screen_zoom(lua_State *L)
enabler->zoom_display(cmd);
return 0;
}
+*/
}
@@ -2671,7 +2823,7 @@ static const luaL_Reg dfhack_screen_funcs[] = {
{ "_doSimulateInput", screen_doSimulateInput },
{ "keyToChar", screen_keyToChar },
{ "charToKey", screen_charToKey },
- { "zoom", screen_zoom },
+ //{ "zoom", screen_zoom },
{ NULL, NULL }
};
@@ -3010,6 +3162,8 @@ static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAPN(getAddressSizeInHeap, get_address_size_in_heap),
WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object),
WRAPN(msizeAddress, msize_address),
+ WRAP(getClipboardTextCp437),
+ WRAP(setClipboardTextCp437),
{ NULL, NULL }
};
@@ -3298,6 +3452,24 @@ static int internal_diffscan(lua_State *L)
return 1;
}
+static int internal_cxxDemangle(lua_State *L)
+{
+ std::string mangled = luaL_checkstring(L, 1);
+ std::string status;
+ std::string demangled = cxx_demangle(mangled, &status);
+ if (demangled.length())
+ {
+ lua_pushstring(L, demangled.c_str());
+ return 1;
+ }
+ else
+ {
+ lua_pushnil(L);
+ lua_pushstring(L, status.c_str());
+ return 2;
+ }
+}
+
static int internal_runCommand(lua_State *L)
{
color_ostream *out = NULL;
@@ -3577,13 +3749,23 @@ static int internal_md5file(lua_State *L)
}
}
+static int internal_getSuppressDuplicateKeyboardEvents(lua_State *L) {
+ Lua::Push(L, Core::getInstance().getSuppressDuplicateKeyboardEvents());
+ return 1;
+}
+
+static int internal_setSuppressDuplicateKeyboardEvents(lua_State *L) {
+ bool suppress = lua_toboolean(L, 1);
+ Core::getInstance().setSuppressDuplicateKeyboardEvents(suppress);
+ return 0;
+}
+
static const luaL_Reg dfhack_internal_funcs[] = {
{ "getPE", internal_getPE },
{ "getMD5", internal_getmd5 },
{ "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress },
{ "getVTable", internal_getVTable },
-
{ "adjustOffset", internal_adjustOffset },
{ "getMemRanges", internal_getMemRanges },
{ "patchMemory", internal_patchMemory },
@@ -3592,6 +3774,7 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "memcmp", internal_memcmp },
{ "memscan", internal_memscan },
{ "diffscan", internal_diffscan },
+ { "cxxDemangle", internal_cxxDemangle },
{ "getDir", filesystem_listdir },
{ "runCommand", internal_runCommand },
{ "getModifiers", internal_getModifiers },
@@ -3605,6 +3788,8 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "getCommandDescription", internal_getCommandDescription },
{ "threadid", internal_threadid },
{ "md5File", internal_md5file },
+ { "getSuppressDuplicateKeyboardEvents", internal_getSuppressDuplicateKeyboardEvents },
+ { "setSuppressDuplicateKeyboardEvents", internal_setSuppressDuplicateKeyboardEvents },
{ NULL, NULL }
};
@@ -3625,7 +3810,7 @@ void OpenDFHackApi(lua_State *state)
luaL_setfuncs(state, dfhack_funcs, 0);
OpenModule(state, "gui", dfhack_gui_module, dfhack_gui_funcs);
OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
- OpenModule(state, "textures", dfhack_textures_module);
+ OpenModule(state, "textures", dfhack_textures_funcs);
OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs);
OpenModule(state, "military", dfhack_military_module);
OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs);
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp
index a1bf855d6..87699641d 100644
--- a/library/LuaTools.cpp
+++ b/library/LuaTools.cpp
@@ -131,12 +131,12 @@ void DFHack::Lua::GetVector(lua_State *state, std::vector &pvec, in
}
}
-static bool trigger_inhibit_l_down = false;
-static bool trigger_inhibit_r_down = false;
-static bool trigger_inhibit_m_down = false;
-static bool inhibit_l_down = false;
-static bool inhibit_r_down = false;
-static bool inhibit_m_down = false;
+static bool trigger_inhibit_l = false;
+static bool trigger_inhibit_r = false;
+static bool trigger_inhibit_m = false;
+static bool inhibit_l = false;
+static bool inhibit_r = false;
+static bool inhibit_m = false;
void DFHack::Lua::PushInterfaceKeys(lua_State *L,
const std::set &keys) {
@@ -161,32 +161,32 @@ void DFHack::Lua::PushInterfaceKeys(lua_State *L,
}
if (df::global::enabler) {
- if (!inhibit_l_down && df::global::enabler->mouse_lbut_down) {
+ if (!inhibit_l && df::global::enabler->mouse_lbut) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_L_DOWN");
- trigger_inhibit_l_down = true;
+ lua_setfield(L, -2, "_MOUSE_L");
+ trigger_inhibit_l = true;
}
- if (!inhibit_r_down && df::global::enabler->mouse_rbut_down) {
+ if (!inhibit_r && df::global::enabler->mouse_rbut) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_R_DOWN");
- trigger_inhibit_r_down = true;
+ lua_setfield(L, -2, "_MOUSE_R");
+ trigger_inhibit_r = true;
}
- if (!inhibit_m_down && df::global::enabler->mouse_mbut_down) {
+ if (!inhibit_m && df::global::enabler->mouse_mbut) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_M_DOWN");
- trigger_inhibit_m_down = true;
+ lua_setfield(L, -2, "_MOUSE_M");
+ trigger_inhibit_m = true;
}
- if (df::global::enabler->mouse_lbut) {
+ if (df::global::enabler->mouse_lbut_down) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_L");
+ lua_setfield(L, -2, "_MOUSE_L_DOWN");
}
- if (df::global::enabler->mouse_rbut) {
+ if (df::global::enabler->mouse_rbut_down) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_R");
+ lua_setfield(L, -2, "_MOUSE_R_DOWN");
}
- if (df::global::enabler->mouse_mbut) {
+ if (df::global::enabler->mouse_mbut_down) {
lua_pushboolean(L, true);
- lua_setfield(L, -2, "_MOUSE_M");
+ lua_setfield(L, -2, "_MOUSE_M_DOWN");
}
}
}
@@ -2159,23 +2159,25 @@ void DFHack::Lua::Core::Reset(color_ostream &out, const char *where)
lua_settop(State, 0);
}
- if (trigger_inhibit_l_down) {
- trigger_inhibit_l_down = false;
- inhibit_l_down = true;
+ if (trigger_inhibit_l) {
+ trigger_inhibit_l = false;
+ inhibit_l = true;
}
- if (trigger_inhibit_r_down) {
- trigger_inhibit_r_down = false;
- inhibit_r_down = true;
+ if (trigger_inhibit_r) {
+ trigger_inhibit_r = false;
+ inhibit_r = true;
}
- if (trigger_inhibit_m_down) {
- trigger_inhibit_m_down = false;
- inhibit_m_down = true;
+ if (trigger_inhibit_m) {
+ trigger_inhibit_m = false;
+ inhibit_m = true;
}
- if (!df::global::enabler->mouse_lbut)
- inhibit_l_down = false;
- if (!df::global::enabler->mouse_rbut)
- inhibit_r_down = false;
- if (!df::global::enabler->mouse_mbut)
- inhibit_m_down = false;
+ if (df::global::enabler) {
+ if (!df::global::enabler->mouse_lbut_down)
+ inhibit_l = false;
+ if (!df::global::enabler->mouse_rbut_down)
+ inhibit_r = false;
+ if (!df::global::enabler->mouse_mbut_down)
+ inhibit_m = false;
+ }
}
diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp
index ef69958e0..407dea265 100644
--- a/library/LuaTypes.cpp
+++ b/library/LuaTypes.cpp
@@ -664,6 +664,9 @@ static int meta_global_field_reference(lua_State *state)
auto field = (struct_field_info*)find_field(state, 2, "reference");
if (!field)
field_error(state, 2, "builtin property or method", "reference");
+ void *ptr = *(void**)field->offset;
+ if (!ptr)
+ field_error(state, 2, "global address not known", "reference");
field_reference(state, field, *(void**)field->offset);
return 1;
}
@@ -1288,11 +1291,27 @@ void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
/**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
+ *
+ * flags:
+ * GLOBALS: if true, pstruct is a global_identity and fields with addresses of 0 are skipped
+ * RAW: if true, no fields are skipped (supersedes `GLOBALS` flag) and
+ * special-case fields like OBJ_METHODs are not added to the metatable
+ *
+ * Stack in & out:
+ * base+1: metatable
+ * base+2: fields table (to be populated, map of name -> struct_field_info*)
+ * base+3: field iter table (to be populated, bimap of name <-> integer index)
*/
-static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bool globals)
+namespace IndexFieldsFlags {
+ enum IndexFieldsFlags {
+ GLOBALS = 1 << 0,
+ RAW = 1 << 1,
+ };
+}
+static void IndexFields(lua_State *state, int base, struct_identity *pstruct, int flags)
{
if (pstruct->getParent())
- IndexFields(state, base, pstruct->getParent(), globals);
+ IndexFields(state, base, pstruct->getParent(), flags);
auto fields = pstruct->getFields();
if (!fields)
@@ -1312,6 +1331,7 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo
bool add_to_enum = true;
+ if (!(flags & IndexFieldsFlags::RAW))
// Handle the field
switch (fields[i].mode)
{
@@ -1334,10 +1354,10 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo
}
// Do not add invalid globals to the enumeration order
- if (globals && !*(void**)fields[i].offset)
+ if ((flags & IndexFieldsFlags::GLOBALS) && !*(void**)fields[i].offset)
add_to_enum = false;
- if (add_to_enum)
+ if (add_to_enum || (flags & IndexFieldsFlags::RAW))
AssociateId(state, base+3, ++cnt, name.c_str());
lua_pushlightuserdata(state, (void*)&fields[i]);
@@ -1345,10 +1365,168 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo
}
}
+static void PushTypeIdentity(lua_State *state, const type_identity *id)
+{
+ lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPEID_TABLE_TOKEN);
+ lua_rawgetp(state, -1, id);
+ lua_remove(state, -2); // TYPEID_TABLE
+}
+
+static void PushFieldInfoSubTable(lua_State *state, const struct_field_info *field)
+{
+ if (!field) {
+ lua_pushnil(state);
+ return;
+ }
+
+ lua_newtable(state); // new field info
+ Lua::TableInsert(state, "mode", field->mode);
+ Lua::TableInsert(state, "name", field->name);
+ Lua::TableInsert(state, "offset", field->offset);
+ Lua::TableInsert(state, "count", field->count);
+
+ if (field->type) {
+ Lua::TableInsert(state, "type_name", field->type->getFullName());
+
+ lua_pushlightuserdata(state, field->type);
+ lua_setfield(state, -2, "type_identity");
+
+ PushTypeIdentity(state, field->type);
+ lua_setfield(state, -2, "type");
+ }
+
+ if (field->extra) {
+ if (field->extra->index_enum) {
+ PushTypeIdentity(state, field->extra->index_enum);
+ lua_setfield(state, -2, "index_enum");
+ }
+ if (field->extra->ref_target) {
+ PushTypeIdentity(state, field->extra->ref_target);
+ lua_setfield(state, -2, "ref_target");
+ }
+ if (field->extra->union_tag_field) {
+ Lua::TableInsert(state, "union_tag_field", field->extra->union_tag_field);
+ }
+ if (field->extra->union_tag_attr) {
+ Lua::TableInsert(state, "union_tag_attr", field->extra->union_tag_attr);
+ }
+ if (field->extra->original_name) {
+ Lua::TableInsert(state, "original_name", field->extra->original_name);
+ }
+ }
+}
+
+/**
+ * Metamethod: __index for struct._fields
+ *
+ * upvalue 1: name -> struct_field_info* table
+ */
+static int meta_fieldinfo_index(lua_State *state)
+{
+ luaL_checktype(state, -1, LUA_TSTRING);
+
+ lua_gettable(state, lua_upvalueindex(1));
+ auto field = static_cast(lua_touserdata(state, -1));
+ lua_pop(state, 1);
+ PushFieldInfoSubTable(state, field);
+
+ return 1;
+}
+
+/**
+ * Metamethod: iterator for struct._fields
+ *
+ * upvalue 1: name -> struct_field_info* table
+ * upvalue 3: field table (int <-> name)
+ */
+static int meta_fieldinfo_next(lua_State *state)
+{
+ if (lua_gettop(state) < 2) lua_pushnil(state);
+
+ int len = lua_rawlen(state, UPVAL_FIELDTABLE);
+ int idx = cur_iter_index(state, len+1, 2, 0);
+ if (idx == len)
+ return 0;
+
+ lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1);
+
+ // modified from meta_struct_next:
+ // retrieve the struct_field_info* from the table and convert it
+ lua_dup(state);
+ lua_gettable(state, lua_upvalueindex(1));
+ auto field = static_cast(lua_touserdata(state, -1));
+ lua_pop(state, 1);
+ PushFieldInfoSubTable(state, field);
+
+ return 2;
+}
+
+static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity *pstruct)
+{
+ Lua::StackUnwinder base{state};
+
+ // metatable
+ lua_newtable(state);
+ int ix_meta = lua_gettop(state);
+
+ // field info table (name -> struct_field_info*)
+ lua_newtable(state);
+ int ix_fieldinfo = lua_gettop(state);
+
+ // field iter table (int <-> name)
+ lua_newtable(state);
+ int ix_fielditer = lua_gettop(state);
+ IndexFields(state, base, pstruct, IndexFieldsFlags::RAW);
+
+ PushStructMethod(state, ix_meta, ix_fielditer, meta_fieldinfo_next);
+ // change upvalue 1 to the field info table since we don't need the original
+ lua_pushvalue(state, ix_fieldinfo);
+ lua_setupvalue(state, -2, 1);
+ SetPairsMethod(state, ix_meta, "__pairs");
+
+ // field table (name -> table representation of struct_field_info)
+ lua_newtable(state);
+ int ix_fields = lua_gettop(state);
+
+ // wrapper table (empty, indexes into field table with metamethods)
+ lua_newtable(state);
+ int ix_wrapper = lua_gettop(state);
+
+ // set up metatable for the wrapper
+ // use field table for __index
+ lua_pushstring(state, "__index");
+ lua_pushvalue(state, ix_fieldinfo);
+ lua_pushcclosure(state, meta_fieldinfo_index, 1);
+ lua_settable(state, ix_meta);
+
+ // use change_error() for __newindex
+ lua_pushstring(state, "__newindex");
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
+ lua_settable(state, ix_meta);
+
+ lua_pushvalue(state, ix_meta);
+ lua_setmetatable(state, ix_wrapper);
+
+ // convert field info table (struct_field_info) to field table (lua tables)
+ lua_pushnil(state); // initial key for next()
+ while (lua_next(state, ix_fieldinfo)) {
+ auto field = static_cast(lua_touserdata(state, -1));
+ lua_pushvalue(state, -2); // field name
+ PushFieldInfoSubTable(state, field);
+ lua_settable(state, ix_fields);
+ lua_pop(state, 1); // struct_field_info
+ }
+
+ // lua_pushvalue(state, ix_fields);
+ // freeze_table(state); // TODO: figure out why this creates an __index cycle for nonexistent fields
+ lua_pushvalue(state, ix_wrapper);
+ lua_setfield(state, ftable_idx, "_fields");
+}
+
void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct)
{
// stack: metatable fieldtable
-
+ AddFieldInfoTable(state, ftable_idx, pstruct);
for (struct_identity *p = pstruct; p; p = p->getParent())
{
auto fields = p->getFields();
@@ -1384,8 +1562,7 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
// Index the fields
lua_newtable(state);
-
- IndexFields(state, base, pstruct, globals);
+ IndexFields(state, base, pstruct, globals ? IndexFieldsFlags::GLOBALS : 0);
// Add the iteration metamethods
PushStructMethod(state, base+1, base+3, iterator);
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp
index 59bd96732..9d2357c70 100644
--- a/library/LuaWrapper.cpp
+++ b/library/LuaWrapper.cpp
@@ -685,7 +685,8 @@ static int meta_new(lua_State *state)
type_identity *id = get_object_identity(state, 1, "df.new()", true);
- void *ptr;
+ void *ptr = nullptr;
+ std::string err_context;
// Support arrays of primitive types
if (argc == 2)
@@ -703,11 +704,22 @@ static int meta_new(lua_State *state)
}
else
{
- ptr = id->allocate();
+ try {
+ ptr = id->allocate();
+ }
+ catch (std::exception &e) {
+ if (e.what()) {
+ err_context = e.what();
+ }
+ }
}
if (!ptr)
- luaL_error(state, "Cannot allocate %s", id->getFullName().c_str());
+ luaL_error(state, "Cannot allocate %s%s%s",
+ id->getFullName().c_str(),
+ err_context.empty() ? "" : ": ",
+ err_context.c_str()
+ );
if (lua_isuserdata(state, 1))
{
@@ -1658,6 +1670,7 @@ static void RenderType(lua_State *state, compound_identity *node)
{
RenderTypeChildren(state, node->getScopeChildren());
+ IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
lua_pushlightuserdata(state, node);
lua_setfield(state, ftable, "_identity");
diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp
index b959e756e..a90cf1208 100644
--- a/library/MiscUtils.cpp
+++ b/library/MiscUtils.cpp
@@ -27,6 +27,8 @@ distribution.
#include "MiscUtils.h"
#include "ColorText.h"
+#include "modules/DFSDL.h"
+
#ifndef LINUX_BUILD
// We don't want min and max macros
#define NOMINMAX
@@ -34,6 +36,7 @@ distribution.
#else
#include
#include
+ #include
#endif
#include
@@ -470,3 +473,29 @@ DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::stri
{
return out.is_console() ? DF2CONSOLE(in) : in;
}
+
+DFHACK_EXPORT std::string cxx_demangle(const std::string &mangled_name, std::string *status_out)
+{
+#ifdef __GNUC__
+ int status;
+ char *demangled = abi::__cxa_demangle(mangled_name.c_str(), nullptr, nullptr, &status);
+ std::string out;
+ if (demangled) {
+ out = demangled;
+ free(demangled);
+ }
+ if (status_out) {
+ if (status == 0) *status_out = "success";
+ else if (status == -1) *status_out = "memory allocation failure";
+ else if (status == -2) *status_out = "invalid mangled name";
+ else if (status == -3) *status_out = "invalid arguments";
+ else *status_out = "unknown error";
+ }
+ return out;
+#else
+ if (status_out) {
+ *status_out = "not implemented on this platform";
+ }
+ return "";
+#endif
+}
diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp
index e50750015..0509f5710 100644
--- a/library/Process-linux.cpp
+++ b/library/Process-linux.cpp
@@ -138,7 +138,7 @@ void Process::getMemRanges( vector & ranges )
{
t_memrange temp;
temp.name[0] = 0;
- sscanf(buffer, "%zx-%zx %s %zx %2zx:%2zx %zu %[^\n]",
+ sscanf(buffer, "%zx-%zx %s %zx %zx:%zx %zu %[^\n]",
&start,
&end,
(char*)&permissions,
diff --git a/library/include/Core.h b/library/include/Core.h
index 696be4ead..b470ebbde 100644
--- a/library/include/Core.h
+++ b/library/include/Core.h
@@ -40,8 +40,6 @@ distribution.
#include
#include
-#include "RemoteClient.h"
-
#define DFH_MOD_SHIFT 1
#define DFH_MOD_CTRL 2
#define DFH_MOD_ALT 4
@@ -74,6 +72,17 @@ namespace DFHack
struct Hide;
}
+ enum command_result
+ {
+ CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error
+ CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console
+ CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded
+ CR_OK = 0, // Success
+ CR_FAILURE = 1, // Failure
+ CR_WRONG_USAGE = 2, // Wrong arguments or ui state
+ CR_NOT_FOUND = 3 // Target object not found (for RPC mainly)
+ };
+
enum state_change_event
{
SC_UNKNOWN = -1,
@@ -97,10 +106,14 @@ namespace DFHack
StateChangeScript(state_change_event event, std::string path, bool save_specific = false)
:event(event), path(path), save_specific(save_specific)
{ }
- bool operator==(const StateChangeScript& other)
+ bool const operator==(const StateChangeScript& other)
{
return event == other.event && path == other.path && save_specific == other.save_specific;
}
+ bool const operator!=(const StateChangeScript& other)
+ {
+ return !(operator==(other));
+ }
};
// Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
@@ -112,15 +125,11 @@ namespace DFHack
friend void ::dfhooks_shutdown();
friend void ::dfhooks_update();
friend void ::dfhooks_prerender();
- friend bool ::dfhooks_sdl_event(SDL::Event* event);
+ friend bool ::dfhooks_sdl_event(SDL_Event* event);
friend bool ::dfhooks_ncurses_key(int key);
public:
/// Get the single Core instance or make one.
- static Core& getInstance()
- {
- static Core instance;
- return instance;
- }
+ static Core& getInstance();
/// check if the activity lock is owned by this thread
bool isSuspended(void);
/// Is everything OK?
@@ -150,6 +159,9 @@ namespace DFHack
std::string findScript(std::string name);
void getScriptPaths(std::vector *dest);
+ bool getSuppressDuplicateKeyboardEvents();
+ void setSuppressDuplicateKeyboardEvents(bool suppress);
+
bool ClearKeyBindings(std::string keyspec);
bool AddKeyBinding(std::string keyspec, std::string cmdline);
std::vector ListKeyBindings(std::string keyspec);
@@ -168,7 +180,7 @@ namespace DFHack
bool isWorldLoaded() { return (last_world_data_ptr != NULL); }
bool isMapLoaded() { return (last_local_map_ptr != NULL && last_world_data_ptr != NULL); }
- static df::viewscreen *getTopViewscreen() { return getInstance().top_viewscreen; }
+ static df::viewscreen *getTopViewscreen();
DFHack::Console &getConsole() { return con; }
@@ -195,7 +207,7 @@ namespace DFHack
bool InitSimulationThread();
int Update (void);
int Shutdown (void);
- bool DFH_SDL_Event(SDL::Event* event);
+ bool DFH_SDL_Event(SDL_Event* event);
bool ncurses_wgetch(int in, int & out);
bool DFH_ncurses_key(int key);
@@ -240,8 +252,8 @@ namespace DFHack
};
int8_t modstate;
+ bool suppress_duplicate_keyboard_events;
std::map > key_bindings;
- std::map hotkey_states;
std::string hotkey_cmd;
enum hotkey_set_t {
NO,
diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h
index 174156e76..c440c1eb7 100644
--- a/library/include/DataDefs.h
+++ b/library/include/DataDefs.h
@@ -265,6 +265,7 @@ namespace DFHack
type_identity *ref_target;
const char *union_tag_field;
const char *union_tag_attr;
+ const char *original_name;
};
struct struct_field_info {
@@ -497,11 +498,29 @@ namespace df
using DFHack::DfLinkedList;
using DFHack::DfOtherVectors;
+ template
+ typename std::enable_if<
+ std::is_copy_assignable::value,
+ void*
+ >::type allocator_try_assign(void *out, const void *in) {
+ *(T*)out = *(const T*)in;
+ return out;
+ }
+
+ template
+ typename std::enable_if<
+ !std::is_copy_assignable::value,
+ void*
+ >::type allocator_try_assign(void *out, const void *in) {
+ // assignment is not possible; do nothing
+ return NULL;
+ }
+
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
template
void *allocator_fn(void *out, const void *in) {
- if (out) { *(T*)out = *(const T*)in; return out; }
+ if (out) { return allocator_try_assign(out, in); }
else if (in) { delete (T*)in; return (T*)in; }
else return new T();
}
@@ -514,6 +533,13 @@ namespace df
else return new T();
}
+ template
+ void *allocator_noassign_fn(void *out, const void *in) {
+ if (out) { return NULL; }
+ else if (in) { delete (T*)in; return (T*)in; }
+ else return new T();
+ }
+
template
struct identity_traits {
static compound_identity *get() { return &T::_identity; }
diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h
index fd47429b9..035f59d4b 100644
--- a/library/include/DataIdentity.h
+++ b/library/include/DataIdentity.h
@@ -25,13 +25,21 @@ distribution.
#pragma once
#include
-#include
+#include
+#include