Merge branch 'develop' of https://github.com/DFHack/dfhack into develop

develop
Japa 2015-12-24 22:14:18 +05:30
commit 15a6eb90d4
45 changed files with 826 additions and 222 deletions

42
.gitignore vendored

@ -1,18 +1,9 @@
# linux backup files # linux backup files
*~ *~
#Kdevelop project file #Kdevelop project files
*.kdev4 *.kdev4
.kdev4
# compiled binaries
output/*
# this one is important, track it
!output/Memory.xml
# a file generated by cmake
dfhack/include/config.h
library/private/config.h
# any build folders # any build folders
build*/ build*/
@ -20,13 +11,13 @@ nix
buntu buntu
build/VC2010 build/VC2010
#except for the real one
!build/
# Sphinx generated documentation # Sphinx generated documentation
docs/_* docs/_*
docs/html/ docs/html/
#except for the real one
!build/
# in-place build # in-place build
build/Makefile build/Makefile
build/CMakeCache.txt build/CMakeCache.txt
@ -35,27 +26,17 @@ build/CMakeFiles
build/doc build/doc
build/lua build/lua
build/bin build/bin
build/depends
build/library build/library
build/tools
build/plugins build/plugins
build/depends build/scripts
build/install_manifest.txt build/install_manifest.txt
build/README.html build/_CPack_Packages
build/LUA_API.html build/dfhack-*.zip
build/COMPILE.html build/dfhack-*.bz2
#ignore Kdevelop stuff
.kdev4
#fake curses header
examples/fake-curses.h
# Python binding binaries # Python binding binaries
*.pyc *.pyc
dfhack/python/pydfhack/_pydfhack.so
dfhack/python/PyDFHack.egg-info
dfhack/python/build
dfhack/python/dist
# CPack stuff # CPack stuff
build/CPack*Config.cmake build/CPack*Config.cmake
@ -71,6 +52,3 @@ tags
# Mac OS X .DS_Store files # Mac OS X .DS_Store files
.DS_Store .DS_Store
# autogenerated include-all.rst files
**include-all.rst

@ -22,6 +22,7 @@ script:
- python travis/script-syntax.py --ext=rb --cmd="ruby -c" --path scripts/ - python travis/script-syntax.py --ext=rb --cmd="ruby -c" --path scripts/
- mkdir build-travis - mkdir build-travis
- cd build-travis - cd build-travis
- cmake .. && make -j3 - cmake .. -DBUILD_DOCS:BOOL=ON
- make -j3
notifications: notifications:
email: false email: false

