Merge branch 'develop' into remoteserver_accept_fix

develop
Myk 2023-11-06 17:29:54 -08:00 committed by GitHub
commit d65ef21251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
286 changed files with 13768 additions and 7699 deletions

@ -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
----------------------------------
<details>
<summary>Highlight 1, Highlight 2</summary>
### Highlight 1
Demo screenshot/vidcap
Text
### Highlight 2
Demo screenshot/vidcap
Text
</details>
Announcements
----------------------------------
<details>
<summary>Annc 1, PSAs</summary>
### 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.
</details>
Generated release notes
====================
<details>
<summary>New tools, fixes, and improvements</summary>
%RELEASE_NOTES%
</details>

@ -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

@ -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

@ -3,218 +3,32 @@ name: Build
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: test:
runs-on: ${{ matrix.os }} uses: ./.github/workflows/test.yml
name: build (Linux, GCC ${{ matrix.gcc }}, ${{ matrix.plugins }} plugins) with:
strategy: dfhack_ref: ${{ github.ref }}
fail-fast: false secrets: inherit
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"
build-cross-win64: package:
name: Build MSVC win64 uses: ./.github/workflows/package.yml
runs-on: ubuntu-22.04 with:
steps: dfhack_ref: ${{ github.ref }}
- name: Install dependencies secrets: inherit
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/*
docs: docs:
runs-on: ubuntu-22.04 uses: ./.github/workflows/build-linux.yml
steps: with:
- name: Set up Python 3 dfhack_ref: ${{ github.ref }}
uses: actions/setup-python@v4 platform-files: false
with: common-files: false
python-version: 3 docs: true
- name: Install dependencies secrets: inherit
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
lint: lint:
runs-on: ubuntu-22.04 uses: ./.github/workflows/lint.yml
steps: with:
- name: Set up Python 3 dfhack_ref: ${{ github.ref }}
uses: actions/setup-python@v4 secrets: inherit
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
check-pr: check-pr:
runs-on: ubuntu-latest runs-on: ubuntu-latest

@ -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 }}

@ -1,5 +1,7 @@
name: Clean up PR caches name: Clean up PR caches
on: on:
workflow_call:
pull_request_target: pull_request_target:
types: types:
- closed - closed
@ -18,7 +20,7 @@ jobs:
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
echo "Fetching list of cache keys" 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 set +e
echo "Deleting caches..." echo "Deleting caches..."

@ -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 }}

@ -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

@ -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

@ -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' }}

@ -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 }}

@ -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

7
.gitignore vendored

@ -1,7 +1,7 @@
# linux backup files # linux backup files
*~ *~
#Kdevelop project files # Kdevelop project files
*.kdev4 *.kdev4
.kdev4 .kdev4
@ -70,7 +70,7 @@ tags
# Mac OS X .DS_Store files # Mac OS X .DS_Store files
.DS_Store .DS_Store
#VS is annoying about this one. # VS is annoying about this one.
/build/win64/DF_PATH.txt /build/win64/DF_PATH.txt
/build/win32/DF_PATH.txt /build/win32/DF_PATH.txt
/.vs /.vs
@ -81,5 +81,6 @@ tags
# external plugins # external plugins
/plugins/CMakeLists.custom.txt /plugins/CMakeLists.custom.txt
# steam api # 3rd party downloads
depends/steam depends/steam
depends/SDL2

@ -4,7 +4,7 @@ ci:
repos: repos:
# shared across repos: # shared across repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.5.0
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
@ -20,11 +20,11 @@ repos:
args: ['--fix=lf'] args: ['--fix=lf']
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.23.1 rev: 0.27.1
hooks: hooks:
- id: check-github-workflows - id: check-github-workflows
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.1 rev: v1.5.4
hooks: hooks:
- id: forbid-tabs - id: forbid-tabs
exclude_types: exclude_types:

@ -7,8 +7,8 @@ cmake_policy(SET CMP0074 NEW)
project(dfhack) project(dfhack)
# set up versioning. # set up versioning.
set(DF_VERSION "50.08") set(DF_VERSION "50.11")
set(DFHACK_RELEASE "r4") set(DFHACK_RELEASE "r2")
set(DFHACK_PRERELEASE FALSE) set(DFHACK_PRERELEASE FALSE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
@ -30,14 +30,16 @@ else(CMAKE_CONFIGURATION_TYPES)
endif(CMAKE_CONFIGURATION_TYPES) endif(CMAKE_CONFIGURATION_TYPES)
option(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF) 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) 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) macro(CHECK_GCC compiler_path)
execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT) execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT)
string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT) string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT)
if(${GCC_VERSION_OUT} VERSION_LESS "4.8") if(${GCC_VERSION_OUT} VERSION_LESS "10")
message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.8 or later") message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 10 or later")
elseif(${GCC_VERSION_OUT} VERSION_GREATER "4.9.9") # 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. # GCC 5 changes ABI name mangling to enable C++11 changes.
# This must be disabled to enable linking against DF. # This must be disabled to enable linking against DF.
# http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/ # http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/
@ -60,14 +62,14 @@ endif()
if(WIN32) if(WIN32)
if(NOT MSVC) if(NOT MSVC)
message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1935 is required.") message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1937 is required.")
elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1935)) elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1937))
message(SEND_ERROR "MSVC 2022 version 1930 to 1935 is required, Version Found: ${MSVC_VERSION}") message(SEND_ERROR "MSVC 2022 version 1930 to 1937 is required, Version Found: ${MSVC_VERSION}")
endif() endif()
endif() endif()
# Ask for C++11 standard from compilers # Ask for C++-20 standard from compilers
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 20)
# Require the standard support from compilers. # Require the standard support from compilers.
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Use only standard c++ to keep code portable # Use only standard c++ to keep code portable
@ -209,28 +211,23 @@ set(DFHACK_BINARY_DESTINATION .)
set(DFHACK_PLUGIN_DESTINATION ${DFHACK_DATA_DESTINATION}/plugins) set(DFHACK_PLUGIN_DESTINATION ${DFHACK_DATA_DESTINATION}/plugins)
# dfhack lua files go here: # dfhack lua files go here:
set(DFHACK_LUA_DESTINATION ${DFHACK_DATA_DESTINATION}/lua) 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: # user documentation goes here:
set(DFHACK_USERDOC_DESTINATION ${DFHACK_DATA_DESTINATION}) 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 # some options for the user/developer to play with
option(BUILD_LIBRARY "Build the DFHack library." ON) option(BUILD_LIBRARY "Build the DFHack library." ON)
option(BUILD_PLUGINS "Build the DFHack plugins." 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) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
if(UNIX) if(UNIX)
## flags for GCC ## flags for GCC
# default to hidden symbols # default to hidden symbols
# ensure compatibility with older CPUs # ensure compatibility with older CPUs
# enable C++11 features
add_definitions(-DLINUX_BUILD) add_definitions(-DLINUX_BUILD)
add_definitions(-D_GLIBCXX_USE_C99) set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror -Wl,--disable-new-dtags")
set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMMON_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMMON_FLAGS}")
if(DFHACK_BUILD_64) if(DFHACK_BUILD_64)
@ -292,22 +289,21 @@ endif()
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
include_directories(${ZLIB_INCLUDE_DIRS}) include_directories(${ZLIB_INCLUDE_DIRS})
if(WIN32) if(BUILD_LIBRARY)
# Do the same for SDL.dll # Download SDL release and extract into depends in the build dir
# (DFHack doesn't require this at build time, so no need to move it to the build folder) # all we need are the header files (including generated headers), so the same release package
# TODO: remove SDL.dll from our distribution once DF moves to SDL2. we only # will work for all platforms
# continue to include it so we don't break Steam players on update by removing # (the above statement is untested for OSX)
# the SDL.dll that DF needs. set(SDL_VERSION 2.26.2)
set(SDL_DOWNLOAD_DIR ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}) set(SDL_ZIP_MD5 574daf26d48de753d0b1e19823c9d8bb)
if(${DFHACK_BUILD_ARCH} STREQUAL "64") set(SDL_ZIP_FILE SDL2-devel-${SDL_VERSION}-VC.zip)
download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-SDL.dll" set(SDL_ZIP_PATH ${dfhack_SOURCE_DIR}/depends/SDL2/)
${SDL_DOWNLOAD_DIR}/SDL.dll download_file("https://github.com/libsdl-org/SDL/releases/download/release-${SDL_VERSION}/${SDL_ZIP_FILE}"
"1ae242c4b94cb03756a1288122a66faf") ${SDL_ZIP_PATH}${SDL_ZIP_FILE}
else() ${SDL_ZIP_MD5})
download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-SDL.dll" file(ARCHIVE_EXTRACT INPUT ${SDL_ZIP_PATH}${SDL_ZIP_FILE}
${SDL_DOWNLOAD_DIR}/SDL.dll DESTINATION ${SDL_ZIP_PATH})
"5a09604daca6b2b5ce049d79af935d6a") include_directories(${SDL_ZIP_PATH}/SDL2-${SDL_VERSION}/include)
endif()
endif() endif()
if(APPLE) if(APPLE)
@ -391,14 +387,17 @@ include_directories(depends/lodepng)
include_directories(depends/tthread) include_directories(depends/tthread)
include_directories(depends/clsocket/src) include_directories(depends/clsocket/src)
include_directories(depends/xlsxio/include) include_directories(depends/xlsxio/include)
add_subdirectory(depends)
if(BUILD_LIBRARY)
add_subdirectory(depends)
endif()
# Testing with CTest # Testing with CTest
macro(dfhack_test name files) 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}) add_executable(${name} ${files})
target_include_directories(${name} PUBLIC depends/googletest/googletest/include) 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}) add_test(NAME ${name} COMMAND ${name})
endif() endif()
endmacro() endmacro()
@ -410,22 +409,25 @@ if(NOT GIT_FOUND)
endif() endif()
# build the lib itself # build the lib itself
add_subdirectory(library)
if(BUILD_LIBRARY) if(BUILD_LIBRARY)
add_subdirectory(library) file(WRITE ${CMAKE_BINARY_DIR}/dfhack_setarch.txt ${DFHACK_SETARCH})
install(FILES LICENSE.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES ${CMAKE_BINARY_DIR}/dfhack_setarch.txt DESTINATION ${DFHACK_DATA_DESTINATION})
install(FILES docs/changelog-placeholder.txt DESTINATION ${DFHACK_USERDOC_DESTINATION} RENAME changelog.txt)
endif() 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 # 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() endif()
add_subdirectory(data) if(INSTALL_SCRIPTS)
add_subdirectory(scripts) add_subdirectory(scripts)
endif()
if(BUILD_DOCS) if(BUILD_DOCS)
find_package(Python3) find_package(Python3)
@ -466,7 +468,14 @@ if(BUILD_DOCS)
"${CMAKE_CURRENT_SOURCE_DIR}/conf.py" "${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( set_property(
DIRECTORY PROPERTY ADDITIONAL_CLEAN_FILES TRUE DIRECTORY PROPERTY ADDITIONAL_CLEAN_FILES TRUE
"${CMAKE_CURRENT_SOURCE_DIR}/docs/changelogs" "${CMAKE_CURRENT_SOURCE_DIR}/docs/changelogs"
@ -483,9 +492,10 @@ if(BUILD_DOCS)
"${CMAKE_BINARY_DIR}/docs/text" "${CMAKE_BINARY_DIR}/docs/text"
"${CMAKE_BINARY_DIR}/docs/xml" "${CMAKE_BINARY_DIR}/docs/xml"
) )
add_custom_command(OUTPUT ${SPHINX_OUTPUT} add_custom_command(OUTPUT ${SPHINX_OUTPUT}
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/docs/build.py" 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} DEPENDS ${SPHINX_DEPS}
COMMENT "Building documentation with Sphinx" COMMENT "Building documentation with Sphinx"
) )
@ -498,10 +508,12 @@ if(BUILD_DOCS)
add_custom_command(TARGET dfhack_docs POST_BUILD add_custom_command(TARGET dfhack_docs POST_BUILD
COMMAND ${CMAKE_COMMAND} -E touch ${SPHINX_OUTPUT}) COMMAND ${CMAKE_COMMAND} -E touch ${SPHINX_OUTPUT})
install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/ if(NOT BUILD_DOCS_NO_HTML)
DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/html/
FILES_MATCHING PATTERN "*" DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs
PATTERN html/_sources EXCLUDE) FILES_MATCHING PATTERN "*"
PATTERN html/_sources EXCLUDE)
endif()
install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/ install(DIRECTORY ${dfhack_SOURCE_DIR}/docs/text/
DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs) DESTINATION ${DFHACK_USERDOC_DESTINATION}/docs)
install(FILES docs/changelogs/news.rst docs/changelogs/news-dev.rst DESTINATION ${DFHACK_USERDOC_DESTINATION}) 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) set(DFHACK_BUILD_ARCH_PREV "${DFHACK_BUILD_ARCH}" CACHE STRING "Previous build architecture" FORCE)
option(BUILD_SIZECHECK "Build the sizecheck library, for research" OFF) option(BUILD_SIZECHECK "Build the sizecheck library, for research" OFF)
if(BUILD_SIZECHECK) if(BUILD_LIBRARY AND BUILD_SIZECHECK)
add_subdirectory(depends/sizecheck) add_subdirectory(depends/sizecheck)
add_dependencies(dfhack sizecheck) add_dependencies(dfhack sizecheck)
endif() endif()

@ -45,7 +45,7 @@ if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/b
-e steam_password \ -e steam_password \
--name dfhack-win \ --name dfhack-win \
ghcr.io/dfhack/build-env:msvc \ 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 ; then
echo echo
echo "Build failed" echo "Build failed"

@ -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()

@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import glob import glob
import itertools
import sys import sys
actual = {'': {}} actual = {'': {}}
SEP = ('=' * 80)
with open(sys.argv[1]) as f: with open(sys.argv[1]) as f:
plugin_name = '' plugin_name = ''
@ -26,7 +28,7 @@ for p in glob.iglob('library/proto/*.proto'):
parts = line.split(' ') parts = line.split(' ')
expected[''][parts[2]] = (parts[4], parts[6]) 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 = '' plugin_name = ''
with open(p) as f: with open(p) as f:
for line in f: for line in f:
@ -53,6 +55,7 @@ for plugin_name in actual:
methods = actual[plugin_name] methods = actual[plugin_name]
if plugin_name not in expected: if plugin_name not in expected:
print(SEP)
print('Missing documentation for plugin proto files: ' + plugin_name) print('Missing documentation for plugin proto files: ' + plugin_name)
print('Add the following lines:') print('Add the following lines:')
print('// Plugin: ' + plugin_name) print('// Plugin: ' + plugin_name)
@ -73,12 +76,14 @@ for plugin_name in actual:
missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1]) missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0: if len(missing) > 0:
print(SEP)
print('Incomplete documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Add the following lines:') print('Incomplete documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Add the following lines:')
for m in missing: for m in missing:
print(m) print(m)
error_count += 1 error_count += 1
if len(wrong) > 0: if len(wrong) > 0:
print(SEP)
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Replace the following comments:') print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Replace the following comments:')
for m in wrong: for m in wrong:
print(m) print(m)
@ -88,6 +93,7 @@ for plugin_name in expected:
methods = expected[plugin_name] methods = expected[plugin_name]
if plugin_name not in actual: if plugin_name not in actual:
print(SEP)
print('Incorrect documentation for plugin proto files: ' + plugin_name) print('Incorrect documentation for plugin proto files: ' + plugin_name)
print('The following methods are documented, but the plugin does not provide any RPC methods:') print('The following methods are documented, but the plugin does not provide any RPC methods:')
for m in methods: for m in methods:
@ -102,6 +108,7 @@ for plugin_name in expected:
missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1]) missing.append('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0: if len(missing) > 0:
print(SEP)
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Remove the following lines:') print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Remove the following lines:')
for m in missing: for m in missing:
print(m) print(m)

@ -1,52 +1,53 @@
#!/bin/sh #!/bin/sh
DF_FOLDER=$1
OS_TARGET=$2
DF_VERSION=$3
set -e set -e
df_tardest="df.tar.bz2" minor=$(echo "$DF_VERSION" | cut -d. -f1)
save_tardest="test_save.tgz" patch=$(echo "$DF_VERSION" | cut -d. -f2)
df_url="https://www.bay12games.com/dwarves/df_${minor}_${patch}"
cd "$(dirname "$0")" if test "$OS_TARGET" = "windows"; then
echo "DF_VERSION: $DF_VERSION" WGET="C:/msys64/usr/bin/wget.exe"
echo "DF_FOLDER: $DF_FOLDER" df_url="${df_url}_win_s.zip"
mkdir -p "$DF_FOLDER" df_archive_name="df.zip"
# back out of df_linux df_extract_cmd="unzip -d ${DF_FOLDER}"
cd "$DF_FOLDER/.." elif test "$OS_TARGET" = "ubuntu"; then
WGET=wget
if ! test -f "$df_tardest"; then df_url="${df_url}_linux.tar.bz2"
minor=$(echo "$DF_VERSION" | cut -d. -f2) df_archive_name="df.tar.bz2"
patch=$(echo "$DF_VERSION" | cut -d. -f3) df_extract_cmd="tar -x -j -C ${DF_FOLDER} -f"
echo "Downloading DF $DF_VERSION" else
while read url; do echo "Unhandled OS target: ${OS_TARGET}"
echo "Attempting download: ${url}" exit 1
if wget -v "$url" -O "$df_tardest"; then fi
break
fi if ! $WGET -v "$df_url" -O "$df_archive_name"; then
done <<URLS echo "Failed to download DF from $df_url"
https://www.bay12games.com/dwarves/df_${minor}_${patch}_linux.tar.bz2 exit 1
https://files.dfhack.org/DF/${minor}.${patch}/df_${minor}_${patch}_linux.tar.bz2 fi
URLS
echo $df_tardest md5sum "$df_archive_name"
if ! test -f "$df_tardest"; then
echo "DF failed to download: $df_tardest not found" save_url="https://dffd.bay12games.com/download.php?id=15434&f=dreamfort.7z"
exit 1 save_archive_name="test_save.7z"
fi save_extract_cmd="7z x -o${DF_FOLDER}/save"
echo "Downloading test save" if ! $WGET -v "$save_url" -O "$save_archive_name"; then
#test_save_url="https://files.dfhack.org/DF/0.${minor}.${patch}/test_save.tgz" echo "Failed to download test save from $save_url"
test_save_url="https://drive.google.com/uc?export=download&id=1XvYngl-DFONiZ9SD9OC4B2Ooecu8rPFz" exit 1
if ! wget -v "$test_save_url" -O "$save_tardest"; then
echo "failed to download test save"
exit 1
fi
echo $save_tardest
fi fi
rm -rf df_linux md5sum "$save_archive_name"
mkdir -p df_linux/save
echo Extracting echo Extracting
tar xf "$df_tardest" --strip-components=1 -C df_linux mkdir -p ${DF_FOLDER}
tar xf "$save_tardest" -C df_linux/save $df_extract_cmd "$df_archive_name"
$save_extract_cmd "$save_archive_name"
mv ${DF_FOLDER}/save/* ${DF_FOLDER}/save/region1
echo Done echo Done
ls -l ls -l

@ -55,33 +55,32 @@ if os.path.exists(test_status_file):
os.remove(test_status_file) os.remove(test_status_file)
print('Backing up init.txt to init.txt.orig') print('Backing up init.txt to init.txt.orig')
init_txt_path = 'data/init/init.txt' default_init_txt_path = 'data/init/init_default.txt'
prefs_path = 'prefs'
init_txt_path = 'prefs/init.txt'
if not os.path.exists(init_txt_path):
os.makedirs(prefs_path, exist_ok=True)
shutil.copyfile(default_init_txt_path, init_txt_path)
shutil.copyfile(init_txt_path, init_txt_path + '.orig') shutil.copyfile(init_txt_path, init_txt_path + '.orig')
with open(init_txt_path) as f: with open(init_txt_path) as f:
init_contents = f.read() init_contents = f.read()
init_contents = change_setting(init_contents, 'INTRO', 'NO')
init_contents = change_setting(init_contents, 'SOUND', 'NO') init_contents = change_setting(init_contents, 'SOUND', 'NO')
init_contents = change_setting(init_contents, 'WINDOWED', 'YES') init_contents = change_setting(init_contents, 'WINDOWED', 'YES')
init_contents = change_setting(init_contents, 'WINDOWEDX', '80') init_contents = change_setting(init_contents, 'WINDOWEDX', '1200')
init_contents = change_setting(init_contents, 'WINDOWEDY', '25') init_contents = change_setting(init_contents, 'WINDOWEDY', '800')
init_contents = change_setting(init_contents, 'FPS', 'YES') #if args.headless:
if args.headless: # init_contents = change_setting(init_contents, 'PRINT_MODE', 'TEXT')
init_contents = change_setting(init_contents, 'PRINT_MODE', 'TEXT')
init_path = 'dfhack-config/init' init_path = 'dfhack-config/init'
if not os.path.isdir('hack/init'): if not os.path.isdir('hack/init'):
# we're on an old branch that still reads init files from the root dir # we're on an old branch that still reads init files from the root dir
init_path = '.' init_path = '.'
try: os.makedirs(init_path, exist_ok=True)
os.mkdir(init_path)
except OSError as error:
# ignore already exists errors
pass
test_init_file = os.path.join(init_path, 'dfhackzzz_test.init') # Core sorts these alphabetically test_init_file = os.path.join(init_path, 'dfhackzzz_test.init') # Core sorts these alphabetically
with open(test_init_file, 'w') as f: with open(test_init_file, 'w') as f:
f.write(''' f.write('''
devel/dump-rpc dfhack-rpc.txt devel/dump-rpc dfhack-rpc.txt
:lua dfhack.internal.addScriptPath(dfhack.getHackPath())
test --resume -- lua scr.breakdown_level=df.interface_breakdown_types.%s test --resume -- lua scr.breakdown_level=df.interface_breakdown_types.%s
''' % ('NONE' if args.no_quit else 'QUIT')) ''' % ('NONE' if args.no_quit else 'QUIT'))

@ -1,11 +1,13 @@
-- DFHack developer test harness -- DFHack developer test harness
--@ module = true --@ module = true
local expect = require 'test_util.expect' local expect = require('test_util.expect')
local json = require 'json' local gui = require('gui')
local mock = require 'test_util.mock' local helpdb = require('helpdb')
local script = require 'gui.script' local json = require('json')
local utils = require 'utils' local mock = require('test_util.mock')
local script = require('gui.script')
local utils = require('utils')
local help_text = local help_text =
[====[ [====[
@ -13,49 +15,59 @@ local help_text =
test test
==== ====
Run DFHack tests. Tags: dev
Usage: Command: "test"
Run DFHack regression tests.
Discover DFHack functionality that has broken due to recent changes in DF or DFHack.
Usage
-----
test [<options>] [<done_command>] test [<options>] [<done_command>]
If a done_command is specified, it will be run after the tests complete. If a done_command is specified, it will be run after the tests complete.
Options: Options
-------
-h, --help display this help message and exit.
-d, --test_dir specifies which directory to look in for tests. defaults to -d, --test_dir specifies which directory to look in for tests. defaults to
the "hack/scripts/test" folder in your DF installation. the "hack/scripts/test" folder in your DF installation.
-m, --modes only run tests in the given comma separated list of modes. -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 see the next section for a list of valid modes. if not
specified, the tests are not filtered by modes. specified, the tests are not filtered by modes.
-r, --resume skip tests that have already been run. remove the -r, --resume skip tests that have already been run. remove the
test_status.json file to reset the record. test_status.json file to reset the record.
-s, --save_dir the save folder to load for "fortress" mode tests. this -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 save is only loaded if a fort is not already loaded when
a "fortress" mode test is run. if not specified, defaults to a "fortress" mode test is run. if not specified, defaults to
'region1'. 'region1'.
-t, --tests only run tests that match one of the comma separated list of -t, --tests only run tests that match one of the comma separated list of
patterns. if not specified, no tests are filtered. patterns. if not specified, no tests are filtered and all tessts
are run.
Modes:
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 none the test can be run on any screen
fortress the test must be run while a map is loaded. if the game is title the test must be run on the DF title screen. note that if the game
currently on the title screen, the save specified by the save_dir has a map loaded, "title" mode tests cannot be run
parameter will be loaded. 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
Examples: parameter will be loaded.
test runs all tests Examples
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 runs all tests
test -t quickfort runs quickfort tests test -r runs all tests that haven't been run before
test -d /path/to/dfhack-scripts/repo/test test -m none runs tests that don't need the game to be in a
runs tests in your dev scripts repo 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 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 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.require = clean_require
test_envvars.reqscript = clean_reqscript test_envvars.reqscript = clean_reqscript
local function is_title_screen(scr) local function is_title_screen()
scr = scr or dfhack.gui.getCurViewscreen() return dfhack.gui.matchFocusString('title/Default')
return df.viewscreen_titlest:is_instance(scr) 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 end
-- This only handles pre-fortress-load screens. It will time out if the player -- 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 -- has already loaded a fortress or is in any screen that can't get to the title
-- screen by sending ESC keys. -- screen by sending ESC keys.
local function ensure_title_screen() 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 for i = 1, 100 do
local scr = dfhack.gui.getCurViewscreen() local scr = dfhack.gui.getCurViewscreen()
if is_title_screen(scr) then if is_title_screen() then
print('Found title screen') print('Found title screen')
return return
end end
@ -160,57 +191,94 @@ local function ensure_title_screen()
if i % 10 == 0 then print('Looking for title screen...') end if i % 10 == 0 then print('Looking for title screen...') end
end end
qerror(string.format('Could not find title screen (timed out at %s)', qerror(string.format('Could not find title screen (timed out at %s)',
dfhack.gui.getCurFocus(true))) dfhack.gui.getCurFocus(true)[1]))
end end
local function is_fortress(focus_string) local function is_fortress()
focus_string = focus_string or dfhack.gui.getCurFocus(true) return dfhack.gui.matchFocusString('dwarfmode/Default')
return focus_string == '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 end
-- Requires that a fortress game is already loaded or is ready to be loaded via -- 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. -- will time out and/or exit with error.
local function ensure_fortress(config) local function ensure_fortress(config)
local focus_string = dfhack.gui.getCurFocus(true)
for screen_timeout = 1,10 do for screen_timeout = 1,10 do
if is_fortress(focus_string) then if is_fortress() then
print('Loaded fortress map') print('Fortress map is loaded')
-- pause the game (if it's not already paused) -- pause the game (if it's not already paused)
dfhack.gui.resetDwarfmodeView(true) dfhack.gui.resetDwarfmodeView(true)
return return
end end
local scr = dfhack.gui.getCurViewscreen(true) local scr = dfhack.gui.getCurViewscreen()
if focus_string == 'title' or if dfhack.gui.matchFocusString('title/Default', scr) then
focus_string == 'dfhack/lua/load_screen' 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 -- qerror()'s on falure
dfhack.run_script('load-save', config.save_dir) -- dfhack.run_script('load-save', config.save_dir)
elseif focus_string ~= 'loadgame' then 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 -- if we're not actively loading a game, hope we're in
-- a screen where hitting ESC will get us to the game map -- a screen where hitting ESC will get us to the game map
-- or the title screen -- or the title screen
scr:feed_key(df.interface_key.LEAVESCREEN) scr:feed_key(df.interface_key.LEAVESCREEN)
end end
-- wait for current screen to change -- wait for current screen to change
local prev_focus_string = focus_string local prev_focus_string = dfhack.gui.getCurFocus()[1]
for frame_timeout = 1,100 do wait_for(60000, 'screen change', function()
delay(10) return dfhack.gui.getCurFocus()[1] ~= prev_focus_string
focus_string = dfhack.gui.getCurFocus(true) end)
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::
end end
qerror(string.format('Could not load fortress (timed out at %s)', qerror(string.format('Could not load fortress (timed out at %s)',
focus_string)) table.concat(dfhack.gui.getCurFocus(), ' ')))
end end
local MODES = { local MODES = {
@ -236,6 +304,25 @@ local function load_test_config(config_file)
return config return config
end 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 -- 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 -- output doesn't trigger its own dfhack.printerr usage detection (see
-- detect_printerr below) -- detect_printerr below)
@ -280,7 +367,7 @@ end
local function build_test_env(path) local function build_test_env(path)
local env = { local env = {
test = utils.OrderedTable(), test = TestTable(),
-- config values can be overridden in the test file to define -- config values can be overridden in the test file to define
-- requirements for the tests in that file -- requirements for the tests in that file
config = { config = {
@ -352,33 +439,46 @@ local function load_tests(file, tests)
if not code then if not code then
dfhack.printerr('Failed to load file: ' .. tostring(err)) dfhack.printerr('Failed to load file: ' .. tostring(err))
return false return false
else end
dfhack.internal.IN_TEST = true dfhack.internal.IN_TEST = true
local ok, err = dfhack.pcall(code) local ok, err = dfhack.pcall(code)
dfhack.internal.IN_TEST = false dfhack.internal.IN_TEST = false
if not ok then if not ok then
dfhack.printerr('Error when running file: ' .. tostring(err)) dfhack.printerr('Error when running file: ' .. tostring(err))
return false return false
else end
if not MODES[env.config.mode] then if not MODES[env.config.mode] then
dfhack.printerr('Invalid config.mode: ' .. tostring(env.config.mode)) dfhack.printerr('Invalid config.mode: ' .. tostring(env.config.mode))
return false return false
end end
for name, test_func in pairs(env.test) do if not env.config.target then
if env.config.wrapper then dfhack.printerr('Skipping tests for unspecified target in ' .. file)
local fn = test_func return true -- TODO: change to false once existing tests have targets specified
test_func = function() env.config.wrapper(fn) end end
end local targets = type(env.config.target) == 'table' and env.config.target or {env.config.target}
local test_data = { for _,target in ipairs(targets) do
full_name = short_filename .. ':' .. name, if target == 'core' then goto continue end
func = test_func, if type(target) ~= 'string' or not helpdb.is_entry(target) or
private = env_private, helpdb.get_entry_tags(target).unavailable
config = env.config, then
} dfhack.printerr('Skipping tests for unavailable target: ' .. target)
test_data.name = test_data.full_name:gsub('test/', ''):gsub('.lua', '') return true
table.insert(tests, test_data) end
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 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
return true return true
end end
@ -520,6 +620,10 @@ local function filter_tests(tests, config)
end end
local function run_tests(tests, status, counts, config) 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)) print(('Running %d tests'):format(#tests))
local start_ms = dfhack.getTickCount() local start_ms = dfhack.getTickCount()
local num_skipped = 0 local num_skipped = 0
@ -529,6 +633,7 @@ local function run_tests(tests, status, counts, config)
goto skip goto skip
end end
if not MODES[test.config.mode].detect() then 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) local ok, err = pcall(MODES[test.config.mode].navigate, config)
if not ok then if not ok then
MODES[test.config.mode].failed = true MODES[test.config.mode].failed = true
@ -537,12 +642,13 @@ local function run_tests(tests, status, counts, config)
goto skip goto skip
end end
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 if run_test(test, status, counts) then
status[test.full_name] = TestStatus.PASSED status[test.full_name] = TestStatus.PASSED
else save_test_status(status)
status[test.full_name] = TestStatus.FAILED
end end
save_test_status(status)
::skip:: ::skip::
end end
local elapsed_ms = dfhack.getTickCount() - start_ms local elapsed_ms = dfhack.getTickCount() - start_ms
@ -575,7 +681,7 @@ local function dump_df_state()
enabler = { enabler = {
fps = df.global.enabler.fps, fps = df.global.enabler.fps,
gfps = df.global.enabler.gfps, gfps = df.global.enabler.gfps,
fullscreen = df.global.enabler.fullscreen, fullscreen_state = df.global.enabler.fullscreen_state.whole,
}, },
gps = { gps = {
dimx = df.global.gps.dimx, dimx = df.global.gps.dimx,

@ -2,6 +2,7 @@ library/xml master
scripts master scripts master
plugins/stonesense master plugins/stonesense master
plugins/isoworld dfhack plugins/isoworld dfhack
depends/clsocket master
depends/libzip dfhack depends/libzip dfhack
depends/libexpat dfhack depends/libexpat dfhack
depends/xlsxio dfhack depends/xlsxio dfhack

@ -78,8 +78,7 @@ def write_tool_docs():
os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])), os.makedirs(os.path.join('docs/tools', os.path.dirname(k[0])),
mode=0o755, exist_ok=True) mode=0o755, exist_ok=True)
with write_file_if_changed('docs/tools/{}.rst'.format(k[0])) as outfile: 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) outfile.write(include)

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@ -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!" "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):" "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)" "- 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) - Bringing magma up to the industry level to power magma forges/furnaces (see library/pump_stack.csv for help with this)
- Manufacturing trade goods - Manufacturing trade goods
@ -42,7 +42,7 @@ interactively."
"# The dreamfort.csv distributed with DFHack is generated with the following command: "# 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/,*$//'" for fname in dreamfort*.xlsx; do xlsx2csv -a -p '' ""$fname""; done | sed 's/,*$//'"
#notes label(checklist) command checklist #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!" "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!) -- -- Preparation (before you embark!) --
@ -61,7 +61,7 @@ gui/quickfort,/perimeter,,Run at embark. Don't actually apply the blueprint -- i
-- Dig -- -- Dig --
DFHack command,Blueprint,Generate orders,Notes 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,/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) -- -- Core fort (should finish at about the third migration wave) --
DFHack command,Blueprint,Generate orders,Notes 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,/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,/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. 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,/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. 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." 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. 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 -- -- 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! 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) -- -- 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:" "On the gui/control-panel ""Autostart"" tab, additionally enable:"
- autobutcher - autobutcher
- autobutcher target 50 50 14 2 BIRD_GOOSE - autobutcher target 10 10 14 2 BIRD_GOOSE
- autochop - autochop
- autofarm - autofarm
- autofish - autofish
@ -121,6 +121,7 @@ You can save some time by setting up your settings in gui/control-panel before y
- ban-cooking all - ban-cooking all
- buildingplan set boulders false - buildingplan set boulders false
- buildingplan set logs false - buildingplan set logs false
- dwarfvet
- nestboxes - nestboxes
- prioritize - prioritize
- seedwatch - seedwatch
@ -144,9 +145,6 @@ On the work details screen (Labor -> Work details)
In standing orders (Labor -> Standing orders): 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" " - 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""" "- 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." "#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). # 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) - 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)" "- 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 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: 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)" "- 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: Sieges and Prisoner Processing:
Here are some tips and procedures for handling seiges -- including how to clean up afterwards! 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." "- 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:" "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)." "- 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 #dig label(central_stairs_odd) start(2;2) hidden() carved spiral stairs odd levels
`,j6,` `,j6,`
u,`,u 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. 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" Once the marked trees are all chopped down (if any), continue with /surface2.) clear trees and set up pastures"
clear_small/surface_clear_small clear_small/surface_clear_small
burrow_start/surface_burrow_start
zones/surface_zones zones/surface_zones
#> #>
central_stairs/central_stairs repeat(down 10) central_stairs/central_stairs repeat(down 10)
@ -367,7 +368,8 @@ traps/surface_traps
clear_large/surface_clear_large clear_large/surface_clear_large
"" ""
"#meta label(surface7) start(central stairs (on ground level)) message(Remember to enqueue manager orders for this blueprint. "#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 roof/surface_roof
roof2/surface_roof2 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,` ,,,`,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. "#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" 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
,,,,,,,,,,,,,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 #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,`,`,`,`,`,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{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 ,,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,`,`,`,`,`,`,"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 ,,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=""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,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,c ,,c,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,c
,,c,`,`,`,`,`,`,`,`,`,`,`,`,"bnpdz{name=""Bar/military feeder"" containers=0}:-potash+adamantinethread(5x3)",,,,~,`,`,`,`,`,`,`,`,`,`,`,`,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,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,"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,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,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" "#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,`,`,`,`,,,`,`,h,~a,`,`,f,,,`,`,`,`,c,`,`
,,`,c,t,c,`,`,s,,,`,`,`,`,`,`,`,,,s,`,`,c,t,c,` ,,`,c,t,c,`,`,s,,,`,`,`,`,`,`,`,,,s,`,`,c,t,c,`
,,`,`,c,`,c,`,`,,,`,`,`,`,`,`,`,,,`,`,c,`,c,`,` ,,`,`,c,`,c,`,`,,,`,`,`,`,`,`,`,,,`,`,c,`,c,`,`
@ -2467,7 +2536,7 @@ doors/guildhall_doors
,,c,~a,`,`,c,c,`,,~,,`,,`,,`,,~,,`,c,c,`,`,~a,c ,,c,~a,`,`,c,c,`,,~,,`,,`,,`,,~,,`,c,c,`,`,~a,c
,,`,h,`,`,c,c,`,~,`,~,`,,,,`,~,`,~,`,c,c,`,`,h,` ,,`,h,`,`,c,c,`,~,`,~,`,,,,`,~,`,~,`,c,c,`,`,h,`
,,s,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,s ,,s,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,s
,,`,`,`,`,`,`,t,,,,,~,,~,,,,,t,`,`,`,`,`,` ,,F,`,`,`,`,`,t,,,,,~,,~,,,,,t,`,`,`,`,`,F
,,,,,,,~,,,,,,`,~,`,,,,,,~ ,,,,,,,~,,,,,,`,~,`,,,,,,~
,,,,,,,~,,,,,,~,,~,,,,,,~ ,,,,,,,~,,,,,,~,,~,,,,,,~
,,`,`,s,`,`,`,t,,,t,`,`,`,`,`,t,,,t,`,`,`,s,`,` ,,`,`,s,`,`,`,t,,,t,`,`,`,`,`,t,,,t,`,`,`,s,`,`
@ -2476,7 +2545,7 @@ doors/guildhall_doors
,,`,`,c,`,c,`,`,,,t,c,`,`,`,c,t,,,`,`,c,`,c,`,` ,,`,`,c,`,c,`,`,,,t,c,`,`,`,c,t,,,`,`,c,`,c,`,`
,,`,c,t,c,`,`,s,,,t,c,`,`,`,c,t,,,s,`,`,c,t,c,` ,,`,c,t,c,`,`,s,,,t,c,`,`,`,c,t,,,s,`,`,c,t,c,`
,,`,`,c,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,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) #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" "#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,d,d,d,d,,,d,d,d,d,d,,d
,d,d,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,d,d ,d,,d,d,d,d,d,,,d,d,d,d,d,,d,,d,,d,d,d,d,d,,,d,d,d,d,d,,d
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,,,,d,,,,,,,d,,,,d,d,d,,,,d,,,,,,,d,,,,d
,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,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. "#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 #dig label(suites_traffic) start(18; 18; central stairs) hidden() don't path through other dwarves' rooms
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,,,,or,,,,,,,or,,,,`,`,`,,,,or,,,,,,,or,,,,`
,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
,`,`,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,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` ,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,,,or,,,,,,or,,,,oh,`,oh,,,,or,,,,,,or,,,,`,` ,`,,,,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,`,`
,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,,`,` ,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`
,`,`,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,,,,`,` ,`,,,,or,,,,,,,or,,,,oh,`,oh,,,,or,,,,,,,or,,,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` ,`,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,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,or,`,`,`,`,`,,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,,`,`,`,`,`,or,`
,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,` ,`,,`,`,`,`,`,,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,,`,`,`,`,`,,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,,,,or,,,,,,,or,,,,`,`,`,,,,or,,,,,,,or,,,,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
#build label(suites_build) start(18; 18; central stairs) hidden() #build label(suites_build) start(18; 18; central stairs) hidden()
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`
,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
,`,`,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,`,` ,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
,`,`,,a,r,`,`,h,,h,`,`,r,a,,`,s,`,,a,r,`,`,h,,h,`,`,r,a,,`,` ,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,` ,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,` ,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,s,`,,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,,`,` ,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` ,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
,`,`,,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,,`,` ,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,` ,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,h,`,`,`,`,,`,` ,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
,`,`,,a,r,`,`,h,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,h,`,`,r,a,,`,` ,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
,`,`,,,,d,,,,,,d,,,,`,`,`,,,,d,,,,,,d,,,,`,` ,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,` ,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`
,`,`,,s,`,`,s,`,`,s,`,`,s,,`,`,~,`,`,,s,`,`,s,`,`,s,`,`,s,,`,` ,`,`,,s,`,`,s,`,`,s,`,`,s,,`,`,~,`,`,,s,`,`,s,`,`,s,`,`,s,,`,`
,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,` ,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,d,`,`
,`,`,,,,d,,,,,,d,,,,`,`,`,,,,d,,,,,,d,,,,`,` ,`,,,,d,,,,,,,d,,,,`,`,`,,,,d,,,,,,,d,,,,`
,`,`,,a,r,`,`,h,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,h,`,`,r,a,,`,` ,`,,a,r,`,`,h,,,h,`,`,r,a,,d,,d,,a,r,`,`,h,,,h,`,`,r,a,,`
,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,h,`,`,`,`,,`,` ,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,` ,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,` ,`,,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,,`
,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` ,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,`,`,,t,`,s,`,n,,n,`,s,`,t,,`,` ,`,,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,,`
,`,`,,c,`,`,`,f,,f,`,`,`,c,,`,s,`,,c,`,`,`,f,,f,`,`,`,c,,`,` ,`,,t,`,s,`,n,,,n,`,s,`,t,,`,s,`,,t,`,s,`,n,,,n,`,s,`,t,,`
,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,`,`,b,`,`,d,`,` ,`,,c,`,`,`,f,,,f,`,`,`,c,,`,`,`,,c,`,`,`,f,,,f,`,`,`,c,,`
,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,`,`,,`,`,`,`,h,,h,`,`,`,`,,`,` ,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`,`,`,d,`,`,b,`,`,,,`,`,b,`,`,d,`
,`,`,,a,r,`,`,h,,h,`,`,r,a,,`,s,`,,a,r,`,`,h,,h,`,`,r,a,,`,` ,`,,`,`,`,`,h,,,h,`,`,`,`,,`,s,`,,`,`,`,`,h,,,h,`,`,`,`,,`
,`,`,,,,d,,,,,,d,,,,d,,d,,,,d,,,,,,d,,,,`,` ,`,,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" "#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 ,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,`,`,`,`,`,`,`,`,`,`,`,`,`
,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 ,h,f,,h,f,,h,f,,h,f,,h,f,,`,`,`,,f,h,,f,h,,f,h,,f,h,,f,h
,,,,,,,,,,,,,,,,`,`,` ,,,,,,,,,,,,,,,,`,`,`

Can't render this file because it has a wrong number of fields in line 53.

@ -7,6 +7,332 @@
Toady One Toady One
Threetoe 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 # animals
Mouse Mouse
Otter Otter

@ -38,7 +38,7 @@ keybinding add Ctrl-V@dwarfmode digv
keybinding add Ctrl-Shift-V@dwarfmode "digv x" keybinding add Ctrl-Shift-V@dwarfmode "digv x"
# clean the selected tile of blood etc # clean the selected tile of blood etc
keybinding add Ctrl-C spotclean keybinding add Ctrl-C@dwarfmode spotclean
# destroy the selected item # destroy the selected item
keybinding add Ctrl-K@dwarfmode autodump-destroy-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" #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 # gui/design
keybinding add Ctrl-D@dwarfmode gui/design keybinding add Ctrl-D@dwarfmode/Default gui/design

@ -80,6 +80,7 @@
# Enable system services # Enable system services
enable buildingplan enable buildingplan
enable burrow
enable confirm enable confirm
enable logistics enable logistics
enable overlay enable overlay

@ -4,11 +4,19 @@ add_subdirectory(lua)
add_subdirectory(md5) add_subdirectory(md5)
add_subdirectory(protobuf) 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 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) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF)
add_subdirectory(googletest) add_subdirectory(googletest)
if(UNIX) 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()
endif() endif()
@ -31,8 +39,6 @@ option(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependenc
add_subdirectory(clsocket) add_subdirectory(clsocket)
ide_folder(clsocket "Depends") 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 # set the default values of libexpat options - the descriptions are left empty
# because later option() calls *do* override those # because later option() calls *do* override those
set(EXPAT_BUILD_EXAMPLES OFF CACHE BOOL "") set(EXPAT_BUILD_EXAMPLES OFF CACHE BOOL "")

@ -1 +1 @@
Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094 Subproject commit 8cf949340e22001bee1ca25c9d6c1d6a89e8faf2

@ -1 +1 @@
Subproject commit 081249cceb59adc857a72d67e60c32047680f787 Subproject commit fc4a77ea28eb5eba0ecd11443f291335ec3d2aa0

@ -46,6 +46,10 @@
#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__
#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #define GOOGLE_PROTOBUF_REPEATED_FIELD_H__
#ifdef __GNUC__
#pragma GCC system_header
#endif
#include <string> #include <string>
#include <iterator> #include <iterator>
#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/common.h>

@ -377,6 +377,23 @@ Other (non-DFHack-specific) variables that affect DFHack:
sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this
should be the case in most UTF-8-capable \*nix terminal emulators already. 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 Miscellaneous notes
=================== ===================
This section is for odd but important notes that don't fit anywhere else. This section is for odd but important notes that don't fit anywhere else.

@ -48,10 +48,6 @@ DF version - see `above <installing-df-version>` for details. For example:
* ``dfhack-50.07-r1-Windows-64bit.zip`` supports 64-bit DF on Windows * ``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:: .. warning::
Do *not* download the source code from GitHub, either from the releases page 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 you want to compile DFHack instead of using a pre-built release, see
`building-dfhack-index` for instructions.) `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 Installing DFHack
================= =================

@ -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 - Quickly scan the map for visible ores of specific types so you can focus
your mining efforts your mining efforts
Some tools are one-shot commands. For example, you can run `unforbid all <unforbid>` Some tools are one-shot commands. For example, you can run
to claim all (reachable) items on the map after a messy siege. `unforbid all <unforbid>` to claim all (reachable) items on the map after a
messy siege.
Other tools must be `enabled <enable>` and then they will run in the background.
For example, `enable seedwatch <seedwatch>` will start monitoring your stocks of Other tools must be `enabled <enable>` once and then they will run in the
seeds and prevent your chefs from cooking seeds that you need for planting. background. For example, once enabled, `seedwatch` will start monitoring your
Tools that are enabled in the context of a fort will save their state with that stocks of seeds and prevent your chefs from cooking seeds that you need for
fort, and they will remember that they are enabled the next time you load your save. 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
A third class of tools add information to the screen or provide new integrated load your save.
functionality via the DFHack `overlay` framework. For example, the `unsuspend`
tool, in addition to its basic function of unsuspending all building construction A third class of tools adds information to the screen or provides new integrated
jobs, can also overlay a marker on suspended buildings to indicate that they are functionality via the DFHack `overlay` framework. For example, the `sort` tool
suspended (and will use different markers to tell you whether this is a problem). 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? 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 The first place to check is the DFHack logo menu. It's in the upper left corner
left corner of the screen by default, though you can move it anywhere you want of the screen by default, though you can move it anywhere you want with the
with the `gui/overlay` configuration UI. `gui/overlay` configuration UI.
When you hover the mouse over the logo (or hit the Ctrl-Shift-C keyboard shortcut) When you click on the logo (or hit the Ctrl-Shift-C keyboard shortcut), a short
a list of DFHack tools relevant to the current context comes up. For example, when list of popular, relevant DFHack tools comes up. These are the tools that have
you have a unit selected, the hotspot will show a list of tools that inspect been assigned hotkeys that are active in the current context. For example, when
units, allow you to edit them, or maybe even teleport them. Next to each tool, you're looking at a fort map, the list will contain fortress design tools like
you'll see the hotkey you can hit to invoke the command without even opening the `gui/quickfort` and `gui/design`. You can click on the tools in the list, or
hover list. 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 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 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 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 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 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 automatically enabled and popular commands you'd like to run when you start a
you can change, like whether DFHack windows will pause the game when they come new fort. On the "Preferences" tab, there are settings you can change, like
up. whether you want to limit DFHack functionality to interface improvements,
bugfixes, and productivity tools, hiding the god-mode tools ("mortal mode") or
Finally, you can explore the full extent of the DFHack catalog in `gui/launcher`, whether you want DFHack windows to pause the game when they come up.
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 Finally, you can explore the full extent of the DFHack catalog in
launcher, you can quickly autocomplete any command name by selecting it in the `gui/launcher`, which is always listed first in the DFHack logo menu list. You
list on the right side of the window. Commands are ordered by how often you run can also bring up the launcher by tapping the backtick key (\`) or hitting
them, so your favorite commands will always be on top. You can also pull full Ctrl-Shift-D. In the launcher, you can quickly autocomplete any command name by
commandlines out of your history with Alt-S or by clicking on the "history search" selecting it in the list on the right side of the window. Commands are ordered
hotkey hint. 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
Once you have typed (or autocompleted, or searched for) a command, other commands on the "history search" hotkey hint.
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 Once you have typed (or autocompleted, or searched for) a command, other
useful. You can also see how commands are grouped by running the `tags` command. 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, 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 allowing you to refer to the usage documentation and examples when you are
your command. After you run a command, the bottom panel switches to command output typing your command. After you run a command, the bottom panel switches to
mode, but you can get back to the help text by hitting Ctrl-T or clicking on the command output mode, but you can get back to the help text by hitting Ctrl-T or
``Help`` tab. clicking on the ``Help`` tab.
How do DFHack in-game windows work? 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 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 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 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 focus. If no DFHack windows are focused, you can right click directly on a
to close it without left clicking to focus it first. 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 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 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). (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 You can generally use DFHack tools without interrupting the game. That is, if
game is unpaused, it can continue to run while a DFHack window is open. If configured the game is unpaused, it can continue to run while a DFHack window is open. If
to do so in `gui/control-panel`, tools will initially pause the game to let you configured to do so in `gui/control-panel`, tools will initially pause the game
focus on the task at hand, but you can unpause like normal if you want. You can to let you focus on the task at hand, but you can unpause like normal if you
also interact with the map, scrolling it with the keyboard or mouse and selecting want. You can also interact with the map, scrolling it with the keyboard or
units, buildings, and items. Some tools will intercept all mouse clicks to allow mouse and selecting units, buildings, and items. Some tools will intercept all
you to select regions on the map. When these tools have focus, you will not be able mouse clicks to allow you to select regions of the map. When these tools have
to use the mouse to interact with map elements or pause/unpause the game. Therefore, focus, you will not be able to use the mouse to interact with map elements or
these tools will pause the game when they open, regardless of your settings in pause/unpause the game. Therefore, these tools will pause the game when they
`gui/control-panel`. You can still unpause with the keyboard (spacebar by default), open, regardless of your settings in `gui/control-panel`. You can still unpause
though. with the keyboard (spacebar by default), though.
Where do I go next? Where do I go next?
------------------- -------------------
To recap: To recap:
You can get to popular, relevant tools for the current context by hovering You can get to popular, relevant tools for the current context by clicking on
the mouse over the DFHack logo or by hitting Ctrl-Shift-C. the DFHack logo or by hitting Ctrl-Shift-C.
You can enable DFHack tools and configure settings with `gui/control-panel`, 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, 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 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 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. fingertips. So what to run first? Here are a few examples to get you started.
You can run them all from the launcher.
First, let's import some useful manager orders to keep your fort stocked with 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 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. manager orders screen, you can see all the orders that have been created for
Note that you could have imported the orders directly from this screen as well, you. Note that you could have imported the orders directly from this screen as
using the DFHack `overlay` widget at the bottom of the manager orders panel. 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 Next, try setting up `autochop` to automatically designate trees for chopping
``Fort`` list. Click on the button to the left of the name or hit Enter to enable when you get low on usable logs. Run `gui/control-panel` and select
it. You can then click on the configure button (the gear icon) to launch ``autochop`` in the ``Fort`` list. Click on the button to the left of the name
`gui/autochop` if you'd like to customize its settings. If you have the extra or hit Enter to enable it. You can then click on the configure button (the gear
screen space, you can go ahead and set the `gui/autochop` window to minimal mode icon) to launch `gui/autochop` if you'd like to customize its settings. If you
(click on the hint near the upper right corner of the window or hit Alt-M) and have the extra screen space, you can go ahead and set the `gui/autochop` window
click on the map so the window loses keyboard focus. As you play the game, you can to minimal mode (click on the hint near the upper right corner of the window or
glance at the live status panel to check on your stocks of wood. 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
Finally, let's do some fort design copy-pasting. Go to some bedrooms that you have wood.
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) Finally, let's do some fort design copy-pasting. Go to some bedrooms that you
and hitting Enter to set. Then draw a box around the target area by clicking with have set up in your fort. Run `gui/blueprint`, set a name for your blueprint by
the mouse. When you select the second corner, the blueprint will be saved to your clicking on the name field (or hitting the 'n' hotkey), typing "rooms" (or
``blueprints`` subfolder. 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
Now open up `gui/quickfort`. You can search for the blueprint you just created by be saved to your ``dfhack-config/blueprints`` subfolder.
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". Now open up `gui/quickfort`. You can search for the blueprint you just created
Click on the "/dig" blueprint or select it with the keyboard arrow keys and hit Enter. by typing its name, but it should be up near the top already. If you copied a
You can rotate or flip the blueprint around if you need to with the transform hotkeys. dug-out area with furniture in it, your blueprint will have two labels: "/dig"
You'll see a preview of where the blueprint will be applied as you move the mouse and "/build". Click on the "/dig" blueprint or select it with the keyboard
cursor around the map. Red outlines mean that the blueprint may fail to fully apply arrow keys and hit Enter. You can rotate or flip the blueprint around if you
at that location, so be sure to choose a spot where all the preview tiles are shown need to with the transform hotkeys. You'll see a preview of where the blueprint
with green diamonds. Click the mouse or hit Enter to apply the blueprint and will be applied as you move the mouse cursor around the map. Red outlines mean
designate the tiles for digging. Your dwarves will come and dig it out as if you that the blueprint may fail to fully apply at that location, so be sure to
had designated the tiles yourself. 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
Once the area is dug out, run `gui/quickfort` again and select the "/build" blueprint digging. Your dwarves will come and dig it out as if you had designated the
this time. Apply the blueprint in the dug-out area, and your furniture will be tiles yourself.
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 Once the area is dug out, run `gui/quickfort` again and select your "/build"
materials in stock. The planned furniture/buildings will get built whenever you are blueprint this time. Hit ``o`` to generate manager orders for the required
able to produce the building materials. 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! There are many, many more tools to explore. Have fun!

@ -3,8 +3,8 @@
DFHack tools DFHack tools
============ ============
DFHack has **a lot** of tools. This page attempts to make it clearer what they DFHack comes with **a lot** of tools. This page attempts to make it clearer
are, how they work, and how to find the ones you want. what they are, how they work, and how to find the ones you want.
.. contents:: Contents .. contents:: Contents
:local: :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 `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 <all-tag-index>`. one flat list, please refer to the `annotated index <all-tag-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 <unavailable-tag-index>`. 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 DFHack tools by game mode
------------------------- -------------------------

@ -3,7 +3,7 @@ List of authors
The following is a list of people who have contributed to DFHack, in The following is a list of people who have contributed to DFHack, in
alphabetical order. 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! or make a pull request!
======================= ======================= =========================== ======================= ======================= ===========================
@ -83,6 +83,7 @@ Herwig Hochleitner bendlas
Hevlikn Hevlikn Hevlikn Hevlikn
Ian S kremlin- Ian S kremlin-
IndigoFenix IndigoFenix
Jacek Konieczny Jajcus
James 20k James 20k
James Gilles kazimuth James Gilles kazimuth
James Logsdon jlogsdon James Logsdon jlogsdon
@ -129,6 +130,7 @@ Michael Crouch creidieki
Michon van Dooren MaienM Michon van Dooren MaienM
miffedmap miffedmap miffedmap miffedmap
Mike Stewart thewonderidiot Mike Stewart thewonderidiot
Mikhail Panov Halifay
Mikko Juola Noeda Adeon Mikko Juola Noeda Adeon
Milo Christiansen milochristiansen Milo Christiansen milochristiansen
MithrilTuxedo MithrilTuxedo MithrilTuxedo MithrilTuxedo
@ -137,6 +139,7 @@ moversti moversti
mrrho mrrho mrrho mrrho
Murad Beybalaev Erquint Murad Beybalaev Erquint
Myk Taylor myk002 Myk Taylor myk002
Najeeb Al-Shabibi master-spike
napagokc napagokc napagokc napagokc
Neil Little nmlittle Neil Little nmlittle
Nick Rart nickrart comestible Nick Rart nickrart comestible
@ -203,6 +206,7 @@ Sebastian Wolfertz Enkrod
SeerSkye SeerSkye SeerSkye SeerSkye
seishuuu seishuuu seishuuu seishuuu
Seth Woodworth sethwoodworth Seth Woodworth sethwoodworth
shevernitskiy shevernitskiy
Shim Panze Shim-Panze Shim Panze Shim-Panze
Silver silverflyone Silver silverflyone
simon simon

@ -10,6 +10,13 @@ work (e.g. links from the `changelog`).
:local: :local:
:depth: 1 :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:
autohauler autohauler
@ -184,6 +191,12 @@ gui/hack-wish
============= =============
Replaced by `gui/create-item`. 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:
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 ``dfhack.init`` is autogenerated in ``dfhack-config/init``, this warning is no
longer necessary. longer necessary.
.. _gui/stockpiles:
gui/stockpiles
==============
Provided import/export dialogs. Converted to an `overlay` that displays when
a stockpile is selected.
.. _masspit: .. _masspit:
masspit masspit
@ -220,6 +226,12 @@ ruby
Support for the Ruby language in DFHack scripts was removed due to the issues Support for the Ruby language in DFHack scripts was removed due to the issues
the Ruby library causes when used as an embedded language. the Ruby library causes when used as an embedded language.
.. _search-plugin:
search
======
Functionality was merged into `sort`.
.. _show-unit-syndromes: .. _show-unit-syndromes:
show-unit-syndromes show-unit-syndromes

@ -33,6 +33,11 @@ The ``<key>`` parameter above has the following **case-sensitive** syntax::
where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote
optional parts. 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 When multiple commands are bound to the same key combination, DFHack selects
the first applicable one. Later ``add`` commands, and earlier entries within one the first applicable one. Later ``add`` commands, and earlier entries within one
``add`` command have priority. Commands that are not specifically intended for ``add`` command have priority. Commands that are not specifically intended for

@ -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. 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. - ``!`` 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 ===end
]]] ]]]
@ -33,22 +51,234 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future # 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 ## 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 ## 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 ## 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 ## Documentation
- `misery`: rewrite the documentation to clarify the actual effects of the plugin
## API ## API
- ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role
- ``Items::markForTrade()``, ``Items::isRequestedTradeGood()``, ``Items::getValue``: see Lua notes below
## Internals ## Internals
- Price calculations fixed for many item types
## Lua ## 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 # 50.08-r4

@ -306,7 +306,32 @@ All types and the global object have the following features:
* ``type._identity`` * ``type._identity``
Contains a lightuserdata pointing to the underlying 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: 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. 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 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``. 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. otherwise the existing one is simply updated.
Returns *entry, did_create_new* Returns *entry, did_create_new*
Since the data is hidden in data structures owned by the DF world, The data is kept in memory, so no I/O occurs when getting or saving keys. It is
and automatically stored in the save game, these save and retrieval all written to a json file in the game save directory when the game is saved.
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.
It is also possible to associate one bit per map tile with an entry, It is also possible to associate one bit per map tile with an entry,
using these two methods: using these two methods:
@ -985,41 +1008,26 @@ General-purpose selections
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
* ``dfhack.gui.getSelectedWorkshopJob([silent])`` * ``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])`` * ``dfhack.gui.getSelectedJob([silent])``
Returns the job selected in a workshop or unit/jobs screen.
* ``dfhack.gui.getSelectedUnit([silent])`` * ``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])`` * ``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])`` * ``dfhack.gui.getSelectedBuilding([silent])``
Returns the building selected via :kbd:`q`, :kbd:`t`, :kbd:`k` or :kbd:`i`.
* ``dfhack.gui.getSelectedCivZone([silent])`` * ``dfhack.gui.getSelectedCivZone([silent])``
* ``dfhack.gui.getSelectedStockpile([silent])``
Returns the zone currently selected via :kbd:`z`
* ``dfhack.gui.getSelectedPlant([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.getAnyUnit(screen)``
* ``dfhack.gui.getAnyItem(screen)`` * ``dfhack.gui.getAnyItem(screen)``
* ``dfhack.gui.getAnyBuilding(screen)`` * ``dfhack.gui.getAnyBuilding(screen)``
* ``dfhack.gui.getAnyCivZone(screen)``
* ``dfhack.gui.getAnyStockpile(screen)``
* ``dfhack.gui.getAnyPlant(screen)`` * ``dfhack.gui.getAnyPlant(screen)``
Similar to the corresponding ``getSelected`` functions, but operate on the 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 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.) ``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(pos[,center[,highlight]])``
``dfhack.gui.revealInDwarfmodeMap(x,y,z[,center])`` ``dfhack.gui.revealInDwarfmodeMap(x,y,z[,center[,highlight]])``
Centers the view on the given coordinates. If ``center`` is true, make sure the Centers the view on the given coordinates. If ``center`` is true, make sure
position is in the exact center of the view, else just bring it on screen. 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`), ``pos`` can be a ``df.coord`` instance or a table assignable to a ``df.coord`` (see `lua-api-table-assignment`),
e.g.:: e.g.::
@ -1130,10 +1140,12 @@ Announcements
If you want a guaranteed announcement without parsing, use ``dfhack.gui.showAutoAnnouncement`` instead. 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 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 Other
~~~~~ ~~~~~
@ -1355,11 +1367,16 @@ Units module
* ``dfhack.units.isTame(unit)`` * ``dfhack.units.isTame(unit)``
* ``dfhack.units.isTamable(unit)`` * ``dfhack.units.isTamable(unit)``
* ``dfhack.units.isDomesticated(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.isMarkedForSlaughter(unit)``
* ``dfhack.units.isMarkedForGelding(unit)`` * ``dfhack.units.isMarkedForGelding(unit)``
* ``dfhack.units.isGeldable(unit)`` * ``dfhack.units.isGeldable(unit)``
* ``dfhack.units.isGelded(unit)`` * ``dfhack.units.isGelded(unit)``
* ``dfhack.units.isEggLayer(unit)`` * ``dfhack.units.isEggLayer(unit)``
* ``dfhack.units.isEggLayerRace(unit)``
* ``dfhack.units.isGrazer(unit)`` * ``dfhack.units.isGrazer(unit)``
* ``dfhack.units.isMilkable(unit)`` * ``dfhack.units.isMilkable(unit)``
@ -1438,10 +1455,22 @@ Units module
Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to
the arguments required by this function. 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])`` * ``dfhack.units.getCitizens([ignore_sanity])``
Returns a table (list) of all citizens, which you would otherwise have to loop over all Returns a list of all living citizens.
units in world and test against ``isCitizen()`` to discover.
* ``dfhack.units.teleport(unit, pos)`` * ``dfhack.units.teleport(unit, pos)``
@ -1487,6 +1516,11 @@ Units module
Computes the effective attribute value, including curse effect. 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])`` * ``dfhack.units.getMiscTrait(unit, type[, create])``
Finds (or creates if requested) a misc trait object with the given id. 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. Currently only one dream per unit is supported by Dwarf Fortress.
Support for multiple dreams may be added in future versions of 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)`` * ``dfhack.units.getStressCategory(unit)``
Returns a number from 0-6 indicating stress. 0 is most stressed; 6 is least. 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. 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)`` * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)``
@ -1773,7 +1821,16 @@ Items module
* ``dfhack.items.canTradeWithContents(item)`` * ``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)`` * ``dfhack.items.isRouteVehicle(item)``
@ -1857,10 +1914,11 @@ Maps module
Returns the plant struct that owns the tile at the specified position. 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, Returns the walkability group for the given tile position. A return value of
using a pathfinding cache maintained by the game. ``0`` indicates that the tile is not walkable. The data comes from a
pathfinding cache maintained by DF.
.. note:: .. note::
This cache is only updated when the game is unpaused, and thus 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 take into account anything that depends on the actual units, like
burrows, or the presence of invaders. 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)`` * ``dfhack.maps.hasTileAssignment(tilemask)``
Checks if the tile_bitmask object is not *nil* and contains any set bits; returns *true* or *false*. 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 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)`` * ``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. Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience.
``_MOUSE_L, _MOUSE_R, _MOUSE_M`` ``_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`` ``_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. 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. ``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 <lua-screen-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 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. The oldval, newval or delta arguments may be used to specify additional constraints.
Returns: *found_index*, or *nil* if end reached. 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)`` * ``dfhack.internal.getDir(path)``
Lists files/directories in a directory. 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. Returns 0 if the address is not found.
Requires a heap snapshot. 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: .. _lua-core-context:
@ -2950,6 +3093,9 @@ environment by the mandatory init file dfhack.lua:
COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED, COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED,
COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE 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`` * State change event codes, used by ``dfhack.onStateChange``
Available only in the `core context <lua-core-context>`, as is the event itself: Available only in the `core context <lua-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]``. 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,...)`` * ``utils.call_with_string(obj,methodname,...)``
Allocates a temporary string object, calls ``obj:method(tmp,...)``, and 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 ``tonumber(arg)``. If ``arg_name`` is specified, it is used to make error
messages more useful. 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 dumper
====== ======
@ -3812,6 +3979,14 @@ Misc
of keycodes to *true* or *false*. For instance, it is possible to use the of keycodes to *true* or *false*. For instance, it is possible to use the
table passed as argument to ``onInput``. 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)`` * ``mkdims_xy(x1,y1,x2,y2)``
Returns a table containing the arguments as fields, and also ``width`` and 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. A frame suitable for overlay widget panels.
* ``FRAME_THIN``
A frame suitable for floating tooltip panels that need the DFHack signature.
* ``FRAME_BOLD`` * ``FRAME_BOLD``
A frame suitable for a non-draggable panel meant to capture the user's focus, 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_anchors = {}`` (default: ``{title=true, frame=false/true, body=true}``)
* ``drag_bound = 'frame' or 'body'`` (default: ``'frame'``) * ``drag_bound = 'frame' or 'body'`` (default: ``'frame'``)
* ``on_drag_begin = function()`` (default: ``nil``) * ``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 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 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 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 mouse, hitting :kbd:`Esc` (while dragging with the mouse or keyboard), or by
calling ``Panel:setKeyboaredDragEnabled(false)`` (while dragging with the 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``) * ``resizable = bool`` (default: ``false``)
* ``resize_anchors = {}`` (default: ``{t=false, l=true, r=true, b=true}`` * ``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}``) * ``resize_min = {}`` (default: w and h from the ``frame``, or ``{w=5, h=5}``)
* ``on_resize_begin = function()`` (default: ``nil``) * ``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 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 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, 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 hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling
``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard). ``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_subviews = bool`` (default: ``false``)
* ``autoarrange_gap = int`` (default: ``0``) * ``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, 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. 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: Double clicking:
If the panel is resizable and the user double-clicks on the top edge (the frame 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-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. - 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: The ``EditField`` class also provides the following functions:
* ``editfield:setCursor([cursor_pos])`` * ``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 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. string, then this function will return ``nil`` for that option.
ToggleHotkeyLabel ToggleHotkeyLabel class
----------------- -----------------------
This is a specialized subclass of CycleHotkeyLabel that has two options: 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`` (with a value of ``true``) and ``Off`` (with a value of ``false``). The
``On`` option is rendered in green. ``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 List class
---------- ----------
@ -5109,12 +5345,11 @@ FilteredList class
------------------ ------------------
This widget combines List, EditField and Label into a combo-box like 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 In addition to passing through all attributes supported by List, it
supports: 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_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_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. :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 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 (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 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_left_change: Callback executed when moving the left handle.
:on_right_change: Callback executed when moving the right 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: .. _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 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. * ``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-api:
cxxrandom cxxrandom

@ -88,9 +88,9 @@ assistance.
All Platforms All Platforms
============= =============
Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, 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 for you, but it's more common to do it from the command line. Windows developers can refer to the
Windows batch files that can be used to avoid opening a terminal/command-prompt. 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 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. the `build-options` page for help finding the DFHack build options relevant to you.

@ -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 This will be filled in with the display name of your widget, in case you
have multiple widgets with the same implementation but different have multiple widgets with the same implementation but different
configurations. 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}``) - ``default_pos`` (default: ``{x=-2, y=-2}``)
Override this attribute with your desired default widget position. See Override this attribute with your desired default widget position. See
the `overlay` docs for information on what positive and negative numbers 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 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 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, 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 Registering a widget with the overlay framework
*********************************************** ***********************************************

@ -45,6 +45,7 @@ this::
info.txt info.txt
graphics/... graphics/...
objects/... objects/...
blueprints/...
scripts_modactive/example-mod.lua scripts_modactive/example-mod.lua
scripts_modactive/internal/example-mod/... scripts_modactive/internal/example-mod/...
scripts_modinstalled/... 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 - 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 :file:`graphics/` and :file:`objects/` folders. You can read more about
the files that go in these directories on the :wiki:`Modding` wiki page. 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 - A control script in :file:`scripts_modactive/` directory that handles
system-level event hooks (e.g. reloading state when a world is loaded), system-level event hooks (e.g. reloading state when a world is loaded),
registering `overlays <overlay-dev-guide>`, and registering `overlays <overlay-dev-guide>`, and

@ -84,7 +84,7 @@ Feature summary
- Configurable zone/location settings, such as the pit/pond toggle or - Configurable zone/location settings, such as the pit/pond toggle or
hospital supply quantities hospital supply quantities
- Build mode - Build mode
- Integrated with DFHack `buildingplan`: you can place buildings before - Integrated with DFHack `buildingplan`: you can place buildings before
manufacturing building materials and you can use the `buildingplan` UI manufacturing building materials and you can use the `buildingplan` UI
@ -108,6 +108,10 @@ Feature summary
- Set building properties (such as a name) - Set building properties (such as a name)
- Can attach and configure track stops as part of hauling routes - Can attach and configure track stops as part of hauling routes
- Burrow mode
- Supports creating, adding to, and subtracting from burrows.
Introduction to blueprints 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 These two track stops (which do not dump their contents) simply exist on a
common route at the ends of a connected carved track. 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: .. _quickfort-modeline:
Modeline markers Modeline markers
@ -1992,10 +2026,10 @@ All stockpiles support the following properties:
Property Description Property Description
================ =========== ================ ===========
``name`` the name of the stockpile ``name`` the name of the stockpile
``take_from`` comma-separated list of names of stockpiles or workshops that ``take_from`` comma-separated list of names or building ids of stockpiles
the stockpile takes from or workshops that the stockpile takes from
``give_to`` comma-separated list of names of stockpiles or workshops that ``give_to`` comma-separated list of names or building ids of stockpiles
the stockpile gives to or workshops that the stockpile gives to
``links_only`` if set to ``true`` then the stockpile will only take from ``links_only`` if set to ``true`` then the stockpile will only take from
links links
``barrels`` the number of desired barrels ``barrels`` the number of desired barrels
@ -2014,6 +2048,12 @@ Property Description
feature 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 #build mode reference
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -2023,8 +2063,8 @@ accept the ``name`` property.
Moreover, all workshops and furnaces accept the ``max_general_orders`` Moreover, all workshops and furnaces accept the ``max_general_orders``
property, which sets the maximum number of general workorders that the building property, which sets the maximum number of general workorders that the building
can accept, and the ``take_from`` and ``give_to`` properties, which are can accept, and the ``take_from`` and ``give_to`` properties, which are
comma-separated lists of names (the same as the correponding stockpile comma-separated lists of names or building ids (the same as the correponding
properties above). stockpile properties above).
================= ============================= ========== ================= ============================= ==========
Symbol Type Properties Symbol Type Properties
@ -2141,7 +2181,8 @@ Symbol Type Properties
route stop on this track stop route stop on this track stop
and make it take from the given and make it take from the given
comma-separated list of comma-separated list of
stockpile names. ``route``: add stockpile names or stockpile
building ids. ``route``: add
this route stop to the named this route stop to the named
route. if no route of this name route. if no route of this name
exists, it will be created. If exists, it will be created. If
@ -2192,3 +2233,25 @@ Symbol Type Properties
``trackrampSEW`` track ramp tee to the S, E, W ``trackrampSEW`` track ramp tee to the S, E, W
``trackrampNSEW`` track ramp cross ``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.
====== ======= ==========

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

@ -3,7 +3,7 @@
.. dfhack-tool:: .. dfhack-tool::
:summary: Rewrite layer veins to expand in 3D space. :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 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 are generated in their place. The transformation preserves the mineral counts

@ -70,7 +70,7 @@ Usage
``autobutcher list_export`` ``autobutcher list_export``
Print commands required to set the current settings in another fort. 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 devel/query --table df.global.world.raws.creatures.all --search ^creature_id --maxdepth 1

@ -3,7 +3,7 @@ autogems
.. dfhack-tool:: .. dfhack-tool::
:summary: Automatically cut rough gems. :summary: Automatically cut rough gems.
:tags: unavailable fort auto workorders :tags: unavailable
:no-command: :no-command:
.. dfhack-command:: autogems-reload .. dfhack-command:: autogems-reload

@ -3,7 +3,7 @@ building-hacks
.. dfhack-tool:: .. dfhack-tool::
:summary: Provides a Lua API for creating powered workshops. :summary: Provides a Lua API for creating powered workshops.
:tags: unavailable fort gameplay buildings :tags: unavailable
:no-command: :no-command:
See `building-hacks-api` for more details. See `building-hacks-api` for more details.

@ -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 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` and a dwarf will come and build the building. If you have the `unsuspend`
overlay enabled (it is enabled by default), then buildingplan-suspended 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 buildings will be tagged with a clock graphic in graphics mode or a ``P``
usual ``x`` marker for "regular" suspended buildings. If you have marker in ASCII mode, as opposed to the ``x`` marker for "regular" suspended
`suspendmanager` running, then buildings will be left suspended when their buildings. If you have `suspendmanager` running, then buildings will be left
items are all attached and ``suspendmanager`` will unsuspend them for suspended when their items are all attached and ``suspendmanager`` will
construction when it is safe to do so. 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, 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 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 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 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 can then place as many of each building as you like. Items will be used to
to build the planned buildings as they are produced, with minimal space build the planned buildings as they are produced, with minimal space dedicated
dedicated to stockpiles. The DFHack `orders` library can help with setting to stockpiles. The DFHack `orders` library can help with setting these manager
these manager workorders up for you. workorders up for you.
If you don't want to use the ``buildingplan`` interface for the building you're 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 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 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. 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 Some building types will have other options available as well, such as a
on this panel. 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 Setting quality and material filters
++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++
If you want to set restrictions on the items chosen to complete the planned 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 building, you can click on the "[any material]" link next to the item name or
the item with the :kbd:`*` and :kbd:`/` keys and hit :kbd:`f` to bring up the select the item with the :kbd:`q` or :kbd:`Q` keys and hit :kbd:`f` to bring up
filter dialog. the filter dialog.
You can select whether the item must be decorated, and you can drag the ends of 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, 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 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 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. that it is perfectly fine to choose materials that currently show zero quantity.
`buildingplan` will patiently watch for items made of materials you have `buildingplan` will patiently wait for items made of materials you have
selected. selected to become available.
Choosing specific items Choosing specific items
+++++++++++++++++++++++ +++++++++++++++++++++++
If you want to choose specific items, click on the "Choose from items" toggle If you want to choose specific items instead of using the filters, click on the
or hit :kbd:`i` before placing the building. When you click to place the "Choose items" selector or hit :kbd:`z` before placing the building. You can
building, a dialog will come up that allows you choose which items to use. The choose to be prompted for every item ("Manually") or you can have it
list is sorted by most recently used materials for that building type by automatically select the type of item that you last chose for this building
default, but you can change to sort by name or by available quantity by type. The list you are prompted with is sorted by most recently used materials
clicking on the "Sort by" selector or hitting :kbd:`R`. The configuration for for that building type by default, but you can change to sort by name or by
whether you would like to choose specific items is saved per building type and available quantity by clicking on the "Sort by" selector or hitting :kbd:`R`.
will be restored when you plan more of that building type. 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 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 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`) 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. 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 :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 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 other available items (or from items produced in the future if not all items
there are multiple item types to choose for the current building, one dialog are available yet). If there are multiple item types to choose for the current
will appear per item type. building, one dialog will appear per item type.
Building status Building status
--------------- ---------------
@ -180,10 +182,18 @@ Building status
When viewing a planned building, a separate `overlay` widget appears on the 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 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 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 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. 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 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 an item is in queue position 1, there may be other queues that snag the needed
item first. 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.

@ -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 <target burrow> [<target burrow> ...] [<options>]
burrow tiles|units set|add|remove <target burrow> <burrow> [...] [<options>]
burrow tiles box-add|box-remove <target burrow> [<pos>] [<pos>] [<options>]
burrow tiles flood-add|flood-remove <target burrow> [<options>]
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 <pos>``
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.

@ -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 <burrow> [<burrow> ...]``
Remove all units from the named burrows.
``burrow clear-tiles <burrow> [<burrow> ...]``
Remove all tiles from the named burrows.
``burrow set-units target-burrow <burrow> [<burrow> ...]``
Clear all units from the target burrow, then add units from the named source
burrows.
``burrow add-units target-burrow <burrow> [<burrow> ...]``
Add units from the source burrows to the target.
``burrow remove-units target-burrow <burrow> [<burrow> ...]``
Remove units in source burrows from the target.
``burrow set-tiles target-burrow <burrow> [<burrow> ...]``
Clear target burrow tiles and add tiles from the names source burrows.
``burrow add-tiles target-burrow <burrow> [<burrow> ...]``
Add tiles from the source burrows to the target.
``burrow remove-tiles target-burrow <burrow> [<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.

@ -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.

@ -50,7 +50,7 @@ Usage
Designate circles. The diameter is the number of tiles across the center of 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 the circle that you want to dig. See the `digcircle`_ section below for
options. options.
``digtype [<designation>] [-p<number>] [-z]`` ``digtype [<designation>] [-p<number>] [--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 Designate all vein tiles of the same type as the selected tile. See the
`digtype`_ section below for options. `digtype`_ section below for options.
``digexp [<pattern>] [<filter>] [-p<number>]`` ``digexp [<pattern>] [<filter>] [-p<number>]``
@ -119,9 +119,11 @@ the last selected parameters.
digtype digtype
------- -------
For every tile on the map of the same vein type as the selected tile, this For every tile on the map of the same vein type as the selected tile, this command
command designates it to have the same designation as the selected tile. If the designates it to have the same designation as the selected tile. If the selected
selected tile has no designation, they will be dig designated. 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 If an argument is given, the designation of the selected tile is ignored, and
all appropriate tiles are set to the specified designation. all appropriate tiles are set to the specified designation.
@ -143,9 +145,18 @@ Designation options:
``clear`` ``clear``
Clear any designations. Clear any designations.
You can also pass a ``-z`` option, which restricts designations to the current Other options:
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. ``-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 digexp
------ ------
@ -179,3 +190,23 @@ Filters:
Take current designation and apply the selected pattern to it. Take current designation and apply the selected pattern to it.
After you have a pattern set, you can use ``expdig`` to apply it again. 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.

@ -3,7 +3,7 @@ digFlood
.. dfhack-tool:: .. dfhack-tool::
:summary: Digs out veins as they are discovered. :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 Once you register specific vein types, this tool will automatically designate
tiles of those types of veins for digging as your miners complete adjacent tiles of those types of veins for digging as your miners complete adjacent

@ -3,7 +3,7 @@ diggingInvaders
.. dfhack-tool:: .. dfhack-tool::
:summary: Invaders dig and destroy to get to your dwarves. :summary: Invaders dig and destroy to get to your dwarves.
:tags: unavailable fort gameplay military units :tags: unavailable
Usage Usage
----- -----

@ -3,7 +3,7 @@ dwarfmonitor
.. dfhack-tool:: .. dfhack-tool::
:summary: Report on dwarf preferences and efficiency. :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. It can also show heads-up display widgets with live fort statistics.

@ -2,22 +2,34 @@ dwarfvet
======== ========
.. dfhack-tool:: .. dfhack-tool::
:summary: Allows animals to be treated at animal hospitals. :summary: Allow animals to be treated at hospitals.
:tags: unavailable fort gameplay animals :tags: fort gameplay animals
Annoyed that your dragons become useless after a minor injury? Well, with 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 dwarfvet, injured animals will be treated at a hospital. Dwarfs with the Animal
a hospital that is also an animal training zone. Dwarfs with the Animal Caretaker labor enabled will come to the hospital to treat the animals. Normal
Caretaker labor enabled will come to the hospital to treat animals. Normal
medical skills are used (and trained), but no experience is given to the Animal medical skills are used (and trained), but no experience is given to the Animal
Caretaker skill itself. 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 Usage
----- -----
``enable dwarfvet`` ::
Enables the plugin.
``dwarfvet report`` enable dwarfvet
Reports all zones that the game considers animal hospitals. dwarfvet [status]
``dwarfvet report-usage`` dwarfvet now
Reports on animals currently being treated.
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.

@ -3,7 +3,7 @@ embark-assistant
.. dfhack-tool:: .. dfhack-tool::
:summary: Embark site selection support. :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 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 reasonably correct) resource information for the embark rectangle as well as

@ -3,7 +3,7 @@ embark-tools
.. dfhack-tool:: .. dfhack-tool::
:summary: Extend the embark screen functionality. :summary: Extend the embark screen functionality.
:tags: unavailable
Usage Usage
----- -----

@ -3,7 +3,7 @@ fix-unit-occupancy
.. dfhack-tool:: .. dfhack-tool::
:summary: Fix phantom unit occupancy issues. :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 If you see "unit blocking tile" messages that you can't account for
(:bug:`3499`), this tool can help. (:bug:`3499`), this tool can help.

@ -3,7 +3,7 @@ fixveins
.. dfhack-tool:: .. dfhack-tool::
:summary: Restore missing mineral inclusions. :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 This tool can also remove invalid references to mineral inclusions if you broke
your embark with tools like `tiletypes`. your embark with tools like `tiletypes`.

@ -3,7 +3,7 @@ follow
.. dfhack-tool:: .. dfhack-tool::
:summary: Make the screen follow the selected unit. :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 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 centered on the unit. Handy for watching dwarves running around. Deactivated by

@ -3,7 +3,7 @@ forceequip
.. dfhack-tool:: .. dfhack-tool::
:summary: Move items into a unit's inventory. :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, 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 but can also be used to put armor onto a war animal or to add unusual items

@ -3,7 +3,7 @@ generated-creature-renamer
.. dfhack-tool:: .. dfhack-tool::
:summary: Automatically renames generated creatures. :summary: Automatically renames generated creatures.
:tags: unavailable adventure fort legends units :tags: unavailable
:no-command: :no-command:
.. dfhack-command:: list-generated .. dfhack-command:: list-generated

@ -19,14 +19,17 @@ Usage
Menu overlay widget Menu overlay widget
------------------- -------------------
The in-game hotkeys menu is registered with the `overlay` framework and can be The in-game hotkeys menu is registered with the `overlay` framework and appears
enabled as a hotspot in the upper-left corner of the screen. You can bring up as a DFHack logo in the upper-left corner of the screen. You can bring up the
the menu by hovering the mouse cursor over the hotspot and can select a command 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
to run from the list by clicking on it with the mouse or by using the keyboard the list by clicking on it with the mouse or by using the keyboard to select a
to select a command with the arrow keys and hitting :kbd:`Enter`. 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 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 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 command, left clicking on the arrow to the left of the command, or by pressing
the right arrow key while the command is selected. the right arrow key while the command is selected.

@ -3,7 +3,7 @@ infiniteSky
.. dfhack-tool:: .. dfhack-tool::
:summary: Automatically allocate new z-levels of sky :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 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 top of the map as you build up. Or it can allocate one or many additional levels

@ -3,7 +3,7 @@ isoworldremote
.. dfhack-tool:: .. dfhack-tool::
:summary: Provides a remote API used by Isoworld. :summary: Provides a remote API used by Isoworld.
:tags: unavailable dev graphics :tags: unavailable
:no-command: :no-command:
See `remote` for related remote APIs. See `remote` for related remote APIs.

@ -5,7 +5,7 @@ jobutils
.. dfhack-tool:: .. dfhack-tool::
:summary: Provides commands for interacting with jobs. :summary: Provides commands for interacting with jobs.
:tags: unavailable fort inspection jobs :tags: unavailable
:no-command: :no-command:
.. dfhack-command:: job .. dfhack-command:: job

@ -3,7 +3,7 @@ labormanager
.. dfhack-tool:: .. dfhack-tool::
:summary: Automatically manage dwarf labors. :summary: Automatically manage dwarf labors.
:tags: unavailable fort auto labors :tags: unavailable
Labormanager is derived from `autolabor` but uses a completely different Labormanager is derived from `autolabor` but uses a completely different
approach to assigning jobs to dwarves. While autolabor tries to keep as many approach to assigning jobs to dwarves. While autolabor tries to keep as many

@ -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 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. 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 Usage
----- -----
@ -69,3 +72,7 @@ Options
Causes the command to act upon stockpiles with the given names or numbers 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 instead of the stockpile that is currently selected in the UI. Note that
the numbers are the stockpile numbers, not the building ids. 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.

@ -3,7 +3,7 @@ manipulator
.. dfhack-tool:: .. dfhack-tool::
:summary: An in-game labor management interface. :summary: An in-game labor management interface.
:tags: unavailable fort productivity labors :tags: unavailable
:no-command: :no-command:
It is equivalent to the popular Dwarf Therapist utility. It is equivalent to the popular Dwarf Therapist utility.

@ -3,7 +3,7 @@ map-render
.. dfhack-tool:: .. dfhack-tool::
:summary: Provides a Lua API for re-rendering portions of the map. :summary: Provides a Lua API for re-rendering portions of the map.
:tags: unavailable dev graphics :tags: unavailable
:no-command: :no-command:
See `map-render-api` for details. See `map-render-api` for details.

@ -2,11 +2,12 @@ misery
====== ======
.. dfhack-tool:: .. dfhack-tool::
:summary: Increase the intensity of your citizens' negative thoughts. :summary: Make citizens more miserable.
:tags: fort gameplay units :tags: fort gameplay units
When enabled, negative thoughts that your citizens have will multiply by the When enabled, all of your citizens receive a negative thought about a
specified factor. This makes it more challenging to keep them happy. 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 Usage
----- -----
@ -18,18 +19,18 @@ Usage
misery <factor> misery <factor>
misery clear misery clear
The default misery factor is ``2``, meaning that your dwarves will become The default misery factor is ``2``, which will result in a moderate hit to your
miserable twice as fast. dwarves' happiness. Larger numbers increase the challenge.
Examples Examples
-------- --------
``enable misery`` ``enable misery``
Start multiplying bad thoughts for your citizens! Start adding bad thoughts about nasty soapy baths to your citizens!
``misery 5`` ``misery 5``
Make dwarves become unhappy 5 times faster than normal -- this is quite Change the strength of the soapy bath negative thought to something quite
challenging to handle! large -- this is very challenging to handle!
``misery clear`` ``misery clear``
Clear away negative thoughts added by ``misery``. Note that this will not Clear away negative thoughts added by ``misery``. Note that this will not

@ -3,7 +3,7 @@ mode
.. dfhack-tool:: .. dfhack-tool::
:summary: See and change the game mode. :summary: See and change the game mode.
:tags: unavailable armok dev gameplay :tags: unavailable
.. warning:: .. warning::

@ -3,7 +3,7 @@ mousequery
.. dfhack-tool:: .. dfhack-tool::
:summary: Adds mouse controls to the DF interface. :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 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 can click on buildings to configure them, hold the mouse button to draw dig

@ -17,6 +17,14 @@ Usage
manager orders. It will not clear the orders that already exist. manager orders. It will not clear the orders that already exist.
``orders clear`` ``orders clear``
Deletes all manager orders in the current embark. 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`` ``orders sort``
Sorts current manager orders by repeat frequency so repeating orders don't Sorts current manager orders by repeat frequency so repeating orders don't
prevent one-time orders from ever being completed. The sorting order is: prevent one-time orders from ever being completed. The sorting order is:

@ -3,7 +3,7 @@ petcapRemover
.. dfhack-tool:: .. dfhack-tool::
:summary: Modify the pet population cap. :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 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 number of children of that species is below a certain percentage. This plugin

@ -5,7 +5,7 @@ plants
.. dfhack-tool:: .. dfhack-tool::
:summary: Provides commands that interact with plants. :summary: Provides commands that interact with plants.
:tags: unavailable adventure fort armok map plants :tags: unavailable
:no-command: :no-command:
.. dfhack-command:: plant .. dfhack-command:: plant

@ -3,7 +3,7 @@ power-meter
.. dfhack-tool:: .. dfhack-tool::
:summary: Allow pressure plates to measure power. :summary: Allow pressure plates to measure power.
:tags: unavailable fort gameplay buildings :tags: unavailable
:no-command: :no-command:
If you run `gui/power-meter` while building a pressure plate, the pressure If you run `gui/power-meter` while building a pressure plate, the pressure

@ -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.

@ -11,8 +11,8 @@ prospector
.. dfhack-command:: prospect .. dfhack-command:: prospect
:summary: Shows a summary of resources that exist on the map. :summary: Shows a summary of resources that exist on the map.
It can also calculate an estimate of resources available in the selected embark It can also calculate an estimate of resources available in the currently
area. highlighted embark area.
Usage Usage
----- -----

@ -3,7 +3,7 @@ rename
.. dfhack-tool:: .. dfhack-tool::
:summary: Easily rename things. :summary: Easily rename things.
:tags: unavailable adventure fort productivity buildings stockpiles units :tags: unavailable
Use `gui/rename` for an in-game interface. Use `gui/rename` for an in-game interface.

@ -3,7 +3,7 @@ rendermax
.. dfhack-tool:: .. dfhack-tool::
:summary: Modify the map lighting. :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 This plugin provides a collection of OpenGL lighting filters that affect how the
map is drawn to the screen. map is drawn to the screen.

@ -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.

@ -3,7 +3,7 @@ siege-engine
.. dfhack-tool:: .. dfhack-tool::
:summary: Extend the functionality and usability of siege engines. :summary: Extend the functionality and usability of siege engines.
:tags: unavailable fort gameplay buildings :tags: unavailable
:no-command: :no-command:
Siege engines in DF haven't been updated since the game was 2D, and can only aim Siege engines in DF haven't been updated since the game was 2D, and can only aim

@ -2,59 +2,159 @@ sort
==== ====
.. dfhack-tool:: .. dfhack-tool::
:summary: Sort lists shown in the DF interface. :summary: Search and sort lists shown in the DF interface.
:tags: unavailable fort productivity interface :tags: fort productivity interface
:no-command: :no-command:
.. dfhack-command:: sort-items The ``sort`` tool provides search and sort functionality for lists displayed in
:summary: Sort the visible item list. the DF interface.
.. dfhack-command:: sort-units Searching and sorting functionality is provided by `overlay` widgets, and
:summary: Sort the visible unit list. 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 <property> [<property> ...] The squad assignment screen can be sorted by name, by arrival order, by stress,
sort-units <property> [<property> ...] by various military-related skills, or by long-term military potential.
Both commands sort the visible list using the given sequence of comparisons. If sorted by "melee effectiveness" (the default), then the citizens are sorted
Each property can be prefixed with a ``<`` or ``>`` character to indicate according to how well they will perform in battle when using the weapon they
whether elements that don't have the given property defined go first or last have the most skill in. The effectiveness rating also takes into account
(respectively) in the sorted list. 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`` The "effectiveness" sorts are the ones you should be using if you need the best
Sort a list of items by material, then by type, then by quality squad you can make right now. The numbers to the left of the unit list indicate
``sort-units profession name`` exactly how effective that dwarf is expected to be. Light green numbers
Sort a list of units by profession, then by name 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 <Stress#Recovering_unhappy_dwarves>` to
help improve their mood.
- ``type`` Similarly, sorting by "need for training" will show you the dwarves that are
- ``description`` feeling the most unfocused because they are having their military training
- ``base_quality`` needs unmet.
- ``quality``
- ``improvement``
- ``wear``
- ``material``
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`` If sorting is done by "melee potential", then citizens are arranged based on
- ``age`` genetic predispositions in physical and mental attributes, as well as body
- ``arrival`` size. Dwarves (and other humanoid creatures) with higher ratings are expected
- ``noble`` to be more effective in melee combat if they train their attributes to their
- ``profession`` genetic maximum.
- ``profession_class``
- ``race`` Similarly, the "ranged potential" sort orders citizens by genetic
- ``squad`` predispositions in physical and mental attributes that are relevant to ranged
- ``squad_position`` combat. Dwarves (and other humanoid creatures) with higher rating are expected
- ``happiness`` 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).

@ -3,7 +3,7 @@ spectate
.. dfhack-tool:: .. dfhack-tool::
:summary: Automatically follow productive dwarves. :summary: Automatically follow productive dwarves.
:tags: unavailable fort interface :tags: fort interface
Usage Usage
----- -----

@ -3,7 +3,7 @@ steam-engine
.. dfhack-tool:: .. dfhack-tool::
:summary: Allow modded steam engine buildings to function. :summary: Allow modded steam engine buildings to function.
:tags: unavailable fort gameplay buildings :tags: unavailable
:no-command: :no-command:
The steam-engine plugin detects custom workshops with the string The steam-engine plugin detects custom workshops with the string

@ -3,7 +3,7 @@ stockflow
.. dfhack-tool:: .. dfhack-tool::
:summary: Queue manager jobs based on free space in stockpiles. :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 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 stockpiles and queue jobs through the manager to produce items to fill the free

@ -95,6 +95,19 @@ file are:
specific item types, and any toggles the category might have (like Prepared specific item types, and any toggles the category might have (like Prepared
meals for the Food category). 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: .. _stockpiles-library:
The stockpiles settings library The stockpiles settings library

@ -3,7 +3,7 @@ stocks
.. dfhack-tool:: .. dfhack-tool::
:summary: Enhanced fortress stock management interface. :summary: Enhanced fortress stock management interface.
:tags: unavailable fort productivity items :tags: unavailable
When the plugin is enabled, two new hotkeys become available: When the plugin is enabled, two new hotkeys become available:

@ -12,10 +12,10 @@ Usage
strangemood [<options>] strangemood [<options>]
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 Trigger a strange mood for the selected unit that will cause them to become
a legendary armorsmith. a legendary armorsmith.

@ -3,7 +3,7 @@ title-folder
.. dfhack-tool:: .. dfhack-tool::
:summary: Displays the DF folder name in the window title bar. :summary: Displays the DF folder name in the window title bar.
:tags: unavailable interface :tags: unavailable
:no-command: :no-command:
Usage Usage

Some files were not shown because too many files have changed in this diff Show More