@ -24,8 +24,13 @@ project(dfhack)
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.5" OR ${GCC_VERSION_OUT} VERSION_GREATER "4.9.9") if (${GCC_VERSION_OUT} VERSION_LESS "4.5")
message(SEND_ERROR "${COMPILER_PATH} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.5 through 4.9") message(SEND_ERROR "${COMPILER_PATH} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.5 or later")
elseif (${GCC_VERSION_OUT} VERSION_GREATER "4.9.9")
# GCC 5 changes ABI name mangling to enable C++11 changes.
# This must be disabled to enable linking against DF.
# http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
endif() endif()
endmacro() endmacro()
@ -92,8 +97,9 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif() endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.40.24") set(DF_VERSION "0.42.03")
SET(DFHACK_RELEASE "r5") SET(DFHACK_RELEASE "alpha1")
SET(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")

@ -29,6 +29,13 @@ Changelog
.. contents:: .. contents::
:depth: 2 :depth: 2
DFHack future
=============
New Features
------------
- `search-plugin`: Support for the location occupation assignment menu
DFHack 0.40.24-r5 DFHack 0.40.24-r5
================= =================

@ -216,15 +216,13 @@ enable \
zone \ zone \
stocks \ stocks \
autochop \ autochop \
stockflow \
stockpiles stockpiles
#end a line with a backslash to make it continue to the next line. The \ is deleted for the final command. #end a line with a backslash to make it continue to the next line. The \ is deleted for the final command.
# Multiline commands are ONLY supported for scripts like dfhack.init. You cannot do multiline command manually on the DFHack console. # Multiline commands are ONLY supported for scripts like dfhack.init. You cannot do multiline command manually on the DFHack console.
# You cannot extend a commented line. # You cannot extend a commented line.
# You can comment out the extension of a line. # You can comment out the extension of a line.
# allow the fortress bookkeeper to queue jobs through the manager
enable stockflow
# enable mouse controls and sand indicator in embark screen # enable mouse controls and sand indicator in embark screen
embark-tools enable sand mouse embark-tools enable sand mouse

@ -10,24 +10,43 @@ and `install the latest release instead <installing>`.
.. contents:: .. contents::
:depth: 2 :depth: 2
.. _compile-how-to-get-the-code:
How to get the code How to get the code
=================== ===================
DFHack doesn't have any kind of system of code snapshots in place, so you will have to DFHack doesn't have any kind of system of code snapshots in place, so you will have to
get code from the github repository using git. How to get git is described under get code from the GitHub repository using Git. How to get Git is described under
the instructions for each platform. the instructions for each platform.
To get the code:: To get the latest release code (master branch)::
git clone --recursive https://github.com/DFHack/dfhack git clone --recursive https://github.com/DFHack/dfhack
cd dfhack cd dfhack
If your version of git does not support the ``--recursive`` flag, you will need to omit it and run If your version of Git does not support the ``--recursive`` flag, you will need to omit it and run
``git submodule update --init`` after entering the dfhack directory. ``git submodule update --init`` after entering the dfhack directory.
To get the latest development code (develop branch), clone as above and then::
git checkout develop
git submodule update
**Important note regarding submodule update and changing branches**:
You must run ``git submodule update`` every time you change Git branch,
for example when switching between master and develop branches and back.
Contributing to DFHack
======================
If you want to get involved with the development, create an account on If you want to get involved with the development, create an account on
Github, make a clone there and then use that as your remote repository instead. GitHub, make a clone there and then use that as your remote repository instead.
We'd love that; join us on IRC (#dfhack channel on freenode) if you need help.
We'd love that; join us on IRC (#dfhack channel on freenode) for discussion,
and whenever you need help.
For lots more details on contributing to DFHack, including pull requests, code format,
and more, please see `contributing-code`.
Build types Build types
@ -40,7 +59,8 @@ Without specifying a build type or 'None', cmake uses the
``CMAKE_CXX_FLAGS`` variable for building. ``CMAKE_CXX_FLAGS`` variable for building.
Valid and useful build types include 'Release', 'Debug' and Valid and useful build types include 'Release', 'Debug' and
'RelWithDebInfo'. 'Debug' is not available on Windows. 'RelWithDebInfo'.
'Debug' is not available on Windows, use 'RelWithDebInfo' instead.
Linux Linux
@ -51,15 +71,25 @@ Dependencies
------------ ------------
DFHack is meant to be installed into an existing DF folder, so get one ready. DFHack is meant to be installed into an existing DF folder, so get one ready.
We assume that any Linux platform will have ``git`` available. We assume that any Linux platform will have ``git`` available (though it may
require installing from your package manager.)
To build DFHack you need a version of GCC 4.x capable of compiling for 32-bit To build DFHack you need GCC version 4.5 or later, capable of compiling for 32-bit
(i386) targets. GCC 4.5 is easiest to work with due to avoiding libstdc++ issues (i386) targets. GCC 4.5 is easiest to work with due to avoiding libstdc++ issues
(see below), but any later 4.x version should work as well. GCC 5.x will not (see below), but any version from 4.5 onwards (including 5.x) will work.
work due to ABI changes (the entire plugin loading system won't work, for
example). On 64-bit distributions, you'll need the multilib development tools On 64-bit distributions, you'll need the multilib development tools and libraries:
and libraries (``gcc-multilib`` or ``gcc-4.x-multilib`` on Debian). Note that
installing a 32-bit GCC on 64-bit systems (e.g. ``gcc:i386`` on Debian) will * ``gcc-multilib`` and ``g++-multilib``
* If you have installed a non-default version of GCC - for example, GCC 4.5 on a
distribution that defaults to 5.x - you may need to add the version number to
the multilib packages.
* For example, ``gcc-4.5-multilib`` and ``g++-4.5-multilib`` if installing for GCC 4.5
on a system that uses a later GCC version.
* This is definitely required on Ubuntu/Debian, check if using a different distribution.
Note that installing a 32-bit GCC on 64-bit systems (e.g. ``gcc:i386`` on Debian) will
typically *not* work, as it depends on several other 32-bit libraries that typically *not* work, as it depends on several other 32-bit libraries that
conflict with system libraries. Alternatively, you might be able to use ``lxc`` conflict with system libraries. Alternatively, you might be able to use ``lxc``
to to
@ -71,13 +101,24 @@ Before you can build anything, you'll also need ``cmake``. It is advisable to al
You also need perl and the XML::LibXML and XML::LibXSLT perl packages (for the code generation parts). You also need perl and the XML::LibXML and XML::LibXSLT perl packages (for the code generation parts).
You should be able to find them in your distro repositories. You should be able to find them in your distro repositories.
* On Arch linux, ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``)
* On 64-bit Ubuntu, ``apt-get install zlib1g-dev:i386 libxml-libxml-perl libxml-libxslt-perl``.
* On 32-bit Ubuntu, ``apt-get install gcc-multilib g++-multilib zlib1g-dev libxml-libxml-perl libxml-libxslt-perl``.
* Debian-derived distros should have similar requirements.
To build Stonesense, you'll also need OpenGL headers. To build Stonesense, you'll also need OpenGL headers.
Here are some package install commands for various platforms:
* On Arch linux:
* For the required Perl modules: ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``)
* On 64-bit Ubuntu:
* ``apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev:i386 libxml-libxml-perl libxml-libxslt-perl``.
* On 32-bit Ubuntu:
* ``apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev libxml-libxml-perl libxml-libxslt-perl``.
* Debian-derived distros should have similar requirements.
Build Build
----- -----
@ -85,11 +126,12 @@ Building is fairly straightforward. Enter the ``build`` folder (or create an
empty folder in the DFHack directory to use instead) and start the build like this:: empty folder in the DFHack directory to use instead) and start the build like this::
cd build cd build
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/home/user/DF cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make install make install # or make -jX install on multi-core systems to compile with X parallel processes
Obviously, replace the install path with path to your DF. This will build the library <path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
along with the normal set of plugins and install them into your DF folder. version for the DFHack you are building. This will build the library along
with the normal set of plugins and install them into your DF folder.
Alternatively, you can use ccmake instead of cmake:: Alternatively, you can use ccmake instead of cmake::
@ -98,21 +140,20 @@ Alternatively, you can use ccmake instead of cmake::
make install make install
This will show a curses-based interface that lets you set all of the This will show a curses-based interface that lets you set all of the
extra options. You can also use a cmake-friendly IDE like KDevelop 4 extra options. You can also use a cmake-friendly IDE like KDevelop 4
or the cmake-gui program. or the cmake-gui program.
Incompatible libstdc++ Incompatible libstdc++
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
When compiling dfhack yourself, it builds against your system libstdc++. When compiling dfhack yourself, it builds against your system libstdc++.
When Dwarf Fortress runs, it uses a libstdc++ shipped with the binary, which When Dwarf Fortress runs, it uses a libstdc++ shipped with the binary, which
comes from GCC 4.5 and is incompatible with code compiled with newer GCC versions. comes from GCC 4.5 and is incompatible with code compiled with newer GCC versions.
This manifests itself with the error message:: This manifests itself with an error message such as::
./libs/Dwarf_Fortress: /pathToDF/libs/libstdc++.so.6: version ./libs/Dwarf_Fortress: /pathToDF/libs/libstdc++.so.6: version
`GLIBCXX_3.4.15' not found (required by ./hack/libdfhack.so) `GLIBCXX_3.4.15' not found (required by ./hack/libdfhack.so)
To fix this, you can compile with GCC 4.5 or remove the libstdc++ shipped with To fix this you can compile with GCC 4.5 or remove the libstdc++ shipped with
DF, which causes DF to use your system libstdc++ instead:: DF, which causes DF to use your system libstdc++ instead::
cd /path/to/DF/ cd /path/to/DF/
@ -121,7 +162,9 @@ DF, which causes DF to use your system libstdc++ instead::
Note that distributing binaries compiled with newer GCC versions requires end- Note that distributing binaries compiled with newer GCC versions requires end-
users to delete libstdc++ themselves and have a libstdc++ on their system from users to delete libstdc++ themselves and have a libstdc++ on their system from
the same GCC version or newer. For this reason, distributing anything compiled the same GCC version or newer. For this reason, distributing anything compiled
with GCC versions newer than 4.8 is discouraged. with GCC versions newer than 4.5 is discouraged. In the future we may start
bundling a later libstdc++ as part of the DFHack package, so as to enable
compilation-for-distribution with a GCC newer than 4.5.
Mac OS X Mac OS X
======== ========
@ -134,12 +177,41 @@ following environment variable::
export MACOSX_DEPLOYMENT_TARGET=10.9 export MACOSX_DEPLOYMENT_TARGET=10.9
Note for El Capitan (OSX 10.11) and XCode 7.x users
---------------------------------------------------
* You will probably find when following the instructions below that GCC 4.5 will
fail to install on OSX 10.11, or any older OSX that is using XCode 7.
* There are two workarounds:
* Install GCC 5.x instead (``brew install gcc5``), and then after compile
replace ``hack/libstdc++.6.dylib`` with a symlink to GCC 5's i386
version of this file::
cd <path to df>/hack && mv libstdc++.6.dylib libstdc++.6.dylib.orig &&
ln -s /usr/local/Cellar/gcc5/5.2.0/lib/gcc/5/i386/libstdc++.6.dylib .
* Install XCode 6, which is available as a free download from the Apple
Developer Center.
* Either install this as your only XCode, or install it additionally
to XCode 7 and then switch between them using ``xcode-select``
* Ensure XCode 6 is active before attempting to install GCC 4.5 and
whenever you are compiling DFHack with GCC 4.5.
Dependencies and system set-up
------------------------------
#. Download and unpack a copy of the latest DF #. Download and unpack a copy of the latest DF
#. Install Xcode from Mac App Store #. Install Xcode from Mac App Store
#. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools.
#. Install the XCode Command Line Tools by running the following command::
xcode-select --install
#. Install dependencies #. Install dependencies
Using `Homebrew <http://brew.sh/>`_:: Using `Homebrew <http://brew.sh/>`_ (recommended)::
brew tap homebrew/versions brew tap homebrew/versions
brew install git brew install git
@ -153,46 +225,70 @@ following environment variable::
Macports will take some time - maybe hours. At some point it may ask Macports will take some time - maybe hours. At some point it may ask
you to install a Java environment; let it do so. you to install a Java environment; let it do so.
#. Install perl dependencies It is recommended to use Homebrew instead of MacPorts, as it is generally
cleaner, quicker, and smarter. For example, installing
MacPort's GCC 4.5 will install more than twice as many dependencies
as Homebrew's will, and all in both 32bit and 64bit variants.
Homebrew also doesn't require constant use of sudo.
1. ``sudo cpan`` #. Install Perl dependencies
If this is the first time you've run cpan, you will need to go through the setup * Using system Perl
process. Just stick with the defaults for everything and you'll be fine.
If you are running OS X 10.6 (Snow Leopard) or earlier, good luck! * ``sudo cpan``
You'll need to open a separate Terminal window and run::
sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml If this is the first time you've run cpan, you will need to go through the setup
process. Just stick with the defaults for everything and you'll be fine.
2. ``install XML::LibXML`` If you are running OS X 10.6 (Snow Leopard) or earlier, good luck!
3. ``install XML::LibXSLT`` You'll need to open a separate Terminal window and run::
#. Get the dfhack source:: sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml
git clone --recursive https://github.com/DFHack/dfhack.git * ``install XML::LibXML``
cd dfhack * ``install XML::LibXSLT``
* In a separate, local Perl install
Rather than using system Perl, you might also want to consider
the Perl manager, `Perlbrew <http://perlbrew.pl>`_.
This manages Perl 5 locally under ``~/perl5/``, providing an easy
way to install Perl and run CPAN against it without ``sudo``.
It can maintain multiple Perl installs and being local has the
benefit of easy migration and insulation from OS issues and upgrades.
See http://perlbrew.pl/ for more details.
Building
--------
#. Set environment variables: * Get the DFHack source as per section `compile-how-to-get-the-code`, above.
* Set environment variables
Homebrew (if installed elsewhere, replace /usr/local with ``$(brew --prefix)``):: Homebrew (if installed elsewhere, replace /usr/local with ``$(brew --prefix)``)::
export CC=/usr/local/bin/gcc-4.5 export CC=/usr/local/bin/gcc-4.5
export CXX=/usr/local/bin/g++-4.5 export CXX=/usr/local/bin/g++-4.5
Macports:: Macports::
export CC=/opt/local/bin/gcc-mp-4.5 export CC=/opt/local/bin/gcc-mp-4.5
export CXX=/opt/local/bin/g++-mp-4.5 export CXX=/opt/local/bin/g++-mp-4.5
#. Build dfhack:: Change the version numbers appropriately if you installed a different version of GCC.
* Build dfhack::
mkdir build-osx mkdir build-osx
cd build-osx cd build-osx
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make make install # or make -j X install on multi-core systems to compile with X parallel processes
make install
<path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
version for the DFHack you are building.
.. _compile-windows:
Windows Windows
======= =======
@ -200,39 +296,150 @@ On Windows, DFHack replaces the SDL library distributed with DF.
Dependencies Dependencies
------------ ------------
You will need some sort of Windows port of git, or a GUI. Some examples: You will need the following:
* Microsoft Visual Studio 2010 SP1, with the C++ language
* Git
* CMake
* Perl with XML::LibXML and XML::LibXSLT
* It is recommended to install StrawberryPerl, which includes both.
Microsoft Visual Studio 2010 SP1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DFHack has to be compiled with the Microsoft Visual C++ 2010 SP1 toolchain; later
versions won't work against Dwarf Fortress due to ABI and STL incompatibilities.
At present, the only way to obtain the MSVC C++ 2010 toolchain is to install a
full copy of Microsoft Visual Studio 2010 SP1. The free Express version is sufficient.
You can grab it from `Microsoft's site <http://download.microsoft.com/download/1/E/5/1E5F1C0A-0D5B-426A-A603-1798B951DDAE/VS2010Express1.iso>`_.
You should also install the Visual Studio 2010 SP1 update.
You can confirm whether you have SP1 by opening the Visual Studio 2010 IDE
and selecting About from the Help menu. If you have SP1 it will have *SP1Rel*
at the end of the version number, for example: *Version 10.0.40219.1 SP1Rel*
Use of pre-SP1 releases has been reported to cause issues and is therefore not
supported by DFHack. Please ensure you are using SP1 before raising any Issues.
If your Windows Update is configured to receive updates for all Microsoft
Products, not just Windows, you will receive the SP1 update automatically
through Windows Update (you will probably need to trigger a manual check.)
If not, you can download it directly `from this Microsoft Download link <https://www.microsoft.com/en-gb/download/details.aspx?id=23691>`_.
Additional dependencies: installing with the Chocolatey Package Manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The remainder of dependencies - Git, CMake and StrawberryPerl - can be most
easily installed using the Chocolatey Package Manger. Chocolatey is a
\*nix-style package manager for Windows. It's fast, small (8-20MB on disk)
and very capable. Think "``apt-get`` for Windows."
Chocolatey is a preferred way of installing the required dependencies
as it's quicker, less effort and will install known-good utilities
guaranteed to have the correct setup (especially PATH).
To install Chocolatey and the required dependencies:
* Go to https://chocolatey.org in a web browser
* At the top of the page it will give you the install command to copy
* Copy the first one, which starts ``@powershell ...``
* It won't be repeated here in case it changes in future Chocolatey releases.
* Open an elevated (Admin) ``cmd.exe`` window
* On Windows 8 and later this can be easily achieved by:
* right-clicking on the Start Menu, or pressing Win+X.
* choosing "Command Prompt (Admin)"
* On earlier Windows: find ``cmd.exe`` in Start Menu, right click
and choose Open As Administrator.
* Paste in the Chocolatey install command and hit enter
* Close this ``cmd.exe`` window and open another Admin ``cmd.exe`` in the same way
* Run the following command::
choco install git cmake strawberryperl -y
* Close the Admin ``cmd.exe`` window; you're done!
You can now use all of these utilities from any normal ``cmd.exe`` window.
You only need Admin/elevated ``cmd.exe`` for running ``choco install`` commands;
for all other purposes, including compiling DFHack, you should use
a normal ``cmd.exe`` (or, better, an improved terminal like `Cmder <http://cmder.net/>`_;
details below, under Build.)
**NOTE**: you can run the above ``choco install`` command even if you already have
Git, CMake or StrawberryPerl installed. Chocolatey will inform you if any software
is already installed and won't re-install it. In that case, please check the PATHs
are correct for that utility as listed in the manual instructions below. Or, better,
manually uninstall the version you have already and re-install via Chocolatey,
which will ensure the PATH are set up right and will allow Chocolatey to manage
that program for you in future.
Additional dependencies: installing manually
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you prefer to install manually rather than using Chocolatey, details and
requirements are as below. If you do install manually, please ensure you
have all PATHs set up correctly.
Git
^^^
Some examples:
* `Git for Windows <https://git-for-windows.github.io>`_ (command-line and GUI) * `Git for Windows <https://git-for-windows.github.io>`_ (command-line and GUI)
* `tortoisegit <https://tortoisegit.org>`_ (GUI and File Explorer integration) * `tortoisegit <https://tortoisegit.org>`_ (GUI and File Explorer integration)
You need ``cmake``. Get the win32 installer version from CMake
^^^^^
You can get the win32 installer version from
`the official site <http://www.cmake.org/cmake/resources/software.html>`_. `the official site <http://www.cmake.org/cmake/resources/software.html>`_.
It has the usual installer wizard. Make sure you let it add its binary folder It has the usual installer wizard. Make sure you let it add its binary folder
to your binary search PATH so the tool can be later run from anywhere. to your binary search PATH so the tool can be later run from anywhere.
You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient. Perl / Strawberry Perl
Grab it from `Microsoft's site <http://download.microsoft.com/download/1/E/5/1E5F1C0A-0D5B-426A-A603-1798B951DDAE/VS2010Express1.iso>`_. ^^^^^^^^^^^^^^^^^^^^^^
You'll also need the Visual Studio 2010 SP1 update. For the code generation parts you'll need Perl 5 with XML::LibXML and XML::LibXSLT.
`Strawberry Perl <http://strawberryperl.com>`_ is recommended as it includes
all of the required packages in a single, easy install.
After install, ensure Perl is in your user's PATH. This can be edited from
``Control Panel -> System -> Advanced System Settings -> Environment Variables``.
The following three directories must be in PATH, in this order:
For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. * ``<path to perl>\c\bin``
`Strawberry Perl <http://strawberryperl.com>`_ works nicely for this and includes * ``<path to perl>\perl\site\bin``
all of the required packages. * ``<path to perl>\perl\bin``
If you already have a different version of perl (for example the one from cygwin), Be sure to close and re-open any existing ``cmd.exe`` windows after updating
you can run into some trouble. Either remove the other perl install from PATH, or your PATH.
install libxml and libxslt for it instead.
If you already have a different version of Perl (for example the one from Cygwin),
you can run into some trouble. Either remove the other Perl install from PATH, or
install XML::LibXML and XML::LibXSLT for it using CPAN.
Build Build
----- -----
There are several different batch files in the ``build`` folder along There are several different batch files in the ``build`` folder along
with a script that's used for picking the DF path. with a script that's used for picking the DF path.
First, run ``set_df_path.vbs`` and point the dialog that pops up at your First, run ``set_df_path.vbs`` and point the dialog that pops up at
DF folder that you want to use for development. a suitable DF installation which is of the appropriate version for the DFHack
Next, run one of the scripts with ``generate`` prefix. These create the MSVC solution file(s): you are compiling. The result is the creation of the file ``DF_PATH.txt`` in
the build directory. It contains the full path to the destination directory.
You could therefore also create this file manually - or copy in a pre-prepared
version - if you prefer.
Next, run one of the scripts with ``generate`` prefix. These create the MSVC
solution file(s):
* ``all`` will create a solution with everything enabled (and the kitchen sink). * ``all`` will create a solution with everything enabled (and the kitchen sink).
* ``gui`` will pop up the cmake gui and let you pick and choose what to build. * ``gui`` will pop up the CMake GUI and let you choose what to build.
This is probably what you want most of the time. Set the options you are interested This is probably what you want most of the time. Set the options you are interested
in, then hit configure, then generate. More options can appear after the configure step. in, then hit configure, then generate. More options can appear after the configure step.
* ``minimal`` will create a minimal solution with just the bare necessities - * ``minimal`` will create a minimal solution with just the bare necessities -
@ -240,12 +447,174 @@ Next, run one of the scripts with ``generate`` prefix. These create the MSVC sol
Then you can either open the solution with MSVC or use one of the msbuild scripts: Then you can either open the solution with MSVC or use one of the msbuild scripts:
Building/installing from the command line:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the build directory you will find several ``.bat`` files:
* Scripts with ``build`` prefix will only build DFHack. * Scripts with ``build`` prefix will only build DFHack.
* Scripts with ``install`` prefix will build DFHack and install it to the previously selected DF path. * Scripts with ``install`` prefix will build DFHack and install it to the previously selected DF path.
* Scripts with ``package`` prefix will build and create a .zip package of DFHack. * Scripts with ``package`` prefix will build and create a .zip package of DFHack.
When you open the solution in MSVC, make sure you never use the Debug builds. Those aren't Compiling from the command line is generally the quickest and easiest option.
binary-compatible with DF. If you try to use a debug build with DF, you'll only get crashes. However be aware that due to the limitations of ``cmd.exe`` - especially in
For this reason the Windows "debug" scripts actually do RelWithDebInfo builds, versions of Windows prior to Windows 10 - it can be very hard to see what happens
so pick either Release or RelWithDebInfo build and build the INSTALL target. during a build. If you get a failure, you may miss important errors or warnings
due to the tiny window size and extremely limited scrollback. For that reason you
may prefer to compile in the IDE which will always show all build output.
Alternatively (or additionally), consider installing an improved Windows terminal
such as `Cmder <http://cmder.net/>`_. Easily installed through Chocolatey with:
``choco install cmder -y``.
**Note for Cygwin/msysgit users**: It is also possible to compile DFHack from a
Bash command line. This has three potential benefits:
* When you've installed Git and are using its Bash, but haven't added Git to your path:
* You can load Git's Bash and as long as it can access Perl and CMake, you can
use it for compile without adding Git to your system path.
* When you've installed Cygwin and its SSH server:
* You can now SSH in to your Windows install and compile from a remote terminal;
very useful if your Windows installation is a local VM on a \*nix host OS.
* In general: you can use Bash as your compilation terminal, meaning you have a decent
sized window, scrollback, etc.
* Whether you're accessing it locally as with Git's Bash, or remotely through
Cygwin's SSH server, this is far superior to using ``cmd.exe``.
You don't need to do anything special to compile from Bash. As long as your PATHs
are set up correctly, you can run the same generate- and build/install/package- bat
files as detailed above.
Building/installing from the Visual Studio IDE:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After running the CMake generate script you will have a new folder called VC2010.
Open the file ``dfhack.sln`` inside that folder. If you have multiple versions of
Visual Studio installed, make sure you open with Visual Studio 2010.
The first thing you must then do is change the build type. It defaults to Debug,
but this cannot be used on Windows. Debug is not binary-compatible with DF.
If you try to use a debug build with DF, you'll only get crashes and for this
reason the Windows "debug" scripts actually do RelWithDebInfo builds.
After loading the Solution, change the Build Type to either ``Release``
or ``RelWithDebInfo``.
Then build the ``INSTALL`` target listed under ``CMakePredefinedTargets``.
##########################
Building the documentation
##########################
DFHack documentation, like the file you are reading now, is created as .rst files,
which are in `reStructuredText (reST) <http://sphinx-doc.org/rest.html>`_ format.
This is a documenation format that has come from the Python community. It is very
similar in concept - and in syntax - to Markdown, as found on GitHub and many other
places. However it is more advanced than Markdown, with more features available when
compiled to HTML, such as automatic tables of contents, cross-linking, special
external links (forum, wiki, etc) and more. The documentation is compiled by a
Python tool, `Sphinx <http://sphinx-doc.org>`_.
The DFHack build process will compile the documentation but this has been disabled
by default. You only need to build the docs if you're changing them, or perhaps
if you want a local HTML copy; otherwise, read them easily online at
`ReadTheDoc's DFHack hosted documentation <https://dfhack.readthedocs.org>`_.
(Note that even if you do want a local copy, it is certainly not necesesary to
compile the documentation in order to read it. Like Markdown, reST documents are
designed to be just as readable in a plain-text editor as they are in HTML format.
The main thing you lose in plain text format is hyperlinking.)
Enabling documentation building
===============================
First, make sure you have followed all the necessary steps for your platform as
outlined in the rest of this document.
To compile documentation with DFHack, add the following flag to your ``cmake`` command::
-DBUILD_DOCS:bool=ON
For example::
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DBUILD_DOCS:bool=ON -DCMAKE_INSTALL_PREFIX=<path to DF>
Alternatively you can use the CMake GUI which allows options to be changed easily.
On Windows you should either use ``generate-msvc-gui.bat`` and set the option
through the GUI, or else if you want to use an alternate file, such as
``generate-msvc-all.bat``, you will need to edit it to add the flag.
Or you could just run ``cmake`` on the command line like in other platforms.
Required dependencies
=====================
In order to build the documentation, you must have Python with Sphinx
version 1.3.1 or later. Both Python 2.x and 3.x are supported.
When installing Sphinx from OS package managers, be aware that there is
another program called Sphinx, completely unrelated to documentation management.
Be sure you are installing the right Sphinx; it may be called ``python-sphinx``,
for example. To avoid doubt, ``pip`` can be used instead as detailed below.
Linux
-----
Most Linux distributions will include Python as standard.
Check your package manager to see if Sphinx 1.3.1 or later is available,
but at the time of writing Ubuntu for example only has 1.2.x.
You can instead install Sphinx with the pip package manager. This may need
to be installed from your OS package manager; this is the case on Ubuntu.
On Ubuntu/Debian, use the following to first install pip::
sudo apt-get install python-pip
Once pip is available, you can then install the Python Sphinx module with::
pip install sphinx
If you run this as a normal user it will install a local copy for your user only.
Run it with sudo if you want a system-wide install. Either is fine for DFHack,
however if installing locally do check that ``sphinx-build`` is in your path.
It may be installed in a directory such as ``~/.local/bin/``, so after pip
install, find ``sphinx-build`` and ensure its directory is in your local ``$PATH``.
Mac OS X
--------
OS X has Python 2.7 installed by default, but it does not have the pip package manager.
You can install Homebrew's Python 3, which includes pip, and then install the
latest Sphinx using pip::
brew install python3
pip3 install sphinx
Alternatively, you can simply install Sphinx 1.3.x directly from Homebrew::
brew install sphinx-doc
This will install Sphinx for OS X's system Python 2.7, without needing pip.
Either method works; if you plan to use Python for other purposes, it might best
to install Homebrew's Python 3 so that you have the latest Python as well as pip.
If not, just installing sphinx-doc for OS X's system Python 2.7 is fine.
Windows
-------
Use the Chocolatey package manager to install Python and pip,
then use pip to install Sphinx.
Run the following commands from an elevated (Admin) ``cmd.exe``, after installing
Chocolatey as outlined in the `Windows section <compile-windows>`::
choco install python pip -y
Then close that Admin ``cmd.exe``, re-open another Admin ``cmd.exe``, and run::
pip install sphinx

@ -5,6 +5,8 @@ How to contribute to DFHack
.. contents:: .. contents::
.. _contributing-code:
Contributing Code Contributing Code
================= =================
Several things should be kept in mind when contributing code to DFHack. Several things should be kept in mind when contributing code to DFHack.

@ -37,8 +37,8 @@ allows easier development of new tools. As an open-source project under
Installing DFHack Installing DFHack
================= =================
DFHack is available for the SDL version of Dwarf Frtress on Windows, DFHack is available for the SDL version of Dwarf Fortress on Windows,
any modern Linux distribution, and OS X (10.6.8 to 10.9). any modern Linux distribution, and Mac OS X (10.6.8 and later).
It is possible to use Windows DF+DFHack under Wine on Linux or OS X. It is possible to use Windows DF+DFHack under Wine on Linux or OS X.
Most releases only support the version of DF mentioned in their title - for Most releases only support the version of DF mentioned in their title - for

@ -2148,8 +2148,6 @@ twice.
createitem createitem
========== ==========
Use `modtools/create-item` - this plugin is deprecated and will be removed soon.
Allows creating new items of arbitrary types and made of arbitrary materials. Allows creating new items of arbitrary types and made of arbitrary materials.
By default, items created are spawned at the feet of the selected unit. By default, items created are spawned at the feet of the selected unit.

@ -263,6 +263,11 @@ SET_PROPERTY(TARGET dfhack-version APPEND PROPERTY COMPILE_DEFINITIONS
DF_VERSION="${DF_VERSION}" DF_VERSION="${DF_VERSION}"
DFHACK_RELEASE="${DFHACK_RELEASE}" DFHACK_RELEASE="${DFHACK_RELEASE}"
) )
IF(DFHACK_PRERELEASE)
SET_PROPERTY(TARGET dfhack-version APPEND PROPERTY COMPILE_DEFINITIONS
DFHACK_PRERELEASE=1
)
ENDIF()
ADD_CUSTOM_TARGET(git-describe ADD_CUSTOM_TARGET(git-describe
COMMAND ${CMAKE_COMMAND} COMMAND ${CMAKE_COMMAND}
@ -304,10 +309,12 @@ ENDIF()
SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" )
IF(APPLE) IF(APPLE)
SET(SDL_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/SDL.framework) SET(DF_SDL_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/SDL.framework/Versions/A/SDL)
IF(NOT EXISTS ${SDL_LIBRARY}) IF(NOT EXISTS ${DF_SDL_LIBRARY})
MESSAGE(SEND_ERROR "SDL framework not found. Make sure CMAKE_INSTALL_PREFIX is specified and correct.") MESSAGE(SEND_ERROR "SDL framework not found. Make sure CMAKE_INSTALL_PREFIX is specified and correct.")
ENDIF() ENDIF()
SET(SDL_LIBRARY ${CMAKE_BINARY_DIR}/SDL)
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DF_SDL_LIBRARY} ${SDL_LIBRARY})
SET(CXX_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/libstdc++.6.dylib) SET(CXX_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/libstdc++.6.dylib)
SET(ZIP_LIBRARY /usr/lib/libz.dylib) SET(ZIP_LIBRARY /usr/lib/libz.dylib)
TARGET_LINK_LIBRARIES(dfhack ${SDL_LIBRARY}) TARGET_LINK_LIBRARIES(dfhack ${SDL_LIBRARY})

@ -2057,6 +2057,12 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
break; break;
} }
if (event == SC_WORLD_LOADED && Version::is_prerelease())
{
runCommand(out, "gui/prerelease-warning");
std::cerr << "loaded map in prerelease build" << std::endl;
}
EventManager::onStateChange(out, event); EventManager::onStateChange(out, event);
buildings_onStateChange(out, event); buildings_onStateChange(out, event);

@ -33,5 +33,14 @@ namespace DFHack {
return false; return false;
#endif #endif
} }
bool is_prerelease()
{
#ifdef DFHACK_PRERELEASE
return true;
#else
return false;
#endif
}
} }
} }

@ -226,9 +226,9 @@ DFhackCExport void * SDL_GetVideoSurface(void)
static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0; static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0;
DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect)
{ {
if ( dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 ) DFHack::Core & c = DFHack::Core::getInstance();
if ( c.isValid() && dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 )
{ {
DFHack::Core & c = DFHack::Core::getInstance();
DFHack::Graphic* g = c.getGraphic(); DFHack::Graphic* g = c.getGraphic();
DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h); DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h);

@ -271,9 +271,9 @@ DFhackCExport vPtr SDL_SetVideoMode(int width, int height, int bpp, uint32_t fla
static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0; static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0;
DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect)
{ {
if ( dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 ) DFHack::Core & c = DFHack::Core::getInstance();
if ( c.isValid() && dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 )
{ {
DFHack::Core & c = DFHack::Core::getInstance();
DFHack::Graphic* g = c.getGraphic(); DFHack::Graphic* g = c.getGraphic();
DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h); DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h);

@ -1418,6 +1418,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP_VERSION_FUNC(getGitDescription, git_description), WRAP_VERSION_FUNC(getGitDescription, git_description),
WRAP_VERSION_FUNC(getGitCommit, git_commit), WRAP_VERSION_FUNC(getGitCommit, git_commit),
WRAP_VERSION_FUNC(isRelease, is_release), WRAP_VERSION_FUNC(isRelease, is_release),
WRAP_VERSION_FUNC(isPrerelease, is_prerelease),
{ NULL, NULL } { NULL, NULL }
}; };

@ -7,6 +7,7 @@ namespace DFHack {
const char *git_description(); const char *git_description();
const char *git_commit(); const char *git_commit();
bool is_release(); bool is_release();
bool is_prerelease();
} }
} }
@ -17,4 +18,5 @@ namespace DFHack {
#define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description()) #define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description())
#define DFHACK_GIT_COMMIT (DFHack::Version::git_commit()) #define DFHACK_GIT_COMMIT (DFHack::Version::git_commit())
#define DFHACK_IS_RELEASE (DFHack::Version::is_release()) #define DFHACK_IS_RELEASE (DFHack::Version::is_release())
#define DFHACK_IS_PRERELEASE (DFHack::Version::is_prerelease())
#endif #endif

@ -197,6 +197,13 @@ INSTANTIATE_WRAPPERS(10, (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10),
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9, class A10, class A11 #define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9, class A10, class A11
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11))
INSTANTIATE_WRAPPERS(11, (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11),
(OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11),
(vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9,vA10,vA11),
(out,vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9,vA10,vA11),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7); LOAD_ARG(A8);
LOAD_ARG(A9); LOAD_ARG(A10); LOAD_ARG(A11);)
#undef FW_TARGS #undef FW_TARGS
#undef FW_TARGSC #undef FW_TARGSC

@ -131,6 +131,7 @@ namespace DFHack
struct DwarfmodeDims { struct DwarfmodeDims {
int map_x1, map_x2, menu_x1, menu_x2, area_x1, area_x2; int map_x1, map_x2, menu_x1, menu_x2, area_x1, area_x2;
int y1, y2; int y1, y2;
int map_y1, map_y2;
bool menu_on, area_on, menu_forced; bool menu_on, area_on, menu_forced;
rect2d map() { return mkrect_xy(map_x1, y1, map_x2, y2); } rect2d map() { return mkrect_xy(map_x1, y1, map_x2, y2); }

@ -184,16 +184,16 @@ namespace DFHack
DFHACK_EXPORT bool inGraphicsMode(); DFHACK_EXPORT bool inGraphicsMode();
/// Paint one screen tile with the given pen /// Paint one screen tile with the given pen
DFHACK_EXPORT bool paintTile(const Pen &pen, int x, int y); DFHACK_EXPORT bool paintTile(const Pen &pen, int x, int y, bool map = false);
/// Retrieves one screen tile from the buffer /// Retrieves one screen tile from the buffer
DFHACK_EXPORT Pen readTile(int x, int y); DFHACK_EXPORT Pen readTile(int x, int y);
/// Paint a string onto the screen. Ignores ch and tile of pen. /// Paint a string onto the screen. Ignores ch and tile of pen.
DFHACK_EXPORT bool paintString(const Pen &pen, int x, int y, const std::string &text); DFHACK_EXPORT bool paintString(const Pen &pen, int x, int y, const std::string &text, bool map = false);
/// Fills a rectangle with one pen. Possibly more efficient than a loop over paintTile. /// Fills a rectangle with one pen. Possibly more efficient than a loop over paintTile.
DFHACK_EXPORT bool fillRect(const Pen &pen, int x1, int y1, int x2, int y2); DFHACK_EXPORT bool fillRect(const Pen &pen, int x1, int y1, int x2, int y2, bool map = false);
/// Draws a standard dark gray window border with a title string /// Draws a standard dark gray window border with a title string
DFHACK_EXPORT bool drawBorder(const std::string &title); DFHACK_EXPORT bool drawBorder(const std::string &title);
@ -261,34 +261,34 @@ namespace DFHack
return *this; return *this;
} }
Painter &fill(const rect2d &area, const Pen &pen) { Painter &fill(const rect2d &area, const Pen &pen, bool map = false) {
rect2d irect = intersect(area, clip); rect2d irect = intersect(area, clip);
fillRect(pen, irect.first.x, irect.first.y, irect.second.x, irect.second.y); fillRect(pen, irect.first.x, irect.first.y, irect.second.x, irect.second.y, map);
return *this; return *this;
} }
Painter &fill(const rect2d &area) { return fill(area, cur_pen); } Painter &fill(const rect2d &area, bool map = false) { return fill(area, cur_pen, map); }
Painter &tile(const Pen &pen) { Painter &tile(const Pen &pen, bool map = false) {
if (isValidPos()) paintTile(pen, gcursor.x, gcursor.y); if (isValidPos()) paintTile(pen, gcursor.x, gcursor.y, map);
return advance(1); return advance(1);
} }
Painter &tile() { return tile(cur_pen); } Painter &tile(bool map = false) { return tile(cur_pen, map); }
Painter &tile(char ch) { return tile(cur_pen.chtile(ch)); } Painter &tile(char ch, bool map = false) { return tile(cur_pen.chtile(ch), map); }
Painter &tile(char ch, int tileid) { return tile(cur_pen.chtile(ch, tileid)); } Painter &tile(char ch, int tileid, bool map = false) { return tile(cur_pen.chtile(ch, tileid), map); }
Painter &string(const std::string &str, const Pen &pen) { Painter &string(const std::string &str, const Pen &pen, bool map = false) {
do_paint_string(str, pen); return advance(str.size()); do_paint_string(str, pen, map); return advance(str.size());
} }
Painter &string(const std::string &str) { return string(str, cur_pen); } Painter &string(const std::string &str, bool map = false) { return string(str, cur_pen, map); }
Painter &string(const std::string &str, int8_t fg) { return string(str, cur_pen.color(fg)); } Painter &string(const std::string &str, int8_t fg, bool map = false) { return string(str, cur_pen.color(fg), map); }
Painter &key(df::interface_key kc, const Pen &pen) { Painter &key(df::interface_key kc, const Pen &pen, bool map = false) {
return string(getKeyDisplay(kc), pen); return string(getKeyDisplay(kc), pen, map);
} }
Painter &key(df::interface_key kc) { return key(kc, cur_key_pen); } Painter &key(df::interface_key kc, bool map = false) { return key(kc, cur_key_pen, map); }
private: private:
void do_paint_string(const std::string &str, const Pen &pen); void do_paint_string(const std::string &str, const Pen &pen, bool map = false);
}; };
namespace Hooks { namespace Hooks {

@ -31,11 +31,13 @@ distribution.
#include "Export.h" #include "Export.h"
#include "modules/Items.h" #include "modules/Items.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "df/unit.h"
#include "df/caste_raw_flags.h"
#include "df/job_skill.h"
#include "df/mental_attribute_type.h"
#include "df/misc_trait_type.h" #include "df/misc_trait_type.h"
#include "df/physical_attribute_type.h" #include "df/physical_attribute_type.h"
#include "df/mental_attribute_type.h" #include "df/unit.h"
#include "df/job_skill.h"
namespace df namespace df
{ {
@ -219,6 +221,7 @@ DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isHidingCurse(df::unit *unit); DFHACK_EXPORT bool isHidingCurse(df::unit *unit);
DFHACK_EXPORT int getPhysicalAttrValue(df::unit *unit, df::physical_attribute_type attr); DFHACK_EXPORT int getPhysicalAttrValue(df::unit *unit, df::physical_attribute_type attr);
DFHACK_EXPORT int getMentalAttrValue(df::unit *unit, df::mental_attribute_type attr); DFHACK_EXPORT int getMentalAttrValue(df::unit *unit, df::mental_attribute_type attr);
DFHACK_EXPORT bool casteFlagSet(int race, int caste, df::caste_raw_flags flag);
DFHACK_EXPORT bool isCrazed(df::unit *unit); DFHACK_EXPORT bool isCrazed(df::unit *unit);
DFHACK_EXPORT bool isOpposedToLife(df::unit *unit); DFHACK_EXPORT bool isOpposedToLife(df::unit *unit);

@ -1366,12 +1366,13 @@ int32_t Items::createItem(df::item_type item_type, int16_t item_subtype, int16_t
} }
//makeItem //makeItem
vector<df::reaction_product*> out_products;
vector<df::item*> out_items; vector<df::item*> out_items;
vector<df::reaction_reagent*> in_reag; vector<df::reaction_reagent*> in_reag;
vector<df::item*> in_items; vector<df::item*> in_items;
df::enums::game_type::game_type type = *df::global::gametype; df::enums::game_type::game_type type = *df::global::gametype;
prod->produce(unit, &out_items, &in_reag, &in_items, 1, job_skill::NONE, prod->produce(unit, &out_products, &out_items, &in_reag, &in_items, 1, job_skill::NONE,
df::historical_entity::find(unit->civ_id), df::historical_entity::find(unit->civ_id),
((type == df::enums::game_type::DWARF_MAIN) || (type == df::enums::game_type::DWARF_RECLAIM)) ? df::world_site::find(df::global::ui->site_id) : NULL); ((type == df::enums::game_type::DWARF_MAIN) || (type == df::enums::game_type::DWARF_RECLAIM)) ? df::world_site::find(df::global::ui->site_id) : NULL);
if ( out_items.size() != 1 ) if ( out_items.size() != 1 )

@ -115,14 +115,14 @@ static void doSetTile(const Pen &pen, int x, int y, bool map)
GUI_HOOK_TOP(Screen::Hooks::set_tile)(pen, x, y, map); GUI_HOOK_TOP(Screen::Hooks::set_tile)(pen, x, y, map);
} }
bool Screen::paintTile(const Pen &pen, int x, int y) bool Screen::paintTile(const Pen &pen, int x, int y, bool map)
{ {
if (!gps || !pen.valid()) return false; if (!gps || !pen.valid()) return false;
auto dim = getWindowSize(); auto dim = getWindowSize();
if (x < 0 || x >= dim.x || y < 0 || y >= dim.y) return false; if (x < 0 || x >= dim.x || y < 0 || y >= dim.y) return false;
doSetTile(pen, x, y, false); doSetTile(pen, x, y, map);
return true; return true;
} }
@ -161,7 +161,7 @@ Pen Screen::readTile(int x, int y)
return pen; return pen;
} }
bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text) bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text, bool map)
{ {
auto dim = getWindowSize(); auto dim = getWindowSize();
if (!gps || y < 0 || y >= dim.y) return false; if (!gps || y < 0 || y >= dim.y) return false;
@ -176,14 +176,14 @@ bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text)
tmp.ch = text[i]; tmp.ch = text[i];
tmp.tile = (pen.tile ? pen.tile + uint8_t(text[i]) : 0); tmp.tile = (pen.tile ? pen.tile + uint8_t(text[i]) : 0);
paintTile(tmp, x+i, y); paintTile(tmp, x+i, y, map);
ok = true; ok = true;
} }
return ok; return ok;
} }
bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2) bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2, bool map)
{ {
auto dim = getWindowSize(); auto dim = getWindowSize();
if (!gps || !pen.valid()) return false; if (!gps || !pen.valid()) return false;
@ -197,7 +197,7 @@ bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2)
for (int x = x1; x <= x2; x++) for (int x = x1; x <= x2; x++)
{ {
for (int y = y1; y <= y2; y++) for (int y = y1; y <= y2; y++)
doSetTile(pen, x, y, false); doSetTile(pen, x, y, map);
} }
return true; return true;
@ -247,7 +247,7 @@ bool Screen::invalidate()
const Pen Screen::Painter::default_pen(0,COLOR_GREY,0); const Pen Screen::Painter::default_pen(0,COLOR_GREY,0);
const Pen Screen::Painter::default_key_pen(0,COLOR_LIGHTGREEN,0); const Pen Screen::Painter::default_key_pen(0,COLOR_LIGHTGREEN,0);
void Screen::Painter::do_paint_string(const std::string &str, const Pen &pen) void Screen::Painter::do_paint_string(const std::string &str, const Pen &pen, bool map)
{ {
if (gcursor.y < clip.first.y || gcursor.y > clip.second.y) if (gcursor.y < clip.first.y || gcursor.y > clip.second.y)
return; return;
@ -256,7 +256,7 @@ void Screen::Painter::do_paint_string(const std::string &str, const Pen &pen)
int len = std::min((int)str.size(), int(clip.second.x - gcursor.x + 1)); int len = std::min((int)str.size(), int(clip.second.x - gcursor.x + 1));
if (len > dx) if (len > dx)
paintString(pen, gcursor.x + dx, gcursor.y, str.substr(dx, len-dx)); paintString(pen, gcursor.x + dx, gcursor.y, str.substr(dx, len-dx), map);
} }
bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *ptile, int *pgs) bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *ptile, int *pgs)

@ -680,7 +680,7 @@ int Units::getMentalAttrValue(df::unit *unit, df::mental_attribute_type attr)
return std::max(0, value); return std::max(0, value);
} }
static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag) bool Units::casteFlagSet(int race, int caste, df::caste_raw_flags flag)
{ {
auto creature = df::creature_raw::find(race); auto creature = df::creature_raw::find(race);
if (!creature) if (!creature)

@ -1 +1 @@
Subproject commit bb950d99da543f314f3dd9babf1446c8331ff99d Subproject commit 636f7787552cc663f01561c7dc84f758cea027f1

@ -34,6 +34,12 @@ fi
# Save current terminal settings # Save current terminal settings
old_tty_settings=$(stty -g) old_tty_settings=$(stty -g)
# Use distro_fixes.sh from LNP if it exists
DISTROFIXES="distro_fixes.sh"
if [ -r "$DISTROFIXES" ]; then
. "./$DISTROFIXES"
fi
# Now run # Now run
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./hack/libs":"./hack" export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./hack/libs":"./hack"

@ -243,7 +243,9 @@ struct product_hook : improvement_product {
DEFINE_VMETHOD_INTERPOSE( DEFINE_VMETHOD_INTERPOSE(
void, produce, void, produce,
(df::unit *unit, std::vector<df::item*> *out_items, (df::unit *unit,
std::vector<df::reaction_product*> *out_products,
std::vector<df::item*> *out_items,
std::vector<df::reaction_reagent*> *in_reag, std::vector<df::reaction_reagent*> *in_reag,
std::vector<df::item*> *in_items, std::vector<df::item*> *in_items,
int32_t quantity, df::job_skill skill, int32_t quantity, df::job_skill skill,
@ -293,7 +295,7 @@ struct product_hook : improvement_product {
return; return;
} }
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site); INTERPOSE_NEXT(produce)(unit, out_products, out_items, in_reag, in_items, quantity, skill, entity, site);
} }
}; };

@ -521,7 +521,9 @@ static const struct labor_default default_labor_infos[] = {
/* HAUL_WATER */ {HAULERS, 0}, /* HAUL_WATER */ {HAULERS, 0},
/* GELD */ {ALLOW, 0}, /* GELD */ {ALLOW, 0},
/* BUILD_ROAD */ {HAULERS, 0}, /* BUILD_ROAD */ {HAULERS, 0},
/* BUILD_CONSTRUCTION */ {HAULERS, 0} /* BUILD_CONSTRUCTION */ {HAULERS, 0},
/* PAPERMAKING */ {ALLOW, 0},
/* BOOKBINDING */ {ALLOW, 0}
}; };
/** /**
@ -1134,7 +1136,7 @@ command_result autohauler (color_ostream &out, std::vector <std::string> & param
out << "All labors reset." << endl; out << "All labors reset." << endl;
return CR_OK; return CR_OK;
} }
else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") else if (parameters.size() == 1 && (parameters[0] == "list" || parameters[0] == "status"))
{ {
if (!enable_autohauler) if (!enable_autohauler)
{ {

@ -489,7 +489,9 @@ static const struct labor_default default_labor_infos[] = {
/* HAUL_WATER */ {HAULERS, false, 1, 200, 0}, /* HAUL_WATER */ {HAULERS, false, 1, 200, 0},
/* GELD */ {AUTOMATIC, false, 1, 200, 0}, /* GELD */ {AUTOMATIC, false, 1, 200, 0},
/* BUILD_ROAD */ {AUTOMATIC, false, 1, 200, 0}, /* BUILD_ROAD */ {AUTOMATIC, false, 1, 200, 0},
/* BUILD_CONSTRUCTION */ {AUTOMATIC, false, 1, 200, 0} /* BUILD_CONSTRUCTION */ {AUTOMATIC, false, 1, 200, 0},
/* PAPERMAKING */ {AUTOMATIC, false, 1, 200, 0},
/* BOOKBINDING */ {AUTOMATIC, false, 1, 200, 0}
}; };
static const int responsibility_penalties[] = { static const int responsibility_penalties[] = {
@ -1509,7 +1511,7 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
out << "All labors reset." << endl; out << "All labors reset." << endl;
return CR_OK; return CR_OK;
} }
else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") else if (parameters.size() == 1 && (parameters[0] == "list" || parameters[0] == "status"))
{ {
if (!enable_autolabor) if (!enable_autolabor)
{ {

@ -70,6 +70,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_item = false) bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_item = false)
{ {
vector<df::reaction_product*> out_products;
vector<df::item *> out_items; vector<df::item *> out_items;
vector<df::reaction_reagent *> in_reag; vector<df::reaction_reagent *> in_reag;
vector<df::item *> in_items; vector<df::item *> in_items;
@ -83,7 +84,7 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
if (dest_building != -1) if (dest_building != -1)
building = df::building::find(dest_building); building = df::building::find(dest_building);
prod->produce(unit, &out_items, &in_reag, &in_items, 1, job_skill::NONE, prod->produce(unit, &out_products, &out_items, &in_reag, &in_items, 1, job_skill::NONE,
df::historical_entity::find(unit->civ_id), df::historical_entity::find(unit->civ_id),
(World::isFortressMode()) ? df::world_site::find(ui->site_id) : NULL); (World::isFortressMode()) ? df::world_site::find(ui->site_id) : NULL);
if (!out_items.size()) if (!out_items.size())

@ -1222,7 +1222,6 @@ public:
job_to_labor_table[df::job_type::MakeChain] = jlf_make_object; job_to_labor_table[df::job_type::MakeChain] = jlf_make_object;
job_to_labor_table[df::job_type::MakeFlask] = jlf_make_object; job_to_labor_table[df::job_type::MakeFlask] = jlf_make_object;
job_to_labor_table[df::job_type::MakeGoblet] = jlf_make_object; job_to_labor_table[df::job_type::MakeGoblet] = jlf_make_object;
job_to_labor_table[df::job_type::MakeInstrument] = jlf_make_object;
job_to_labor_table[df::job_type::MakeToy] = jlf_make_object; job_to_labor_table[df::job_type::MakeToy] = jlf_make_object;
job_to_labor_table[df::job_type::MakeAnimalTrap] = jlf_const(df::unit_labor::TRAPPER); job_to_labor_table[df::job_type::MakeAnimalTrap] = jlf_const(df::unit_labor::TRAPPER);
job_to_labor_table[df::job_type::MakeBarrel] = jlf_make_furniture; job_to_labor_table[df::job_type::MakeBarrel] = jlf_make_furniture;

@ -25,9 +25,18 @@ void color_text_tile(const Screen::Pen &pen, int x, int y, bool map)
{ {
Screen::Pen pen2 = pen; Screen::Pen pen2 = pen;
uint8_t color = config.flicker ? config.tick % 8 : config.color; uint8_t color = config.flicker ? config.tick % 8 : config.color;
pen2.fg = color; if (map)
pen2.bg = color; {
pen2.bold = true; pen2.fg = color % 8;
pen2.bg = (color % 8) + 8;
pen2.bold = false;
}
else
{
pen2.fg = color;
pen2.bg = color;
pen2.bold = true;
}
return color_text_hook.next()(pen2, x, y, map); return color_text_hook.next()(pen2, x, y, map);
} }

@ -252,10 +252,11 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df:
prod->count = 1; prod->count = 1;
prod->product_dimension = 1; prod->product_dimension = 1;
vector<df::reaction_product*> out_products;
vector<df::item*> out_items; vector<df::item*> out_items;
vector<df::reaction_reagent*> in_reag; vector<df::reaction_reagent*> in_reag;
vector<df::item*> in_items; vector<df::item*> in_items;
prod->produce(firstInvader, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE, prod->produce(firstInvader, &out_products, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE,
df::historical_entity::find(firstInvader->civ_id), df::historical_entity::find(firstInvader->civ_id),
df::world_site::find(df::global::ui->site_id)); df::world_site::find(df::global::ui->site_id));

@ -706,7 +706,6 @@ public:
case job_type::MakeChain: case job_type::MakeChain:
case job_type::MakeFlask: case job_type::MakeFlask:
case job_type::MakeGoblet: case job_type::MakeGoblet:
case job_type::MakeInstrument:
case job_type::MakeToy: case job_type::MakeToy:
case job_type::MakeAnimalTrap: case job_type::MakeAnimalTrap:
case job_type::MakeBarrel: case job_type::MakeBarrel:

@ -309,7 +309,9 @@ struct product_hook : item_product {
DEFINE_VMETHOD_INTERPOSE( DEFINE_VMETHOD_INTERPOSE(
void, produce, void, produce,
(df::unit *unit, std::vector<df::item*> *out_items, (df::unit *unit,
std::vector<df::reaction_product*> *out_products,
std::vector<df::item*> *out_items,
std::vector<df::reaction_reagent*> *in_reag, std::vector<df::reaction_reagent*> *in_reag,
std::vector<df::item*> *in_items, std::vector<df::item*> *in_items,
int32_t quantity, df::job_skill skill, int32_t quantity, df::job_skill skill,
@ -318,7 +320,7 @@ struct product_hook : item_product {
color_ostream_proxy out(Core::getInstance().getConsole()); color_ostream_proxy out(Core::getInstance().getConsole());
auto product = products[this]; auto product = products[this];
if ( !product ) { if ( !product ) {
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site); INTERPOSE_NEXT(produce)(unit, out_products, out_items, in_reag, in_items, quantity, skill, entity, site);
return; return;
} }
df::reaction* this_reaction=product->react; df::reaction* this_reaction=product->react;
@ -329,7 +331,7 @@ struct product_hook : item_product {
return; return;
size_t out_item_count = out_items->size(); size_t out_item_count = out_items->size();
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site); INTERPOSE_NEXT(produce)(unit, out_products, out_items, in_reag, in_items, quantity, skill, entity, site);
if ( out_items->size() == out_item_count ) if ( out_items->size() == out_item_count )
return; return;
//if it produced something, call the scripts //if it produced something, call the scripts

@ -127,9 +127,28 @@ function collect_orders()
entry = entry, entry = entry,
} }
else else
-- It might be worth searching reaction_list for the name. -- Todo: Search reaction_list for the name.
-- Then again, this should only happen in unusual situations. -- This can happen when loading an old save in a new version.
print("Mismatched stockflow entry for stockpile #"..stockpile.stockpile_number..": "..entry.value.." ("..order_number..")") -- It's even possible that the reaction has been removed.
local found = false
for number, reaction in ipairs(reaction_list) do
if reaction.name == entry.value then
print("Adjusting stockflow entry for stockpile #"..stockpile.stockpile_number..": "..entry.value.." ("..order_number.." => "..number..")")
entry.ints[entry_ints.order_number] = number
entry:save()
result[spid] = {
stockpile = stockpile,
entry = entry,
}
found = true
break
end
end
if not found then
print("Unmatched stockflow entry for stockpile #"..stockpile.stockpile_number..": "..entry.value.." ("..order_number..")")
end
end end
else else
-- The stockpile no longer exists. -- The stockpile no longer exists.
@ -399,6 +418,14 @@ function collect_reactions()
reaction_entry(result, job_types.CustomReaction, {reaction_name = reaction.code}, name) reaction_entry(result, job_types.CustomReaction, {reaction_name = reaction.code}, name)
end end
-- Reactions generated by the game.
for _, reaction in ipairs(df.global.world.raws.reactions) do
if reaction.source_enid == entity.id then
local name = string.gsub(reaction.name, "^.", string.upper)
reaction_entry(result, job_types.CustomReaction, {reaction_name = reaction.code}, name)
end
end
-- Metal forging -- Metal forging
local itemdefs = df.global.world.raws.itemdefs local itemdefs = df.global.world.raws.itemdefs
for rock_id = 0, #rock_types - 1 do for rock_id = 0, #rock_types - 1 do
@ -465,19 +492,10 @@ function collect_reactions()
clothing_reactions(result, mat_flags, metalclothing) clothing_reactions(result, mat_flags, metalclothing)
end end
if material.flags.ITEMS_HARD then resource_reactions(result, job_types.MakeTool, mat_flags, entity.resources.tool_type, itemdefs.tools, {
resource_reactions(result, job_types.MakeTool, mat_flags, entity.resources.tool_type, itemdefs.tools, { permissible = (function(itemdef) return ((material.flags.ITEMS_HARD and itemdef.flags.HARD_MAT) or (material.flags.ITEMS_METAL and itemdef.flags.METAL_MAT)) and not itemdef.flags.NO_DEFAULT_JOB end),
permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), capitalize = true,
capitalize = true, })
})
end
if material.flags.ITEMS_METAL then
resource_reactions(result, job_types.MakeTool, mat_flags, entity.resources.tool_type, itemdefs.tools, {
permissible = (function(itemdef) return itemdef.flags.METAL_MAT end),
capitalize = true,
})
end
if material.flags.ITEMS_HARD then if material.flags.ITEMS_HARD then
material_reactions(result, { material_reactions(result, {
@ -560,7 +578,8 @@ function collect_reactions()
}, materials.wood) }, materials.wood)
resource_reactions(result, job_types.MakeTool, materials.wood, entity.resources.tool_type, itemdefs.tools, { resource_reactions(result, job_types.MakeTool, materials.wood, entity.resources.tool_type, itemdefs.tools, {
-- permissible = (function(itemdef) return itemdef.flags.WOOD_MAT end), -- permissible = (function(itemdef) return itemdef.flags.WOOD_MAT and not itemdef.flags.NO_DEFAULT_JOB end),
permissible = (function(itemdef) return not itemdef.flags.NO_DEFAULT_JOB end),
capitalize = true, capitalize = true,
}) })

@ -170,6 +170,8 @@ const SkillColumn columns[] = {
{9, 9, profession::POTTER, unit_labor::POTTERY, job_skill::POTTERY, "Po"}, {9, 9, profession::POTTER, unit_labor::POTTERY, job_skill::POTTERY, "Po"},
{9, 9, profession::GLAZER, unit_labor::GLAZING, job_skill::GLAZING, "Gl"}, {9, 9, profession::GLAZER, unit_labor::GLAZING, job_skill::GLAZING, "Gl"},
{9, 9, profession::WAX_WORKER, unit_labor::WAX_WORKING, job_skill::WAX_WORKING, "Wx"}, {9, 9, profession::WAX_WORKER, unit_labor::WAX_WORKING, job_skill::WAX_WORKING, "Wx"},
{9, 9, profession::PAPERMAKER, unit_labor::PAPERMAKING, job_skill::PAPERMAKING, "Pa"},
{9, 9, profession::BOOKBINDER, unit_labor::BOOKBINDING, job_skill::BOOKBINDING, "Bk"},
// Engineering // Engineering
{10, 12, profession::SIEGE_ENGINEER, unit_labor::SIEGECRAFT, job_skill::SIEGECRAFT, "En"}, {10, 12, profession::SIEGE_ENGINEER, unit_labor::SIEGECRAFT, job_skill::SIEGECRAFT, "En"},
{10, 12, profession::SIEGE_OPERATOR, unit_labor::SIEGEOPERATE, job_skill::SIEGEOPERATE, "Op"}, {10, 12, profession::SIEGE_OPERATOR, unit_labor::SIEGEOPERATE, job_skill::SIEGEOPERATE, "Op"},
@ -1592,7 +1594,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
if (enabler->mouse_lbut) if (enabler->mouse_lbut)
{ {
input_row = click_unit; input_row = click_unit;
events->insert(interface_key::UNITJOB_VIEW); events->insert(interface_key::UNITJOB_VIEW_UNIT);
} }
if (enabler->mouse_rbut) if (enabler->mouse_rbut)
{ {
@ -1804,7 +1806,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
if (VIRTUAL_CAST_VAR(unitlist, df::viewscreen_unitlistst, parent)) if (VIRTUAL_CAST_VAR(unitlist, df::viewscreen_unitlistst, parent))
{ {
if (events->count(interface_key::UNITJOB_VIEW) || events->count(interface_key::UNITJOB_ZOOM_CRE)) if (events->count(interface_key::UNITJOB_VIEW_UNIT) || events->count(interface_key::UNITJOB_ZOOM_CRE))
{ {
for (int i = 0; i < unitlist->units[unitlist->page].size(); i++) for (int i = 0; i < unitlist->units[unitlist->page].size(); i++)
{ {
@ -2057,7 +2059,7 @@ void viewscreen_unitlaborsst::render()
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SELECT_ALL)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SELECT_ALL));
OutputString(canToggle ? 15 : 8, x, y, ": Toggle Group, "); OutputString(canToggle ? 15 : 8, x, y, ": Toggle Group, ");
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::UNITJOB_VIEW)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::UNITJOB_VIEW_UNIT));
OutputString(15, x, y, ": ViewCre, "); OutputString(15, x, y, ": ViewCre, ");
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::UNITJOB_ZOOM_CRE)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::UNITJOB_ZOOM_CRE));

@ -674,7 +674,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest
} }
} }
OutputString(color, mx, my, "X"); OutputString(color, mx, my, "X", false, 0, 0, true);
return; return;
} }

@ -724,7 +724,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
for(int i=0;i<block->flows.size();i++) for(int i=0;i<block->flows.size();i++)
{ {
df::flow_info* f=block->flows[i]; df::flow_info* f=block->flows[i];
if(f && f->density>0 && f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire) if(f && f->density>0 && (f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire))
{ {
df::coord2d pos=f->pos; df::coord2d pos=f->pos;
pos=worldToViewportCoord(pos,vp,window2d); pos=worldToViewportCoord(pos,vp,window2d);

@ -21,6 +21,7 @@
#include "df/viewscreen_buildinglistst.h" #include "df/viewscreen_buildinglistst.h"
#include "df/viewscreen_joblistst.h" #include "df/viewscreen_joblistst.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/viewscreen_locationsst.h"
#include "df/interface_key.h" #include "df/interface_key.h"
#include "df/interfacest.h" #include "df/interfacest.h"
#include "df/layer_object_listst.h" #include "df/layer_object_listst.h"
@ -1948,6 +1949,47 @@ IMPLEMENT_HOOKS(df::viewscreen_topicmeeting_fill_land_holder_positionsst, noble_
// END: Noble suggestion search // END: Noble suggestion search
// //
//
// START: Location occupation assignment search
//
typedef search_generic<df::viewscreen_locationsst, df::unit*> location_assign_occupation_search_base;
class location_assign_occupation_search : public location_assign_occupation_search_base
{
public:
bool can_init (df::viewscreen_locationsst *screen)
{
return screen->menu == df::viewscreen_locationsst::AssignOccupation;
}
string get_element_description (df::unit *unit) const
{
return unit ? get_unit_description(unit) : "Nobody";
}
void render() const
{
print_search_option(37, gps->dimy - 3);
}
vector<df::unit*> *get_primary_list()
{
return &viewscreen->units;
}
virtual int32_t *get_viewscreen_cursor()
{
return &viewscreen->unit_idx;
}
};
IMPLEMENT_HOOKS(df::viewscreen_locationsst, location_assign_occupation_search);
//
// END: Location occupation assignment search
//
#define SEARCH_HOOKS \ #define SEARCH_HOOKS \
HOOK_ACTION(unitlist_search_hook) \ HOOK_ACTION(unitlist_search_hook) \
HOOK_ACTION(roomlist_search_hook) \ HOOK_ACTION(roomlist_search_hook) \
@ -1964,7 +2006,8 @@ IMPLEMENT_HOOKS(df::viewscreen_topicmeeting_fill_land_holder_positionsst, noble_
HOOK_ACTION(burrow_search_hook) \ HOOK_ACTION(burrow_search_hook) \
HOOK_ACTION(stockpile_search_hook) \ HOOK_ACTION(stockpile_search_hook) \
HOOK_ACTION(room_assign_search_hook) \ HOOK_ACTION(room_assign_search_hook) \
HOOK_ACTION(noble_suggest_search_hook) HOOK_ACTION(noble_suggest_search_hook) \
HOOK_ACTION(location_assign_occupation_search_hook)
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{ {

@ -26,6 +26,9 @@
#include "df/entity_raw.h" #include "df/entity_raw.h"
#include "df/builtin_mats.h" #include "df/builtin_mats.h"
#include "df/general_ref_unit_workerst.h" #include "df/general_ref_unit_workerst.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/caste_raw_flags.h"
using std::string; using std::string;
using std::vector; using std::vector;
@ -56,6 +59,8 @@ bool isUnitMoodable (df::unit *unit)
return false; return false;
if (!ENUM_ATTR(profession,moodable,unit->profession)) if (!ENUM_ATTR(profession,moodable,unit->profession))
return false; return false;
if (!Units::casteFlagSet(unit->race, unit->caste, caste_raw_flags::STRANGE_MOODS))
return false;
return true; return true;
} }
@ -104,11 +109,11 @@ df::job_skill getMoodSkill (df::unit *unit)
} }
if (!skills.size() && civ) if (!skills.size() && civ)
{ {
if (civ->entity_raw->jobs.permitted_skill[job_skill::WOODCRAFT]) if (civ->resources.permitted_skill[job_skill::WOODCRAFT])
skills.push_back(job_skill::WOODCRAFT); skills.push_back(job_skill::WOODCRAFT);
if (civ->entity_raw->jobs.permitted_skill[job_skill::STONECRAFT]) if (civ->resources.permitted_skill[job_skill::STONECRAFT])
skills.push_back(job_skill::STONECRAFT); skills.push_back(job_skill::STONECRAFT);
if (civ->entity_raw->jobs.permitted_skill[job_skill::BONECARVE]) if (civ->resources.permitted_skill[job_skill::BONECARVE])
skills.push_back(job_skill::BONECARVE); skills.push_back(job_skill::BONECARVE);
} }
if (!skills.size()) if (!skills.size())
@ -728,6 +733,8 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
case job_skill::WOODCRAFT: case job_skill::WOODCRAFT:
case job_skill::STONECRAFT: case job_skill::STONECRAFT:
case job_skill::BONECARVE: case job_skill::BONECARVE:
case job_skill::PAPERMAKING: // These aren't actually moodable skills
case job_skill::BOOKBINDING: // but the game still checks for them anyways
job->job_type = job_type::StrangeMoodCrafter; job->job_type = job_type::StrangeMoodCrafter;
break; break;
case job_skill::TANNER: case job_skill::TANNER:
@ -861,6 +868,8 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
case job_skill::CARPENTRY: case job_skill::CARPENTRY:
case job_skill::WOODCRAFT: case job_skill::WOODCRAFT:
case job_skill::BOWYER: case job_skill::BOWYER:
case job_skill::PAPERMAKING:
case job_skill::BOOKBINDING:
job->job_items.push_back(item = new df::job_item()); job->job_items.push_back(item = new df::job_item());
item->item_type = item_type::WOOD; item->item_type = item_type::WOOD;
item->quantity = base_item_count; item->quantity = base_item_count;
@ -960,6 +969,7 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
case job_skill::FORGE_WEAPON: case job_skill::FORGE_WEAPON:
case job_skill::FORGE_ARMOR: case job_skill::FORGE_ARMOR:
// there are actually 2 distinct cases here, but they're identical
case job_skill::FORGE_FURNITURE: case job_skill::FORGE_FURNITURE:
case job_skill::METALCRAFT: case job_skill::METALCRAFT:
filter = NULL; filter = NULL;
@ -990,7 +1000,7 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
item->item_type = item_type::BAR; item->item_type = item_type::BAR;
item->mat_type = filter->getMaterial(); item->mat_type = filter->getMaterial();
item->mat_index = filter->getMaterialIndex(); item->mat_index = filter->getMaterialIndex();
item->quantity = base_item_count * 150; item->quantity = base_item_count * 150; // BUGFIX - the game does not adjust here!
item->min_dimension = 150; item->min_dimension = 150;
} }
else else
@ -1012,7 +1022,7 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
} }
if (mats.size()) if (mats.size())
item->mat_index = mats[rng.df_trandom(mats.size())]; item->mat_index = mats[rng.df_trandom(mats.size())];
item->quantity = base_item_count * 150; item->quantity = base_item_count * 150; // BUGFIX - the game does not adjust here!
item->min_dimension = 150; item->min_dimension = 150;
} }
break; break;
@ -1264,12 +1274,12 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
item->quantity = 1; item->quantity = 1;
if (item_type == item_type::BAR) if (item_type == item_type::BAR)
{ {
item->quantity *= 150; item->quantity *= 150; // BUGFIX - the game does not adjust here!
item->min_dimension = 150; item->min_dimension = 150;
} }
if (item_type == item_type::CLOTH) if (item_type == item_type::CLOTH)
{ {
item->quantity *= 10000; item->quantity *= 10000; // BUGFIX - the game does not adjust here!
item->min_dimension = 10000; item->min_dimension = 10000;
} }
} }

@ -91,9 +91,9 @@ static void transform_(vector<T> &src, vector<V> &dst, Fn func)
typedef int8_t UIColor; typedef int8_t UIColor;
static void OutputString(UIColor color, int &x, int &y, const std::string &text, static void OutputString(UIColor color, int &x, int &y, const std::string &text,
bool newline = false, int left_margin = 0, const UIColor bg_color = 0) bool newline = false, int left_margin = 0, const UIColor bg_color = 0, bool map = false)
{ {
Screen::paintString(Screen::Pen(' ', color, bg_color), x, y, text); Screen::paintString(Screen::Pen(' ', color, bg_color), x, y, text, map);
if (newline) if (newline)
{ {
++y; ++y;
@ -104,54 +104,62 @@ static void OutputString(UIColor color, int &x, int &y, const std::string &text,
} }
static void OutputHotkeyString(int &x, int &y, const char *text, const char *hotkey, bool newline = false, static void OutputHotkeyString(int &x, int &y, const char *text, const char *hotkey, bool newline = false,
int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{ {
OutputString(hotkey_color, x, y, hotkey); OutputString(hotkey_color, x, y, hotkey, false, 0, 0, map);
string display(": "); string display(": ");
display.append(text); display.append(text);
OutputString(text_color, x, y, display, newline, left_margin); OutputString(text_color, x, y, display, newline, left_margin, 0, map);
} }
static void OutputHotkeyString(int &x, int &y, const char *text, df::interface_key hotkey, static void OutputHotkeyString(int &x, int &y, const char *text, df::interface_key hotkey,
bool newline = false, int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) bool newline = false, int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN,
bool map = false)
{ {
OutputHotkeyString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), newline, left_margin, text_color, hotkey_color); OutputHotkeyString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), newline, left_margin, text_color, hotkey_color, map);
} }
static void OutputLabelString(int &x, int &y, const char *text, const char *hotkey, const string &label, bool newline = false, static void OutputLabelString(int &x, int &y, const char *text, const char *hotkey, const string &label, bool newline = false,
int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{ {
OutputString(hotkey_color, x, y, hotkey); OutputString(hotkey_color, x, y, hotkey, false, 0, 0, map);
string display(": "); string display(": ");
display.append(text); display.append(text);
display.append(": "); display.append(": ");
OutputString(text_color, x, y, display); OutputString(text_color, x, y, display, false, 0, 0, map);
OutputString(hotkey_color, x, y, label, newline, left_margin); OutputString(hotkey_color, x, y, label, newline, left_margin, 0, map);
}
static void OutputLabelString(int &x, int &y, const char *text, df::interface_key hotkey, const string &label, bool newline = false,
int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{
OutputLabelString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), label, newline,
left_margin, text_color, hotkey_color, map);
} }
static void OutputFilterString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = false, static void OutputFilterString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = false,
int left_margin = 0, int8_t hotkey_color = COLOR_LIGHTGREEN) int left_margin = 0, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{ {
OutputString(hotkey_color, x, y, hotkey); OutputString(hotkey_color, x, y, hotkey, false, 0, 0, map);
OutputString(COLOR_WHITE, x, y, ": "); OutputString(COLOR_WHITE, x, y, ": ", false, 0, 0, map);
OutputString((state) ? COLOR_WHITE : COLOR_GREY, x, y, text, newline, left_margin); OutputString((state) ? COLOR_WHITE : COLOR_GREY, x, y, text, newline, left_margin, 0, map);
} }
static void OutputToggleString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = true, static void OutputToggleString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = true,
int left_margin = 0, int8_t color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) int left_margin = 0, int8_t color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{ {
OutputHotkeyString(x, y, text, hotkey, false, 0, color, hotkey_color); OutputHotkeyString(x, y, text, hotkey, false, 0, color, hotkey_color, map);
OutputString(color, x, y, ": "); OutputString(color, x, y, ": ", false, 0, 0, map);
if (state) if (state)
OutputString(COLOR_GREEN, x, y, "On", newline, left_margin); OutputString(COLOR_GREEN, x, y, "On", newline, left_margin, 0, map);
else else
OutputString(COLOR_GREY, x, y, "Off", newline, left_margin); OutputString(COLOR_GREY, x, y, "Off", newline, left_margin, 0, map);
} }
static void OutputToggleString(int &x, int &y, const char *text, df::interface_key hotkey, bool state, bool newline = true, static void OutputToggleString(int &x, int &y, const char *text, df::interface_key hotkey, bool state, bool newline = true,
int left_margin = 0, int8_t color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) int left_margin = 0, int8_t color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN, bool map = false)
{ {
OutputToggleString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), state, newline, left_margin, color, hotkey_color); OutputToggleString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), state, newline, left_margin, color, hotkey_color, map);
} }
inline string int_to_string(const int n) inline string int_to_string(const int n)

@ -1 +1 @@
Subproject commit 0b8303f6b03d574e3a0b3fd8b17b7ff0014af47f Subproject commit 01b594027a84c6b732f7639870538f138483a78c

@ -259,9 +259,19 @@ local function dwarfmode_to_top()
return true return true
end end
local function feed_menu_choice(catnames,catkeys,enum) local function feed_menu_choice(catnames,catkeys,enum,enter_seq,exit_seq,prompt)
local entered = false
return function (idx) return function (idx)
if idx == 0 and prompt and not utils.prompt_yes_no(' Proceed?', true) then
return false
end
idx = idx % #catnames + 1 idx = idx % #catnames + 1
if not entered then
entered = true
else
dwarfmode_feed_input(table.unpack(exit_seq or {}))
end
dwarfmode_feed_input(table.unpack(enter_seq or {}))
dwarfmode_feed_input(catkeys[idx]) dwarfmode_feed_input(catkeys[idx])
if enum then if enum then
return true, enum[catnames[idx]] return true, enum[catnames[idx]]
@ -1155,6 +1165,48 @@ NOTE: If not done after first 3-4 steps, resize the game window.]],
ms.found_offset('ui_building_in_resize', addr) ms.found_offset('ui_building_in_resize', addr)
end end
--
-- ui_lever_target_type
--
local function find_ui_lever_target_type()
local catnames = {
'Bridge', 'Door', 'Floodgate',
'Cage', 'Chain', 'TrackStop',
'GearAssembly',
}
local catkeys = {
'HOTKEY_TRAP_BRIDGE', 'HOTKEY_TRAP_DOOR', 'HOTKEY_TRAP_FLOODGATE',
'HOTKEY_TRAP_CAGE', 'HOTKEY_TRAP_CHAIN', 'HOTKEY_TRAP_TRACK_STOP',
'HOTKEY_TRAP_GEAR_ASSEMBLY',
}
local addr
if dwarfmode_to_top() then
dwarfmode_feed_input('D_BUILDJOB')
addr = searcher:find_interactive(
'Auto-searching for ui_lever_target_type. Please select a lever:',
'int8_t',
feed_menu_choice(catnames, catkeys, df.lever_target_type,
{'BUILDJOB_ADD'},
{'LEAVESCREEN', 'LEAVESCREEN'},
true -- prompt
),
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_lever_target_type. Please select a lever with
'q' and enter the "add task" menu with 'a':]],
'int8_t', catnames, df.lever_target_type
)
end
ms.found_offset('ui_lever_target_type', addr)
end
-- --
-- window_x -- window_x
-- --
@ -1327,7 +1379,7 @@ end
local function find_cur_year_tick() local function find_cur_year_tick()
local zone local zone
if os_type == 'windows' then if os_type == 'windows' then
zone = zoomed_searcher('artifact_next_id', -32) zone = zoomed_searcher('ui_unit_view_mode', 0x200)
else else
zone = zoomed_searcher('cur_year', 128) zone = zoomed_searcher('cur_year', 128)
end end
@ -1568,6 +1620,7 @@ exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor') exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
exec_finder(find_ui_building_in_assign, 'ui_building_in_assign') exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
exec_finder(find_ui_building_in_resize, 'ui_building_in_resize') exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
exec_finder(find_ui_lever_target_type, 'ui_lever_target_type')
exec_finder(find_window_x, 'window_x') exec_finder(find_window_x, 'window_x')
exec_finder(find_window_y, 'window_y') exec_finder(find_window_y, 'window_y')
exec_finder(find_window_z, 'window_z') exec_finder(find_window_z, 'window_z')

@ -0,0 +1,49 @@
-- Shows the warning about missing configuration file.
--[[=begin
gui/prerelease-warning
======================
Shows a warning on world load for pre-release builds.
=end]]
if not dfhack.isPrerelease() then qerror('not a prerelease build') end
local gui = require 'gui'
local dlg = require 'gui.dialogs'
local utils = require 'utils'
local message = {
'This is a prerelease build of DFHack. Some structures are likely', NEWLINE,
'to be incorrect, resulting in crashes or save corruption', NEWLINE,
{pen=COLOR_LIGHTRED, text='Make backups of your saves and avoid saving if possible.'}, NEWLINE,
}
path = dfhack.getHackPath():lower()
if path:find('lnp') or path:find('starter') or path:find('newb') or path:find('lazy') or path:find('pack') then
local pack_msg = [[
Under no circumstances should this be enabled by default in a pack.
If you are seeing this message and did not enable DFHack yourself,
please report this to your pack's maintainer.]]
for _, v in pairs(utils.split_string(pack_msg, '\n')) do
table.insert(message, NEWLINE)
table.insert(message, {text=v, pen=COLOR_LIGHTMAGENTA})
end
end
dfhack.print('\n')
for k,v in ipairs(message) do
if type(v) == 'table' then
dfhack.color(v.pen)
dfhack.print(v.text)
else
dfhack.color(COLOR_YELLOW)
dfhack.print(v)
end
end
dfhack.color(COLOR_RESET)
dfhack.print('\n\n')
dlg.showMessage('Warning', message, COLOR_YELLOW)

@ -15,5 +15,6 @@ nr = $script_args[0].to_i
raise 'too low' if nr < 7 raise 'too low' if nr < 7
addr = df.get_global_address('start_dwarf_count') addr = df.get_global_address('start_dwarf_count')
raise 'patch address not available' if addr == 0
df.memory_patch(addr, [nr].pack('L')) df.memory_patch(addr, [nr].pack('L'))