Merge branch 'master' into autolabor

develop
Kelly Martin 2012-10-21 17:25:14 -05:00
commit 59ece3d4f1
165 changed files with 6715 additions and 1297 deletions

3
.gitignore vendored

@ -36,6 +36,9 @@ build/tools
build/plugins build/plugins
build/depends build/depends
build/install_manifest.txt build/install_manifest.txt
build/README.html
build/LUA_API.html
build/COMPILE.html
#ignore Kdevelop stuff #ignore Kdevelop stuff
.kdev4 .kdev4

@ -0,0 +1,3 @@
FIND_PROGRAM(RST2HTML_EXECUTABLE NAMES rst2html rst2html.py)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Docutils DEFAULT_MSG RST2HTML_EXECUTABLE)

@ -63,7 +63,7 @@ set(DF_VERSION_MINOR "34")
set(DF_VERSION_PATCH "11") set(DF_VERSION_PATCH "11")
set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.") SET(DFHACK_RELEASE "r2" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
@ -145,12 +145,33 @@ include_directories(depends/clsocket/src)
add_subdirectory(depends) add_subdirectory(depends)
find_package(Docutils)
#set (RST_FILES
#"Readme"
#"Compile"
#"LUA Api"
#"Contributors"
#)
#set (RST_PROCESSED_FILES "")
#IF(RST2HTML_EXECUTABLE)
# foreach(F ${RST_FILES})
# add_custom_command(
# OUTPUT "${dfhack_BINARY_DIR}/${F}.html"
# COMMAND ${RST2HTML_EXECUTABLE} "${dfhack_SOURCE_DIR}/${F}.rst" "${dfhack_BINARY_DIR}/${F}.html"
# COMMENT "Translating ${F} to html"
# DEPENDS "${dfhack_SOURCE_DIR}/${F}.rst")
# list (APPEND RST_PROCESSED_FILES "${dfhack_BINARY_DIR}/${F}.html")
# endforeach()
# add_custom_target(HTML_DOCS ALL DEPENDS ${RST_PROCESSED_FILES})
#ENDIF()
# build the lib itself # build the lib itself
IF(BUILD_LIBRARY) IF(BUILD_LIBRARY)
add_subdirectory (library) add_subdirectory (library)
## install the default documentation files ## install the default documentation files
install(FILES LICENSE Readme.html Compile.html Lua\ API.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif() endif()
#build the plugins #build the plugins

@ -3,13 +3,13 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>Building DFHACK</title> <title>Building DFHACK</title>
<style type="text/css"> <style type="text/css">
/* /*
:Author: David Goodger (goodger@python.org) :Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7056 2011-06-17 10:50:48Z milde $ :Id: $Id: html4css1.css 7434 2012-05-11 21:06:27Z milde $
:Copyright: This stylesheet has been placed in the public domain. :Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils. Default cascading style sheet for the HTML output of Docutils.
@ -249,10 +249,18 @@ pre.address {
margin-top: 0 ; margin-top: 0 ;
font: inherit } font: inherit }
pre.literal-block, pre.doctest-block, pre.math { pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ; margin-left: 2em ;
margin-right: 2em } margin-right: 2em }
pre.code .ln { /* line numbers */
color: grey;
}
.code {
background-color: #eeeeee
}
span.classifier { span.classifier {
font-family: sans-serif ; font-family: sans-serif ;
font-style: oblique } font-style: oblique }
@ -326,20 +334,21 @@ ul.auto-toc {
<li><a class="reference internal" href="#build" id="id7">Build</a></li> <li><a class="reference internal" href="#build" id="id7">Build</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#windows" id="id8">Windows</a><ul> <li><a class="reference internal" href="#mac-os-x" id="id8">Mac OS X</a></li>
<li><a class="reference internal" href="#id1" id="id9">How to get the code</a></li> <li><a class="reference internal" href="#windows" id="id9">Windows</a><ul>
<li><a class="reference internal" href="#id2" id="id10">Dependencies</a></li> <li><a class="reference internal" href="#id1" id="id10">How to get the code</a></li>
<li><a class="reference internal" href="#id3" id="id11">Build</a></li> <li><a class="reference internal" href="#id2" id="id11">Dependencies</a></li>
<li><a class="reference internal" href="#id3" id="id12">Build</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#build-types" id="id12">Build types</a></li> <li><a class="reference internal" href="#build-types" id="id13">Build types</a></li>
<li><a class="reference internal" href="#using-the-library-as-a-developer" id="id13">Using the library as a developer</a><ul> <li><a class="reference internal" href="#using-the-library-as-a-developer" id="id14">Using the library as a developer</a><ul>
<li><a class="reference internal" href="#df-data-structure-definitions" id="id14">DF data structure definitions</a></li> <li><a class="reference internal" href="#df-data-structure-definitions" id="id15">DF data structure definitions</a></li>
<li><a class="reference internal" href="#remote-access-interface" id="id15">Remote access interface</a></li> <li><a class="reference internal" href="#remote-access-interface" id="id16">Remote access interface</a></li>
<li><a class="reference internal" href="#contributing-to-dfhack" id="id16">Contributing to DFHack</a><ul> <li><a class="reference internal" href="#contributing-to-dfhack" id="id17">Contributing to DFHack</a><ul>
<li><a class="reference internal" href="#coding-style" id="id17">Coding style</a></li> <li><a class="reference internal" href="#coding-style" id="id18">Coding style</a></li>
<li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id18">How to get new code into DFHack</a></li> <li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id19">How to get new code into DFHack</a></li>
<li><a class="reference internal" href="#memory-research" id="id19">Memory research</a></li> <li><a class="reference internal" href="#memory-research" id="id20">Memory research</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -395,11 +404,66 @@ extra options.</p>
program.</p> program.</p>
</div> </div>
</div> </div>
<div class="section" id="mac-os-x">
<h1><a class="toc-backref" href="#id8">Mac OS X</a></h1>
<ol class="arabic">
<li><p class="first">Download and unpack a copy of the latest DF</p>
</li>
<li><p class="first">Install Xcode from Mac App Store</p>
</li>
<li><p class="first">Open Xcode, go to Preferences &gt; Downloads, and install the Command Line Tools.</p>
</li>
<li><p class="first">Install MacPorts.</p>
</li>
<li><p class="first">Install dependencies from MacPorts:</p>
<ul>
<li><p class="first"><tt class="docutils literal">sudo port install gcc45 +universal cmake +universal <span class="pre">git-core</span> +universal</tt></p>
<p>This will take some time—maybe hours, depending on your machine.</p>
</li>
<li><p class="first">At some point during this process, it may ask you to install a Java environment; let it do so.</p>
</li>
</ul>
</li>
<li><p class="first">Install perl dependencies</p>
<blockquote>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">sudo cpan</tt></p>
<p>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.</p>
</li>
<li><p class="first"><tt class="docutils literal">install <span class="pre">XML::LibXML</span></tt></p>
</li>
<li><p class="first"><tt class="docutils literal">install <span class="pre">XML::LibXSLT</span></tt></p>
</li>
</ol>
</blockquote>
</li>
<li><p class="first">Get the dfhack source:</p>
<pre class="literal-block">
git clone https://github.com/danaris/dfhack.git
cd dfhack
git submodule init
git submodule update
</pre>
</li>
<li><p class="first">Build dfhack:</p>
<pre class="literal-block">
mkdir build-osx
cd build-osx
export CC=/opt/local/bin/gcc-mp-4.5
export CXX=/opt/local/bin/g++-mp-4.5
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory
make
make install
</pre>
</li>
</ol>
</div>
<div class="section" id="windows"> <div class="section" id="windows">
<h1><a class="toc-backref" href="#id8">Windows</a></h1> <h1><a class="toc-backref" href="#id9">Windows</a></h1>
<p>On Windows, DFHack replaces the SDL library distributed with DF.</p> <p>On Windows, DFHack replaces the SDL library distributed with DF.</p>
<div class="section" id="id1"> <div class="section" id="id1">
<h2><a class="toc-backref" href="#id9">How to get the code</a></h2> <h2><a class="toc-backref" href="#id10">How to get the code</a></h2>
<p>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. <p>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.
You will need some sort of Windows port of git, or a GUI. Some examples:</p> You will need some sort of Windows port of git, or a GUI. Some examples:</p>
<blockquote> <blockquote>
@ -420,7 +484,7 @@ git submodule update
<p>If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).</p> <p>If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).</p>
</div> </div>
<div class="section" id="id2"> <div class="section" id="id2">
<h2><a class="toc-backref" href="#id10">Dependencies</a></h2> <h2><a class="toc-backref" href="#id11">Dependencies</a></h2>
<p>First, you need <tt class="docutils literal">cmake</tt>. Get the win32 installer version from the official <p>First, you need <tt class="docutils literal">cmake</tt>. Get the win32 installer version from the official
site: <a class="reference external" href="http://www.cmake.org/cmake/resources/software.html">http://www.cmake.org/cmake/resources/software.html</a></p> site: <a class="reference external" href="http://www.cmake.org/cmake/resources/software.html">http://www.cmake.org/cmake/resources/software.html</a></p>
<p>It has the usual installer wizard. Make sure you let it add its binary folder <p>It has the usual installer wizard. Make sure you let it add its binary folder
@ -437,7 +501,7 @@ Grab it from Microsoft's site.</p>
<p>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 libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p> <p>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 libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p>
</div> </div>
<div class="section" id="id3"> <div class="section" id="id3">
<h2><a class="toc-backref" href="#id11">Build</a></h2> <h2><a class="toc-backref" href="#id12">Build</a></h2>
<p>There are several different batch files in the <tt class="docutils literal">build</tt> folder along with a script that's used for picking the DF path.</p> <p>There are several different batch files in the <tt class="docutils literal">build</tt> folder along with a script that's used for picking the DF path.</p>
<p>First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development. <p>First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development.
Next, run one of the scripts with <tt class="docutils literal">generate</tt> prefix. These create the MSVC solution file(s):</p> Next, run one of the scripts with <tt class="docutils literal">generate</tt> prefix. These create the MSVC solution file(s):</p>
@ -459,7 +523,7 @@ So pick either Release or RelWithDebInfo build and build the INSTALL target.</p>
</div> </div>
</div> </div>
<div class="section" id="build-types"> <div class="section" id="build-types">
<h1><a class="toc-backref" href="#id12">Build types</a></h1> <h1><a class="toc-backref" href="#id13">Build types</a></h1>
<p><tt class="docutils literal">cmake</tt> allows you to pick a build type by changing this <p><tt class="docutils literal">cmake</tt> allows you to pick a build type by changing this
variable: <tt class="docutils literal">CMAKE_BUILD_TYPE</tt></p> variable: <tt class="docutils literal">CMAKE_BUILD_TYPE</tt></p>
<pre class="literal-block"> <pre class="literal-block">
@ -471,7 +535,7 @@ cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
'RelWithDebInfo'. 'Debug' is not available on Windows.</p> 'RelWithDebInfo'. 'Debug' is not available on Windows.</p>
</div> </div>
<div class="section" id="using-the-library-as-a-developer"> <div class="section" id="using-the-library-as-a-developer">
<h1><a class="toc-backref" href="#id13">Using the library as a developer</a></h1> <h1><a class="toc-backref" href="#id14">Using the library as a developer</a></h1>
<p>Currently, the most direct way to use the library is to write a plugin that can be loaded by it. <p>Currently, the most direct way to use the library is to write a plugin that can be loaded by it.
All the plugins can be found in the 'plugins' folder. There's no in-depth documentation All the plugins can be found in the 'plugins' folder. There's no in-depth documentation
on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p> on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p>
@ -489,29 +553,29 @@ The main license is zlib/libpng, some bits are MIT licensed, and some are BSD li
<p>Feel free to add your own extensions and plugins. Contributing back to <p>Feel free to add your own extensions and plugins. Contributing back to
the dfhack repository is welcome and the right thing to do :)</p> the dfhack repository is welcome and the right thing to do :)</p>
<div class="section" id="df-data-structure-definitions"> <div class="section" id="df-data-structure-definitions">
<h2><a class="toc-backref" href="#id14">DF data structure definitions</a></h2> <h2><a class="toc-backref" href="#id15">DF data structure definitions</a></h2>
<p>DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.</p> <p>DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.</p>
<p>Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.</p> <p>Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.</p>
<p>Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.</p> <p>Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.</p>
</div> </div>
<div class="section" id="remote-access-interface"> <div class="section" id="remote-access-interface">
<h2><a class="toc-backref" href="#id15">Remote access interface</a></h2> <h2><a class="toc-backref" href="#id16">Remote access interface</a></h2>
<p>DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The <tt class="docutils literal"><span class="pre">dfhack-run</span></tt> command uses this interface to invoke ordinary console commands.</p> <p>DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The <tt class="docutils literal"><span class="pre">dfhack-run</span></tt> command uses this interface to invoke ordinary console commands.</p>
<p>Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.</p> <p>Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.</p>
<p>Protocol client implementations exist for Java and C#.</p> <p>Protocol client implementations exist for Java and C#.</p>
</div> </div>
<div class="section" id="contributing-to-dfhack"> <div class="section" id="contributing-to-dfhack">
<h2><a class="toc-backref" href="#id16">Contributing to DFHack</a></h2> <h2><a class="toc-backref" href="#id17">Contributing to DFHack</a></h2>
<p>Several things should be kept in mind when contributing to DFHack.</p> <p>Several things should be kept in mind when contributing to DFHack.</p>
<div class="section" id="coding-style"> <div class="section" id="coding-style">
<h3><a class="toc-backref" href="#id17">Coding style</a></h3> <h3><a class="toc-backref" href="#id18">Coding style</a></h3>
<p>DFhack uses ANSI formatting and four spaces as indentation. Line <p>DFhack uses ANSI formatting and four spaces as indentation. Line
endings are UNIX. The files use UTF-8 encoding. Code not following this endings are UNIX. The files use UTF-8 encoding. Code not following this
won't make me happy, because I'll have to fix it. There's a good chance won't make me happy, because I'll have to fix it. There's a good chance
I'll make <em>you</em> fix it ;)</p> I'll make <em>you</em> fix it ;)</p>
</div> </div>
<div class="section" id="how-to-get-new-code-into-dfhack"> <div class="section" id="how-to-get-new-code-into-dfhack">
<h3><a class="toc-backref" href="#id18">How to get new code into DFHack</a></h3> <h3><a class="toc-backref" href="#id19">How to get new code into DFHack</a></h3>
<p>You can send patches or make a clone of the github repo and ask me on <p>You can send patches or make a clone of the github repo and ask me on
the IRC channel to pull your code in. I'll review it and see if there the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.</p> are any problems. I'll fix them if they are minor.</p>
@ -521,7 +585,7 @@ this is also a good place to dump new ideas and/or bugs that need
fixing.</p> fixing.</p>
</div> </div>
<div class="section" id="memory-research"> <div class="section" id="memory-research">
<h3><a class="toc-backref" href="#id19">Memory research</a></h3> <h3><a class="toc-backref" href="#id20">Memory research</a></h3>
<p>If you want to do memory research, you'll need some tools and some knowledge. <p>If you want to do memory research, you'll need some tools and some knowledge.
In general, you'll need a good memory viewer and optionally something In general, you'll need a good memory viewer and optionally something
to look at machine code without getting crazy :)</p> to look at machine code without getting crazy :)</p>

@ -63,6 +63,49 @@ extra options.
You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui
program. program.
========
Mac OS X
========
1. Download and unpack a copy of the latest DF
2. Install Xcode from Mac App Store
3. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools.
4. Install MacPorts.
5. Install dependencies from MacPorts:
* ``sudo port install gcc45 +universal cmake +universal git-core +universal``
This will take some time—maybe hours, depending on your machine.
* At some point during this process, it may ask you to install a Java environment; let it do so.
6. Install perl dependencies
1. ``sudo cpan``
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``
3. ``install XML::LibXSLT``
7. Get the dfhack source::
git clone https://github.com/danaris/dfhack.git
cd dfhack
git submodule init
git submodule update
8. Build dfhack::
mkdir build-osx
cd build-osx
export CC=/opt/local/bin/gcc-mp-4.5
export CXX=/opt/local/bin/g++-mp-4.5
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory
make
make install
======= =======
Windows Windows
======= =======

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>Contributors</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7434 2012-05-11 21:06:27Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { /* line numbers */
color: grey;
}
.code {
background-color: #eeeeee
}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="contributors">
<h1 class="title">Contributors</h1>
<p>If you belong here and are missing, please add yourself and send me (peterix) a pull request :-)</p>
<p>The following is a list of people who have contributed to <strong>DFHack</strong>.</p>
<ul class="simple">
<li>Petr Mrázek &lt;<a class="reference external" href="mailto:peterix&#64;gmail.com">peterix&#64;gmail.com</a>&gt;</li>
<li>Alexander Gavrilov &lt;<a class="reference external" href="mailto:angavrilov&#64;gmail.com">angavrilov&#64;gmail.com</a>&gt;</li>
<li>doomchild &lt;<a class="reference external" href="mailto:lee.crabtree&#64;gmail.com">lee.crabtree&#64;gmail.com</a>&gt;</li>
<li>Quietust &lt;<a class="reference external" href="mailto:quietust&#64;gmail.com">quietust&#64;gmail.com</a>&gt;</li>
<li>jj &lt;<a class="reference external" href="mailto:john-git&#64;ofjj.net">john-git&#64;ofjj.net</a>&gt;</li>
<li>Warmist &lt;<a class="reference external" href="mailto:warmist&#64;gmail.com">warmist&#64;gmail.com</a>&gt;</li>
<li>Robert Heinrich &lt;<a class="reference external" href="mailto:robertheinrich73&#64;googlemail.com">robertheinrich73&#64;googlemail.com</a>&gt;</li>
<li>simon &lt;<a class="reference external" href="mailto:simon&#64;banquise.net">simon&#64;banquise.net</a>&gt;</li>
<li>Kelly Martin &lt;<a class="reference external" href="mailto:kelly.lynn.martin&#64;gmail.com">kelly.lynn.martin&#64;gmail.com</a>&gt;</li>
<li>mizipzor &lt;<a class="reference external" href="mailto:mizipzor&#64;gmail.com">mizipzor&#64;gmail.com</a>&gt;</li>
<li>Simon Jackson &lt;<a class="reference external" href="mailto:sizeak&#64;hotmail.com">sizeak&#64;hotmail.com</a>&gt;</li>
<li>belal &lt;<a class="reference external" href="mailto:jimbelal&#64;gmail.com">jimbelal&#64;gmail.com</a>&gt;</li>
<li>RusAnon &lt;<a class="reference external" href="mailto:rusanon&#64;dollchan.ru">rusanon&#64;dollchan.ru</a>&gt;</li>
<li>Raoul XQ &lt;<a class="reference external" href="mailto:raoulxq&#64;gmail.com">raoulxq&#64;gmail.com</a>&gt;</li>
<li>Matthew Cline &lt;<a class="reference external" href="mailto:zelgadis&#64;sourceforge.net">zelgadis&#64;sourceforge.net</a>&gt;</li>
<li>Mike Stewart &lt;<a class="reference external" href="mailto:thewonderidiot&#64;gmail.com">thewonderidiot&#64;gmail.com</a>&gt;</li>
<li>Timothy Collett &lt;<a class="reference external" href="mailto:tcollett+github&#64;topazgryphon.org">tcollett+github&#64;topazgryphon.org</a>&gt;</li>
<li>RossM &lt;<a class="reference external" href="mailto:Ross&#64;Gnome">Ross&#64;Gnome</a>&gt;</li>
<li>Tom Prince &lt;<a class="reference external" href="mailto:tom.prince&#64;ualberta.net">tom.prince&#64;ualberta.net</a>&gt;</li>
<li>Jared Adams &lt;<a class="reference external" href="mailto:jaxad0127&#64;gmail.com">jaxad0127&#64;gmail.com</a>&gt;</li>
<li>expwnent &lt;<a class="reference external" href="mailto:q309185&#64;gmail.com">q309185&#64;gmail.com</a>&gt;</li>
<li>Erik Youngren &lt;<a class="reference external" href="mailto:artanis.00&#64;gmail.com">artanis.00&#64;gmail.com</a>&gt;</li>
<li>Espen Wiborg &lt;<a class="reference external" href="mailto:espen.wiborg&#64;telio.no">espen.wiborg&#64;telio.no</a>&gt;</li>
<li>Tim Walberg &lt;<a class="reference external" href="mailto:twalberg&#64;comcast.net">twalberg&#64;comcast.net</a>&gt;</li>
<li>Mikko Juola &lt;<a class="reference external" href="mailto:mikko.juola&#64;kolumbus.fi">mikko.juola&#64;kolumbus.fi</a>&gt;</li>
<li>rampaging-poet &lt;<a class="reference external" href="mailto:yrudoingthis&#64;hotmail.com">yrudoingthis&#64;hotmail.com</a>&gt;</li>
<li>U-glouglou\simon</li>
<li>Clayton Hughes &lt;<a class="reference external" href="mailto:clayton.hughes&#64;gmail.com">clayton.hughes&#64;gmail.com</a>&gt;</li>
<li>zilpin &lt;<a class="reference external" href="mailto:ziLpin&#64;gmail.com">ziLpin&#64;gmail.com</a>&gt;</li>
<li>Will Rogers &lt;<a class="reference external" href="mailto:wjrogers&#64;gmail.com">wjrogers&#64;gmail.com</a>&gt;</li>
<li>NMLittle &lt;<a class="reference external" href="mailto:nmlittle&#64;gmail.com">nmlittle&#64;gmail.com</a>&gt;</li>
<li>root</li>
<li>reverb</li>
<li>Zhentar &lt;<a class="reference external" href="mailto:Zhentar&#64;gmail.com">Zhentar&#64;gmail.com</a>&gt;</li>
<li>Valentin Ochs &lt;<a class="reference external" href="mailto:a&#64;0au.de">a&#64;0au.de</a>&gt;</li>
<li>Priit Laes &lt;<a class="reference external" href="mailto:plaes&#64;plaes.org">plaes&#64;plaes.org</a>&gt;</li>
<li>kmartin</li>
<li>Neil Little</li>
<li>rout &lt;<a class="reference external" href="mailto:rout.mail+github&#64;gmail.com">rout.mail+github&#64;gmail.com</a>&gt;</li>
<li>rofl0r &lt;<a class="reference external" href="mailto:retnyg&#64;gmx.net">retnyg&#64;gmx.net</a>&gt;</li>
<li>harlanplayford &lt;<a class="reference external" href="mailto:harlanplayford&#64;gmail.com">harlanplayford&#64;gmail.com</a>&gt;</li>
<li>gsvslto &lt;<a class="reference external" href="mailto:gsvslto&#64;gmail.com">gsvslto&#64;gmail.com</a>&gt;</li>
<li>sami</li>
<li>potato</li>
<li>playfordh &lt;<a class="reference external" href="mailto:harlanplayford&#64;gmail.com">harlanplayford&#64;gmail.com</a>&gt;</li>
<li>feng1st &lt;<a class="reference external" href="mailto:nf_xp&#64;hotmail.com">nf_xp&#64;hotmail.com</a>&gt;</li>
<li>comestible &lt;<a class="reference external" href="mailto:nickolas.g.russell&#64;gmail.com">nickolas.g.russell&#64;gmail.com</a>&gt;</li>
<li>Rumrusher &lt;<a class="reference external" href="mailto:Anuleakage&#64;yahoo.com">Anuleakage&#64;yahoo.com</a>&gt;</li>
<li>Rinin &lt;<a class="reference external" href="mailto:RininS&#64;Gmail.com">RininS&#64;Gmail.com</a>&gt;</li>
<li>Raoul van Putten</li>
<li>John Shade &lt;<a class="reference external" href="mailto:gsvslto&#64;gmail.com">gsvslto&#64;gmail.com</a>&gt;</li>
<li>John Beisley &lt;<a class="reference external" href="mailto:greatred&#64;gmail.com">greatred&#64;gmail.com</a>&gt;</li>
<li>Feng &lt;<a class="reference external" href="mailto:nf_xp&#64;hotmail.com">nf_xp&#64;hotmail.com</a>&gt;</li>
<li>Donald Ruegsegger &lt;<a class="reference external" href="mailto:druegsegger&#64;gmail.com">druegsegger&#64;gmail.com</a>&gt;</li>
<li>Caldfir &lt;<a class="reference external" href="mailto:caldfir&#64;hotmail.com">caldfir&#64;hotmail.com</a>&gt;</li>
<li>Antalia &lt;<a class="reference external" href="mailto:tamarakorr&#64;gmail.com">tamarakorr&#64;gmail.com</a>&gt;</li>
<li>Angus Mezick &lt;<a class="reference external" href="mailto:amezick&#64;gmail.com">amezick&#64;gmail.com</a>&gt;</li>
</ul>
<p>And those are the cool people who made <strong>stonesense</strong>.</p>
<ul class="simple">
<li>Kris Parker &lt;kaypy&gt;</li>
<li>Japa &lt;<a class="reference external" href="mailto:japa.mala.illo&#64;gmail.com">japa.mala.illo&#64;gmail.com</a>&gt;</li>
<li>Jonas Ask &lt;<a class="reference external" href="mailto:jonask84&#64;gmail.com">jonask84&#64;gmail.com</a>&gt;</li>
<li>Petr Mrázek &lt;<a class="reference external" href="mailto:peterix&#64;gmail.com">peterix&#64;gmail.com</a>&gt;</li>
<li>Caldfir &lt;<a class="reference external" href="mailto:aitken.tim&#64;gmail.com">aitken.tim&#64;gmail.com</a>&gt;</li>
<li>8Z &lt;<a class="reference external" href="mailto:git8z&#64;ya.ru">git8z&#64;ya.ru</a>&gt;</li>
<li>Alexander Gavrilov &lt;<a class="reference external" href="mailto:angavrilov&#64;gmail.com">angavrilov&#64;gmail.com</a>&gt;</li>
<li>Timothy Collett &lt;<a class="reference external" href="mailto:tcollett+github&#64;topazgryphon.org">tcollett+github&#64;topazgryphon.org</a>&gt;</li>
</ul>
</div>
</body>
</html>

@ -0,0 +1,74 @@
Contributors
============
If you belong here and are missing, please add yourself and send me (peterix) a pull request :-)
The following is a list of people who have contributed to **DFHack**.
- Petr Mrázek <peterix@gmail.com>
- Alexander Gavrilov <angavrilov@gmail.com>
- doomchild <lee.crabtree@gmail.com>
- Quietust <quietust@gmail.com>
- jj <john-git@ofjj.net>
- Warmist <warmist@gmail.com>
- Robert Heinrich <robertheinrich73@googlemail.com>
- simon <simon@banquise.net>
- Kelly Martin <kelly.lynn.martin@gmail.com>
- mizipzor <mizipzor@gmail.com>
- Simon Jackson <sizeak@hotmail.com>
- belal <jimbelal@gmail.com>
- RusAnon <rusanon@dollchan.ru>
- Raoul XQ <raoulxq@gmail.com>
- Matthew Cline <zelgadis@sourceforge.net>
- Mike Stewart <thewonderidiot@gmail.com>
- Timothy Collett <tcollett+github@topazgryphon.org>
- RossM <Ross@Gnome>
- Tom Prince <tom.prince@ualberta.net>
- Jared Adams <jaxad0127@gmail.com>
- expwnent <q309185@gmail.com>
- Erik Youngren <artanis.00@gmail.com>
- Espen Wiborg <espen.wiborg@telio.no>
- Tim Walberg <twalberg@comcast.net>
- Mikko Juola <mikko.juola@kolumbus.fi>
- rampaging-poet <yrudoingthis@hotmail.com>
- U-glouglou\\simon
- Clayton Hughes <clayton.hughes@gmail.com>
- zilpin <ziLpin@gmail.com>
- Will Rogers <wjrogers@gmail.com>
- NMLittle <nmlittle@gmail.com>
- root
- reverb
- Zhentar <Zhentar@gmail.com>
- Valentin Ochs <a@0au.de>
- Priit Laes <plaes@plaes.org>
- kmartin
- Neil Little
- rout <rout.mail+github@gmail.com>
- rofl0r <retnyg@gmx.net>
- harlanplayford <harlanplayford@gmail.com>
- gsvslto <gsvslto@gmail.com>
- sami
- potato
- playfordh <harlanplayford@gmail.com>
- feng1st <nf_xp@hotmail.com>
- comestible <nickolas.g.russell@gmail.com>
- Rumrusher <Anuleakage@yahoo.com>
- Rinin <RininS@Gmail.com>
- Raoul van Putten
- John Shade <gsvslto@gmail.com>
- John Beisley <greatred@gmail.com>
- Feng <nf_xp@hotmail.com>
- Donald Ruegsegger <druegsegger@gmail.com>
- Caldfir <caldfir@hotmail.com>
- Antalia <tamarakorr@gmail.com>
- Angus Mezick <amezick@gmail.com>
And those are the cool people who made **stonesense**.
- Kris Parker <kaypy>
- Japa <japa.mala.illo@gmail.com>
- Jonas Ask <jonask84@gmail.com>
- Petr Mrázek <peterix@gmail.com>
- Caldfir <aitken.tim@gmail.com>
- 8Z <git8z@ya.ru>
- Alexander Gavrilov <angavrilov@gmail.com>
- Timothy Collett <tcollett+github@topazgryphon.org>

@ -2,7 +2,7 @@
License of dfhack License of dfhack
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -3,13 +3,13 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.8.1: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>DFHack Lua API</title> <title>DFHack Lua API</title>
<style type="text/css"> <style type="text/css">
/* /*
:Author: David Goodger (goodger@python.org) :Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7056 2011-06-17 10:50:48Z milde $ :Id: $Id: html4css1.css 7434 2012-05-11 21:06:27Z milde $
:Copyright: This stylesheet has been placed in the public domain. :Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils. Default cascading style sheet for the HTML output of Docutils.
@ -249,10 +249,18 @@ pre.address {
margin-top: 0 ; margin-top: 0 ;
font: inherit } font: inherit }
pre.literal-block, pre.doctest-block, pre.math { pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ; margin-left: 2em ;
margin-right: 2em } margin-right: 2em }
pre.code .ln { /* line numbers */
color: grey;
}
.code {
background-color: #eeeeee
}
span.classifier { span.classifier {
font-family: sans-serif ; font-family: sans-serif ;
font-style: oblique } font-style: oblique }
@ -781,7 +789,7 @@ running coroutine and let the C++ code release the core suspend
lock. Using an explicit <tt class="docutils literal">dfhack.with_suspend</tt> will prevent lock. Using an explicit <tt class="docutils literal">dfhack.with_suspend</tt> will prevent
this, forcing the function to block on input with lock held.</p> this, forcing the function to block on input with lock held.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.interpreter([prompt[,env[,history_filename]]])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.interpreter([prompt[,history_filename[,env]]])</span></tt></p>
<p>Starts an interactive lua interpreter, using the specified prompt <p>Starts an interactive lua interpreter, using the specified prompt
string, global environment and command-line history file.</p> string, global environment and command-line history file.</p>
<p>If the interactive console is not accessible, returns <em>nil, error</em>.</p> <p>If the interactive console is not accessible, returns <em>nil, error</em>.</p>
@ -924,6 +932,21 @@ Returns <em>entry, did_create_new</em></p>
and automatically stored in the save game, these save and retrieval and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O. functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p> However, currently every entry has a 180+-byte dead-weight overhead.</p>
<p>It is also possible to associate one bit per map tile with an entry,
using these two methods:</p>
<ul>
<li><p class="first"><tt class="docutils literal">entry:getTilemask(block[, create])</tt></p>
<p>Retrieves the tile bitmask associated with this entry in the given map
block. If <tt class="docutils literal">create</tt> is <em>true</em>, an empty mask is created if none exists;
otherwise the function returns <em>nil</em>, which must be assumed to be the same
as an all-zero mask.</p>
</li>
<li><p class="first"><tt class="docutils literal">entry:deleteTilemask(block)</tt></p>
<p>Deletes the associated tile mask from the given map block.</p>
</li>
</ul>
<p>Note that these masks are only saved in fortress mode, and also that deleting
the persistent entry will <strong>NOT</strong> delete the associated masks.</p>
</div> </div>
<div class="section" id="material-info-lookup"> <div class="section" id="material-info-lookup">
<h3><a class="toc-backref" href="#id17">Material info lookup</a></h3> <h3><a class="toc-backref" href="#id17">Material info lookup</a></h3>
@ -1087,6 +1110,13 @@ above operations accordingly. If enabled, pauses and zooms to position.</p>
if there are any jobs with <tt class="docutils literal">first_id &lt;= id &lt; job_next_id</tt>, if there are any jobs with <tt class="docutils literal">first_id &lt;= id &lt; job_next_id</tt>,
a lua list containing them.</p> a lua list containing them.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.isSuitableItem(job_item, item_type, item_subtype)</tt></p>
<p>Does basic sanity checks to verify if the suggested item type matches
the flags in the job item.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)</tt></p>
<p>Likewise, if replacing material.</p>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="units-module"> <div class="section" id="units-module">
@ -1150,6 +1180,10 @@ same checks the game uses to decide game-over by extinction.</p>
<p>Returns the age of the unit in years as a floating-point value. <p>Returns the age of the unit in years as a floating-point value.
If <tt class="docutils literal">true_age</tt> is true, ignores false identities.</p> If <tt class="docutils literal">true_age</tt> is true, ignores false identities.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getNominalSkill(unit, skill[, use_rust])</tt></p>
<p>Retrieves the nominal skill level for the given unit. If <tt class="docutils literal">use_rust</tt>
is <em>true</em>, subtracts the rust penalty.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getEffectiveSkill(unit, skill)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.units.getEffectiveSkill(unit, skill)</tt></p>
<p>Computes the effective rating for the given skill, taking into account exhaustion, pain etc.</p> <p>Computes the effective rating for the given skill, taking into account exhaustion, pain etc.</p>
</li> </li>
@ -1223,6 +1257,15 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.items.makeProjectile(item)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.items.makeProjectile(item)</tt></p>
<p>Turns the item into a projectile, and returns the new object, or <em>nil</em> if impossible.</p> <p>Turns the item into a projectile, and returns the new object, or <em>nil</em> if impossible.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.items.isCasteMaterial(item_type)</tt></p>
<p>Returns <em>true</em> if this item type uses a creature/caste pair as its material.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getSubtypeCount(item_type)</tt></p>
<p>Returns the number of raw-defined subtypes of the given item type, or <em>-1</em> if not applicable.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getSubtypeDef(item_type, subtype)</tt></p>
<p>Returns the raw definition for the given item type and subtype, or <em>nil</em> if invalid.</p>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="maps-module"> <div class="section" id="maps-module">
@ -1237,7 +1280,7 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getBlock(x,y,z)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getBlock(x,y,z)</tt></p>
<p>Returns a map block object for given x,y,z in local block coordinates.</p> <p>Returns a map block object for given x,y,z in local block coordinates.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.isValidTilePos(coords)</tt>, or isValidTilePos(x,y,z)``</p> <li><p class="first"><tt class="docutils literal">dfhack.maps.isValidTilePos(coords)</tt>, or <tt class="docutils literal">isValidTilePos(x,y,z)</tt></p>
<p>Checks if the given df::coord or x,y,z in local tile coordinates are valid.</p> <p>Checks if the given df::coord or x,y,z in local tile coordinates are valid.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileBlock(coords)</tt>, or <tt class="docutils literal">getTileBlock(x,y,z)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getTileBlock(coords)</tt>, or <tt class="docutils literal">getTileBlock(x,y,z)</tt></p>
@ -1246,6 +1289,12 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.maps.ensureTileBlock(coords)</tt>, or <tt class="docutils literal">ensureTileBlock(x,y,z)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.ensureTileBlock(coords)</tt>, or <tt class="docutils literal">ensureTileBlock(x,y,z)</tt></p>
<p>Like <tt class="docutils literal">getTileBlock</tt>, but if the block is not allocated, try creating it.</p> <p>Like <tt class="docutils literal">getTileBlock</tt>, but if the block is not allocated, try creating it.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileType(coords)</tt>, or <tt class="docutils literal">getTileType(x,y,z)</tt></p>
<p>Returns the tile type at the given coordinates, or <em>nil</em> if invalid.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileFlags(coords)</tt>, or <tt class="docutils literal">getTileFlags(x,y,z)</tt></p>
<p>Returns designation and occupancy references for the given coordinates, or <em>nil, nil</em> if invalid.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getRegionBiome(region_coord2d)</tt>, or <tt class="docutils literal">getRegionBiome(x,y)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getRegionBiome(region_coord2d)</tt>, or <tt class="docutils literal">getRegionBiome(x,y)</tt></p>
<p>Returns the biome info struct for the given global map region.</p> <p>Returns the biome info struct for the given global map region.</p>
</li> </li>
@ -1274,6 +1323,18 @@ tools like liquids or tiletypes are used. It also cannot possibly
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.</p> burrows, or the presence of invaders.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.hasTileAssignment(tilemask)</tt></p>
<p>Checks if the tile_bitmask object is not <em>nil</em> and contains any set bits; returns <em>true</em> or <em>false</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileAssignment(tilemask,x,y)</tt></p>
<p>Checks if the tile_bitmask object is not <em>nil</em> and has the relevant bit set; returns <em>true</em> or <em>false</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.setTileAssignment(tilemask,x,y,enable)</tt></p>
<p>Sets the relevant bit in the tile_bitmask object to the <em>enable</em> argument.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.maps.resetTileAssignment(tilemask[,enable])</span></tt></p>
<p>Sets all bits in the mask to the <em>enable</em> argument.</p>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="burrows-module"> <div class="section" id="burrows-module">
@ -1799,6 +1860,15 @@ coordinates), returns <em>nil</em>.</p>
<li><p class="first"><tt class="docutils literal">xyz2pos(x,y,z)</tt></p> <li><p class="first"><tt class="docutils literal">xyz2pos(x,y,z)</tt></p>
<p>Returns a table with x, y and z as fields.</p> <p>Returns a table with x, y and z as fields.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">same_xyz(a,b)</tt></p>
<p>Checks if <tt class="docutils literal">a</tt> and <tt class="docutils literal">b</tt> have the same x, y and z fields.</p>
</li>
<li><p class="first"><tt class="docutils literal">get_path_xyz(path,i)</tt></p>
<p>Returns <tt class="docutils literal">path.x[i], path.y[i], path.z[i]</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">pos2xy(obj)</tt>, <tt class="docutils literal">xy2pos(x,y)</tt>, <tt class="docutils literal">same_xy(a,b)</tt>, <tt class="docutils literal">get_path_xy(a,b)</tt></p>
<p>Same as above, but for 2D coordinates.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">safe_index(obj,index...)</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">safe_index(obj,index...)</span></tt></p>
<p>Walks a sequence of dereferences, which may be represented by numbers or strings. <p>Walks a sequence of dereferences, which may be represented by numbers or strings.
Returns <em>nil</em> if any of obj or indices is <em>nil</em>, or a numeric index is out of array bounds.</p> Returns <em>nil</em> if any of obj or indices is <em>nil</em>, or a numeric index is out of array bounds.</p>
@ -1902,6 +1972,12 @@ utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
</pre> </pre>
<p>(For an explanation of <tt class="docutils literal">new=true</tt>, see table assignment in the wrapper section)</p> <p>(For an explanation of <tt class="docutils literal">new=true</tt>, see table assignment in the wrapper section)</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">utils.erase_sorted_key(vector,key,field,cmpfun)</tt></p>
<p>Removes the item with the given key from the list. Returns: <em>did_erase, vector[idx], idx</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">utils.erase_sorted(vector,item,field,cmpfun)</tt></p>
<p>Exactly like <tt class="docutils literal">erase_sorted_key</tt>, but if field is specified, takes the key from <tt class="docutils literal">item[field]</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">utils.prompt_yes_no(prompt, default)</tt></p> <li><p class="first"><tt class="docutils literal">utils.prompt_yes_no(prompt, default)</tt></p>
<p>Presents a yes/no prompt to the user. If <tt class="docutils literal">default</tt> is not <em>nil</em>, <p>Presents a yes/no prompt to the user. If <tt class="docutils literal">default</tt> is not <em>nil</em>,
allows just pressing Enter to submit the default choice. allows just pressing Enter to submit the default choice.

@ -488,7 +488,7 @@ Input & Output
lock. Using an explicit ``dfhack.with_suspend`` will prevent lock. Using an explicit ``dfhack.with_suspend`` will prevent
this, forcing the function to block on input with lock held. this, forcing the function to block on input with lock held.
* ``dfhack.interpreter([prompt[,env[,history_filename]]])`` * ``dfhack.interpreter([prompt[,history_filename[,env]]])``
Starts an interactive lua interpreter, using the specified prompt Starts an interactive lua interpreter, using the specified prompt
string, global environment and command-line history file. string, global environment and command-line history file.
@ -646,6 +646,24 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O. functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead. 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,
using these two methods:
* ``entry:getTilemask(block[, create])``
Retrieves the tile bitmask associated with this entry in the given map
block. If ``create`` is *true*, an empty mask is created if none exists;
otherwise the function returns *nil*, which must be assumed to be the same
as an all-zero mask.
* ``entry:deleteTilemask(block)``
Deletes the associated tile mask from the given map block.
Note that these masks are only saved in fortress mode, and also that deleting
the persistent entry will **NOT** delete the associated masks.
Material info lookup Material info lookup
-------------------- --------------------
@ -845,6 +863,15 @@ Job module
if there are any jobs with ``first_id <= id < job_next_id``, if there are any jobs with ``first_id <= id < job_next_id``,
a lua list containing them. a lua list containing them.
* ``dfhack.job.isSuitableItem(job_item, item_type, item_subtype)``
Does basic sanity checks to verify if the suggested item type matches
the flags in the job item.
* ``dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)``
Likewise, if replacing material.
Units module Units module
------------ ------------
@ -918,6 +945,11 @@ Units module
Returns the age of the unit in years as a floating-point value. Returns the age of the unit in years as a floating-point value.
If ``true_age`` is true, ignores false identities. If ``true_age`` is true, ignores false identities.
* ``dfhack.units.getNominalSkill(unit, skill[, use_rust])``
Retrieves the nominal skill level for the given unit. If ``use_rust``
is *true*, subtracts the rust penalty.
* ``dfhack.units.getEffectiveSkill(unit, skill)`` * ``dfhack.units.getEffectiveSkill(unit, skill)``
Computes the effective rating for the given skill, taking into account exhaustion, pain etc. Computes the effective rating for the given skill, taking into account exhaustion, pain etc.
@ -1011,6 +1043,18 @@ Items module
Turns the item into a projectile, and returns the new object, or *nil* if impossible. Turns the item into a projectile, and returns the new object, or *nil* if impossible.
* ``dfhack.items.isCasteMaterial(item_type)``
Returns *true* if this item type uses a creature/caste pair as its material.
* ``dfhack.items.getSubtypeCount(item_type)``
Returns the number of raw-defined subtypes of the given item type, or *-1* if not applicable.
* ``dfhack.items.getSubtypeDef(item_type, subtype)``
Returns the raw definition for the given item type and subtype, or *nil* if invalid.
Maps module Maps module
----------- -----------
@ -1027,7 +1071,7 @@ Maps module
Returns a map block object for given x,y,z in local block coordinates. Returns a map block object for given x,y,z in local block coordinates.
* ``dfhack.maps.isValidTilePos(coords)``, or isValidTilePos(x,y,z)`` * ``dfhack.maps.isValidTilePos(coords)``, or ``isValidTilePos(x,y,z)``
Checks if the given df::coord or x,y,z in local tile coordinates are valid. Checks if the given df::coord or x,y,z in local tile coordinates are valid.
@ -1039,6 +1083,14 @@ Maps module
Like ``getTileBlock``, but if the block is not allocated, try creating it. Like ``getTileBlock``, but if the block is not allocated, try creating it.
* ``dfhack.maps.getTileType(coords)``, or ``getTileType(x,y,z)``
Returns the tile type at the given coordinates, or *nil* if invalid.
* ``dfhack.maps.getTileFlags(coords)``, or ``getTileFlags(x,y,z)``
Returns designation and occupancy references for the given coordinates, or *nil, nil* if invalid.
* ``dfhack.maps.getRegionBiome(region_coord2d)``, or ``getRegionBiome(x,y)`` * ``dfhack.maps.getRegionBiome(region_coord2d)``, or ``getRegionBiome(x,y)``
Returns the biome info struct for the given global map region. Returns the biome info struct for the given global map region.
@ -1074,6 +1126,22 @@ 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.hasTileAssignment(tilemask)``
Checks if the tile_bitmask object is not *nil* and contains any set bits; returns *true* or *false*.
* ``dfhack.maps.getTileAssignment(tilemask,x,y)``
Checks if the tile_bitmask object is not *nil* and has the relevant bit set; returns *true* or *false*.
* ``dfhack.maps.setTileAssignment(tilemask,x,y,enable)``
Sets the relevant bit in the tile_bitmask object to the *enable* argument.
* ``dfhack.maps.resetTileAssignment(tilemask[,enable])``
Sets all bits in the mask to the *enable* argument.
Burrows module Burrows module
-------------- --------------
@ -1688,6 +1756,18 @@ environment by the mandatory init file dfhack.lua:
Returns a table with x, y and z as fields. Returns a table with x, y and z as fields.
* ``same_xyz(a,b)``
Checks if ``a`` and ``b`` have the same x, y and z fields.
* ``get_path_xyz(path,i)``
Returns ``path.x[i], path.y[i], path.z[i]``.
* ``pos2xy(obj)``, ``xy2pos(x,y)``, ``same_xy(a,b)``, ``get_path_xy(a,b)``
Same as above, but for 2D coordinates.
* ``safe_index(obj,index...)`` * ``safe_index(obj,index...)``
Walks a sequence of dereferences, which may be represented by numbers or strings. Walks a sequence of dereferences, which may be represented by numbers or strings.
@ -1801,6 +1881,14 @@ utils
(For an explanation of ``new=true``, see table assignment in the wrapper section) (For an explanation of ``new=true``, see table assignment in the wrapper section)
* ``utils.erase_sorted_key(vector,key,field,cmpfun)``
Removes the item with the given key from the list. Returns: *did_erase, vector[idx], idx*.
* ``utils.erase_sorted(vector,item,field,cmpfun)``
Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``.
* ``utils.prompt_yes_no(prompt, default)`` * ``utils.prompt_yes_no(prompt, default)``
Presents a yes/no prompt to the user. If ``default`` is not *nil*, Presents a yes/no prompt to the user. If ``default`` is not *nil*,

26
NEWS

@ -1,4 +1,20 @@
DFHack v0.34.11-r2 (UNRELEASED) DFHack future
Internals:
- support for displaying active keybindings properly.
- support for reusable widgets in lua screen library.
Notable bugfixes:
- autobutcher can be re-enabled again after being stopped.
- stopped Dwarf Manipulator from unmasking vampires.
Misc improvements:
- fastdwarf: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing binary patches.
- removebadthoughts: add --dry-run option
New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them.
DFHack v0.34.11-r2
Internals: Internals:
- full support for Mac OS X. - full support for Mac OS X.
@ -19,6 +35,10 @@ DFHack v0.34.11-r2 (UNRELEASED)
- liquids: can paint permaflow, i.e. what makes rivers power water wheels. - liquids: can paint permaflow, i.e. what makes rivers power water wheels.
- prospect: pre-embark prospector accounts for caves & magma sea in its estimate. - prospect: pre-embark prospector accounts for caves & magma sea in its estimate.
- rename: supports renaming stockpiles, workshops, traps, siege engines. - rename: supports renaming stockpiles, workshops, traps, siege engines.
- fastdwarf: now has an additional option to make dwarves teleport to their destination.
New commands:
- misery: multiplies every negative thought gained (2x by default).
- digtype: designates every tile of the same type of vein on the map for 'digging' (any dig designation).
New tweaks: New tweaks:
- tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus. - tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus.
- tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does. - tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does.
@ -27,6 +47,9 @@ DFHack v0.34.11-r2 (UNRELEASED)
- tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster. - tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster.
- tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc. - tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc.
- tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode. - tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode.
- tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens.
- tweak military-stable-assign: Stop rightmost list of military->Positions from jumping to top.
- tweak military-color-assigned: In same list, color already assigned units in brown & green.
New scripts: New scripts:
- fixnaked: removes thoughts about nakedness. - fixnaked: removes thoughts about nakedness.
- setfps: set FPS cap at runtime, in case you want slow motion or speed-up. - setfps: set FPS cap at runtime, in case you want slow motion or speed-up.
@ -49,6 +72,7 @@ DFHack v0.34.11-r2 (UNRELEASED)
- gui/rename: renaming stockpiles, workshops and units via an in-game dialog. - gui/rename: renaming stockpiles, workshops and units via an in-game dialog.
- gui/power-meter: front-end for the Power Meter plugin. - gui/power-meter: front-end for the Power Meter plugin.
- gui/siege-engine: front-end for the Siege Engine plugin. - gui/siege-engine: front-end for the Siege Engine plugin.
- gui/choose-weapons: auto-choose matching weapons in the military equip screen.
Autolabor plugin: Autolabor plugin:
- can set nonidle hauler percentage. - can set nonidle hauler percentage.
- broker excluded from all labors when needed at depot. - broker excluded from all labors when needed at depot.

File diff suppressed because it is too large Load Diff

@ -31,7 +31,7 @@ Compatibility
DFHack works on Windows XP, Vista, 7 or any modern Linux distribution. DFHack works on Windows XP, Vista, 7 or any modern Linux distribution.
OSX is not supported due to lack of developers with a Mac. OSX is not supported due to lack of developers with a Mac.
Currently, versions 0.34.08 - 0.34.11 are supported. If you need DFHack Currently, version 0.34.11 is supported (and tested). If you need DFHack
for older versions, look for older releases. for older versions, look for older releases.
On Windows, you have to use the SDL version of DF. On Windows, you have to use the SDL version of DF.
@ -88,6 +88,36 @@ Interactive commands like 'liquids' cannot be used as hotkeys.
Most of the commands come from plugins. Those reside in 'hack/plugins/'. Most of the commands come from plugins. Those reside in 'hack/plugins/'.
Patched binaries
================
On linux and OSX, users of patched binaries may have to find the relevant
section in symbols.xml, and add a new line with the checksum of their
executable::
<md5-hash value='????????????????????????????????'/>
In order to find the correct value of the hash, look into stderr.log;
DFHack prints an error there if it does not recognize the hash.
DFHack includes a small stand-alone utility for applying and removing
binary patches from the game executable. Use it from the regular operating
system console:
* ``binpatch check "Dwarf Fortress.exe" patch.dif``
Checks and prints if the patch is currently applied.
* ``binpatch apply "Dwarf Fortress.exe" patch.dif``
Applies the patch, unless it is already applied or in conflict.
* ``binpatch remove "Dwarf Fortress.exe" patch.dif``
Removes the patch, unless it is already removed.
The patches are expected to be encoded in text format used by IDA.
============================= =============================
Something doesn't work, help! Something doesn't work, help!
============================= =============================
@ -148,6 +178,24 @@ for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@fo
Commands Commands
======== ========
DFHack command syntax consists of a command name, followed by arguments separated
by whitespace. To include whitespace in an argument, quote it in double quotes.
To include a double quote character, use ``\"`` inside double quotes.
If the first non-whitespace character of a line is ``#``, the line is treated
as a comment, i.e. a silent no-op command.
If the first non-whitespace character is ``:``, the command is parsed in a special
alternative mode: first, non-whitespace characters immediately following the ``:``
are used as the command name; the remaining part of the line, starting with the first
non-whitespace character *after* the command name, is used verbatim as the first argument.
The following two command lines are exactly equivalent:
* ``:foo a b "c d" e f``
* ``foo "a b \"c d\" e f"``
This is intended for commands like ``rb_eval`` that evaluate script language statements.
Almost all the commands support using the 'help <command-name>' built-in command Almost all the commands support using the 'help <command-name>' built-in command
to retrieve further help without having to look at this document. Alternatively, to retrieve further help without having to look at this document. Alternatively,
some accept a 'help'/'?' option on their command line. some accept a 'help'/'?' option on their command line.
@ -175,11 +223,14 @@ by 'reveal hell'. This is nice for digging under rivers.
fastdwarf fastdwarf
--------- ---------
Makes your minions move at ludicrous speeds. Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed.
* Activate with 'fastdwarf 1'
* Deactivate with 'fastdwarf 0'
* 'fastdwarf 0 0' disables both
* 'fastdwarf 0 1' disables speedydwarf and enables teledwarf
* 'fastdwarf 1 0' enables speedydwarf and disables teledwarf
* 'fastdwarf 1 1' enables both
* 'fastdwarf 0' disables both
* 'fastdwarf 1' enables speedydwarf and disables teledwarf
Game interface Game interface
============== ==============
@ -603,14 +654,20 @@ Options:
Pre-embark estimate Pre-embark estimate
................... ...................
If called during the embark selection screen, displays an estimate of layer If prospect is called during the embark selection screen, it displays an estimate of
stone availability. If the 'all' option is specified, also estimates veins. layer stone availability.
The estimate is computed either for 1 embark tile of the blinking biome, or
for all tiles of the embark rectangle. .. note::
The results of pre-embark prospect are an *estimate*, and can at best be expected
to be somewhere within +/- 30% of the true amount; sometimes it does a lot worse.
Especially, it is not clear how to precisely compute how many soil layers there
will be in a given embark tile, so it can report a whole extra layer, or omit one
that is actually present.
Options: Options:
:all: processes all tiles, even hidden ones. :all: Also estimate vein mineral amounts.
reveal reveal
------ ------
@ -780,6 +837,22 @@ Examples:
* 'digcircle' = Do it again. * 'digcircle' = Do it again.
digtype
-------
For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated.
If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.
Options:
:dig:
:channel:
:ramp:
:updown: up/down stairs
:up: up stairs
:down: down stairs
:clear: clear designation
filltraffic filltraffic
----------- -----------
Set traffic designations using flood-fill starting at the cursor. Set traffic designations using flood-fill starting at the cursor.
@ -938,9 +1011,9 @@ accidentally placed a construction on top of a valuable mineral floor.
tweak tweak
----- -----
Contains various tweaks for minor bugs (currently just one). Contains various tweaks for minor bugs.
Options: One-shot subcommands:
:clear-missing: Remove the missing status from the selected unit. :clear-missing: Remove the missing status from the selected unit.
This allows engraving slabs for ghostly, but not yet This allows engraving slabs for ghostly, but not yet
@ -969,6 +1042,9 @@ Options:
and are not flagged as tame), but you are allowed to mark them and are not flagged as tame), but you are allowed to mark them
for slaughter. Grabbing wagons results in some funny spam, then for slaughter. Grabbing wagons results in some funny spam, then
they are scuttled. they are scuttled.
Subcommands that persist until disabled or DF quit:
:stable-cursor: Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode. :stable-cursor: Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.
:patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts. :patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts.
Does NOT fix the problem when soldiers go off-duty (i.e. civilian). Does NOT fix the problem when soldiers go off-duty (i.e. civilian).
@ -986,6 +1062,13 @@ Options:
in advmode. The issue is that the screen tries to force you to select in advmode. The issue is that the screen tries to force you to select
the contents separately from the container. This forcefully skips child the contents separately from the container. This forcefully skips child
reagents. reagents.
:fast-trade: Makes Shift-Enter in the Move Goods to Depot and Trade screens select
the current item (fully, in case of a stack), and scroll down one line.
:military-stable-assign: Preserve list order and cursor position when assigning to squad,
i.e. stop the rightmost list of the Positions page of the military
screen from constantly resetting to the top.
:military-color-assigned: Color squad candidates already assigned to other squads in brown/green
to make them stand out more in the list.
Mode switch and reclaim Mode switch and reclaim
@ -1476,6 +1559,17 @@ Confirmed working DFusion plugins:
* This is currently working only on Windows. * This is currently working only on Windows.
* The game will be suspended while you're using dfusion. Don't panic when it doen't respond. * The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
misery
------
When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).
Usage:
:misery enable n: enable misery with optional magnitude n. If specified, n must be positive.
:misery n: same as "misery enable n"
:misery enable: same as "misery enable 2"
:misery disable: stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to "misery 1"
:misery clear: remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.
======= =======
Scripts Scripts
@ -1544,8 +1638,9 @@ siren
Wakes up sleeping units, cancels breaks and stops parties either everywhere, Wakes up sleeping units, cancels breaks and stops parties either everywhere,
or in the burrows given as arguments. In return, adds bad thoughts about or in the burrows given as arguments. In return, adds bad thoughts about
noise and tiredness. Also, the units with interrupted breaks will go on noise, tiredness and lack of protection. Also, the units with interrupted
break again a lot sooner. breaks will go on break again a lot sooner. The script is intended for
emergencies, e.g. when a siege appears, and all your military is partying.
growcrops growcrops
========= =========
@ -1567,16 +1662,18 @@ removebadthoughts
This script remove negative thoughts from your dwarves. Very useful against This script remove negative thoughts from your dwarves. Very useful against
tantrum spirals. tantrum spirals.
With a selected unit in 'v' mode, will clear this unit's mind, otherwise The script can target a single creature, when used with the ``him`` argument,
clear all your fort's units minds. or the whole fort population, with ``all``.
To show every bad thought present without actually removing them, run the
script with the ``-n`` or ``--dry-run`` argument. This can give a quick
hint on what bothers your dwarves the most.
Individual dwarf happiness may not increase right after this command is run, Individual dwarf happiness may not increase right after this command is run,
but in the short term your dwarves will get much more joyful. but in the short term your dwarves will get much more joyful.
The thoughts are set to be very old, and the game will remove them soon when
you unpause.
With the optional ``-v`` parameter, the script will dump the negative thoughts Internals: the thoughts are set to be very old, so that the game remove them
it removed. quickly after you unpause.
slayrace slayrace
@ -1585,7 +1682,7 @@ Kills any unit of a given race.
With no argument, lists the available races. With no argument, lists the available races.
With the special argument 'him', targets only the selected creature. With the special argument ``him``, targets only the selected creature.
Any non-dead non-caged unit of the specified race gets its ``blood_count`` Any non-dead non-caged unit of the specified race gets its ``blood_count``
set to 0, which means immediate death at the next game tick. For creatures set to 0, which means immediate death at the next game tick. For creatures
@ -1636,8 +1733,8 @@ digfort
A script to designate an area for digging according to a plan in csv format. A script to designate an area for digging according to a plan in csv format.
This script, inspired from quickfort, can designate an area for digging. This script, inspired from quickfort, can designate an area for digging.
Your plan should be stored in a .csv file like this: Your plan should be stored in a .csv file like this::
::
# this is a comment # this is a comment
d;d;u;d;d;skip this tile;d d;d;u;d;d;skip this tile;d
d;d;d;i d;d;d;i
@ -1656,8 +1753,8 @@ superdwarf
========== ==========
Similar to fastdwarf, per-creature. Similar to fastdwarf, per-creature.
To make any creature superfast, target it ingame using 'v' and: To make any creature superfast, target it ingame using 'v' and::
::
superdwarf add superdwarf add
Other options available: ``del``, ``clear``, ``list``. Other options available: ``del``, ``clear``, ``list``.
@ -1681,6 +1778,14 @@ In-game interface tools
These tools work by displaying dialogs or overlays in the game window, and These tools work by displaying dialogs or overlays in the game window, and
are mostly implemented by lua scripts. are mostly implemented by lua scripts.
.. note::
In order to avoid user confusion, as a matter of policy all these tools
display the word "DFHack" on the screen somewhere while active.
As an exception, the tweak plugin described above does not follow this
guideline because it arguably just fixes small usability bugs in the game UI.
Dwarf Manipulator Dwarf Manipulator
================= =================
@ -1699,14 +1804,15 @@ Use the arrow keys or number pad to move the cursor around, holding Shift to
move 10 tiles at a time. move 10 tiles at a time.
Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill
categories. categories. The numpad Z-Up and Z-Down keys seek to the first or last unit
in the list. Backspace seeks to the top left corner.
Press Enter to toggle the selected labor for the selected unit, or Shift+Enter Press Enter to toggle the selected labor for the selected unit, or Shift+Enter
to toggle all labors within the selected category. to toggle all labors within the selected category.
Press the ``+-`` keys to sort the unit list according to the currently selected Press the ``+-`` keys to sort the unit list according to the currently selected
skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession, skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession,
or Happiness (using Tab to select which sort method to use here). Happiness, or Arrival order (using Tab to select which sort method to use here).
With a unit selected, you can press the "v" key to view its properties (and With a unit selected, you can press the "v" key to view its properties (and
possibly set a custom nickname or profession) or the "c" key to exit possibly set a custom nickname or profession) or the "c" key to exit
@ -1726,19 +1832,19 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen. directly to the main dwarf mode screen.
Liquids gui/liquids
======= ===========
Implemented by the gui/liquids script. To use, bind to a key and activate in the 'k' mode. To use, bind to a key and activate in the 'k' mode.
While active, use the suggested keys to switch the usual liquids parameters, and Enter While active, use the suggested keys to switch the usual liquids parameters, and Enter
to select the target area and apply changes. to select the target area and apply changes.
Mechanisms gui/mechanisms
========== ==============
Implemented by the gui/mechanims script. To use, bind to a key and activate in the 'q' mode. To use, bind to a key and activate in the 'q' mode.
Lists mechanisms connected to the building, and their links. Navigating the list centers Lists mechanisms connected to the building, and their links. Navigating the list centers
the view on the relevant linked buildings. the view on the relevant linked buildings.
@ -1748,20 +1854,10 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter
re-entering the mechanisms ui. re-entering the mechanisms ui.
Power Meter gui/rename
=========== ==========
Front-end to the power-meter plugin implemented by the gui/power-meter script. Bind to a
key and activate after selecting Pressure Plate in the build menu.
The script follows the general look and feel of the regular pressure plate build
configuration page, but configures parameters relevant to the modded power meter building.
Rename
======
Backed by the rename plugin, the gui/rename script allows entering the desired name Backed by the rename plugin, this script allows entering the desired name
via a simple dialog in the game ui. via a simple dialog in the game ui.
* ``gui/rename [building]`` in 'q' mode changes the name of a building. * ``gui/rename [building]`` in 'q' mode changes the name of a building.
@ -1773,24 +1869,69 @@ via a simple dialog in the game ui.
* ``gui/rename unit-profession`` changes the selected unit's custom profession name. * ``gui/rename unit-profession`` changes the selected unit's custom profession name.
The ``building`` or ``unit`` are automatically assumed when in relevant ui state. The ``building`` or ``unit`` options are automatically assumed when in relevant ui state.
Room List gui/room-list
========= =============
Implemented by the gui/room-list script. To use, bind to a key and activate in the 'q' mode, To use, bind to a key and activate in the 'q' mode, either immediately or after opening
either immediately or after opening the assign owner page. the assign owner page.
The script lists other rooms owned by the same owner, or by the unit selected in the assign The script lists other rooms owned by the same owner, or by the unit selected in the assign
list, and allows unassigning them. list, and allows unassigning them.
gui/choose-weapons
==================
Bind to a key, and activate in the Equip->View/Customize page of the military screen.
Depending on the cursor location, it rewrites all 'individual choice weapon' entries
in the selected squad or position to use a specific weapon type matching the assigned
unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites
only that entry, and does it even if it is not 'individual choice'.
Rationale: individual choice seems to be unreliable when there is a weapon shortage,
and may lead to inappropriate weapons being selected.
=============
Behavior Mods
=============
These plugins, when activated via configuration UI or by detecting certain
structures in RAWs, modify the game engine behavior concerning the target
objects to add features not otherwise present.
.. admonition:: DISCLAIMER
The plugins in this section have mostly been created for fun as an interesting
technical challenge, and do not represent any long-term plans to produce more
similar modifications of the game.
Siege Engine Siege Engine
============ ============
Front-end to the siege-engine plugin implemented by the gui/siege-engine script. Bind to a The siege-engine plugin enables siege engines to be linked to stockpiles, and
key and activate after selecting a siege engine in 'q' mode. aimed at an arbitrary rectangular area across Z levels, instead of the original
four directions. Also, catapults can be ordered to load arbitrary objects, not
just stones.
Rationale
---------
Siege engines are a very interesting feature, but sadly almost useless in the current state
because they haven't been updated since 2D and can only aim in four directions. This is an
attempt to bring them more up to date until Toady has time to work on it. Actual improvements,
e.g. like making siegers bring their own, are something only Toady can do.
Configuration UI
----------------
The configuration front-end to the plugin is implemented by the gui/siege-engine
script. Bind it to a key and activate after selecting a siege engine in 'q' mode.
The main mode displays the current target, selected ammo item type, linked stockpiles and The main mode displays the current target, selected ammo item type, linked stockpiles and
the allowed operator skill range. The map tile color is changed to signify if it can be the allowed operator skill range. The map tile color is changed to signify if it can be
@ -1799,7 +1940,8 @@ yellow for partially blocked.
Pressing 'r' changes into the target selection mode, which works by highlighting two points Pressing 'r' changes into the target selection mode, which works by highlighting two points
with Enter like all designations. When a target area is set, the engine projectiles are with Enter like all designations. When a target area is set, the engine projectiles are
aimed at that area, or units within it, instead of the vanilla four directions. aimed at that area, or units within it (this doesn't actually change the original aiming
code, instead the projectile trajectory parameters are rewritten as soon as it appears).
After setting the target in this way for one engine, you can 'paste' the same area into others After setting the target in this way for one engine, you can 'paste' the same area into others
just by pressing 'p' in the main page of this script. The area to paste is kept until you quit just by pressing 'p' in the main page of this script. The area to paste is kept until you quit
@ -1811,19 +1953,18 @@ Exiting from the siege engine script via ESC reverts the view to the state prior
the script. Shift-ESC retains the current viewport, and also exits from the 'q' mode to main the script. Shift-ESC retains the current viewport, and also exits from the 'q' mode to main
menu. menu.
.. admonition:: DISCLAIMER
Siege engines are a very interesting feature, but currently nearly useless Power Meter
because they haven't been updated since 2D and can only aim in four directions. This is an ===========
attempt to bring them more up to date until Toady has time to work on it. Actual improvements,
e.g. like making siegers bring their own, are something only Toady can do.
The power-meter plugin implements a modified pressure plate that detects power being
supplied to gear boxes built in the four adjacent N/S/W/E tiles.
========= The configuration front-end is implemented by the gui/power-meter script. Bind it to a
RAW hacks key and activate after selecting Pressure Plate in the build menu.
=========
These plugins detect certain structures in RAWs, and enhance them in various ways. The script follows the general look and feel of the regular pressure plate build
configuration page, but configures parameters relevant to the modded power meter building.
Steam Engine Steam Engine
@ -1855,7 +1996,7 @@ The magma version also needs magma.
.. admonition:: ISSUE .. admonition:: ISSUE
Since this building is a machine, and machine collapse Since this building is a machine, and machine collapse
code cannot be modified, it would collapse over true open space. code cannot be hooked, it would collapse over true open space.
As a loophole, down stair provides support to machines, while As a loophole, down stair provides support to machines, while
being passable, so use them. being passable, so use them.
@ -1866,7 +2007,7 @@ is extracted from the workshop raws.
.. admonition:: ISSUE .. admonition:: ISSUE
Like with collapse above, part of the code involved in Like with collapse above, part of the code involved in
machine connection cannot be modified. As a result, the workshop machine connection cannot be hooked. As a result, the workshop
can only immediately connect to machine components built AFTER it. can only immediately connect to machine components built AFTER it.
This also means that engines cannot be chained without intermediate This also means that engines cannot be chained without intermediate
short axles that can be built later than both of the engines. short axles that can be built later than both of the engines.
@ -1879,8 +2020,13 @@ on repeat). A furnace operator will come, possibly bringing a bar of fuel,
and perform it. As a result, a "boiling water" item will appear and perform it. As a result, a "boiling water" item will appear
in the 't' view of the workshop. in the 't' view of the workshop.
**NOTE**: The completion of the job will actually consume one unit .. note::
of the appropriate liquids from below the workshop.
The completion of the job will actually consume one unit
of the appropriate liquids from below the workshop. This means
that you cannot just raise 7 units of magma with a piston and
have infinite power. However, liquid consumption should be slow
enough that water can be supplied by a pond zone bucket chain.
Every such item gives 100 power, up to a limit of 300 for coal, Every such item gives 100 power, up to a limit of 300 for coal,
and 500 for a magma engine. The building can host twice that and 500 for a magma engine. The building can host twice that
@ -1928,9 +2074,12 @@ Add Spatter
=========== ===========
This plugin makes reactions with names starting with ``SPATTER_ADD_`` This plugin makes reactions with names starting with ``SPATTER_ADD_``
produce contaminants on the items instead of improvements. produce contaminants on the items instead of improvements. The produced
contaminants are immune to being washed away by water or destroyed by
the ``clean items`` command.
Intended to give some use to all those poisons that can be bought from caravans. The plugin is intended to give some use to all those poisons that can
be bought from caravans. :)
To be really useful this needs patches from bug 808, ``tweak fix-dimensions`` To be really useful this needs patches from bug 808, ``tweak fix-dimensions``
and ``tweak advmode-contained``. and ``tweak advmode-contained``.

@ -1 +1 @@
Subproject commit d0b2d0750dc2d529a152eba4f3f519f69ff7eab0 Subproject commit 178a4916da838e46e46e769e566e47fff6eff8f8

@ -36,16 +36,14 @@
* internal hash function, calling * internal hash function, calling
* the basic methods from md5.h * the basic methods from md5.h
*/ */
std::string md5wrapper::hashit(std::string text) std::string md5wrapper::hashit(unsigned char *data, size_t length)
{ {
MD5Context ctx; MD5Context ctx;
//init md5 //init md5
MD5Init(&ctx); MD5Init(&ctx);
//update with our string //update with our string
MD5Update(&ctx, MD5Update(&ctx, data, length);
(unsigned char*)text.c_str(),
text.length());
//create the hash //create the hash
unsigned char buff[16] = ""; unsigned char buff[16] = "";
@ -95,10 +93,9 @@ md5wrapper::~md5wrapper()
*/ */
std::string md5wrapper::getHashFromString(std::string text) std::string md5wrapper::getHashFromString(std::string text)
{ {
return this->hashit(text); return this->hashit((unsigned char*)text.data(), text.length());
} }
/* /*
* creates a MD5 hash from * creates a MD5 hash from
* a file specified in "filename" and * a file specified in "filename" and

@ -31,7 +31,7 @@ class md5wrapper
* internal hash function, calling * internal hash function, calling
* the basic methods from md5.h * the basic methods from md5.h
*/ */
std::string hashit(std::string text); std::string hashit(unsigned char *data, size_t length);
/* /*
* converts the numeric giets to * converts the numeric giets to
@ -52,6 +52,10 @@ class md5wrapper
*/ */
std::string getHashFromString(std::string text); std::string getHashFromString(std::string text);
std::string getHashFromBytes(const unsigned char *data, size_t size) {
return hashit(const_cast<unsigned char*>(data),size);
}
/* /*
* creates a MD5 hash from * creates a MD5 hash from
* a file specified in "filename" and * a file specified in "filename" and

@ -18,7 +18,7 @@ keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
# gui/rename script # gui/rename script
keybinding add Ctrl-Shift-N gui/rename keybinding add Ctrl-Shift-N gui/rename
keybinding add Ctrl-Shift-P "gui/rename unit-profession" keybinding add Alt-Shift-P "gui/rename unit-profession"
############################## ##############################
# Generic adv mode bindings # # Generic adv mode bindings #
@ -45,8 +45,15 @@ keybinding add Shift-R "job-material RHYOLITE"
keybinding add Shift-I "job-material CINNABAR" keybinding add Shift-I "job-material CINNABAR"
keybinding add Shift-B "job-material COBALTITE" keybinding add Shift-B "job-material COBALTITE"
keybinding add Shift-O "job-material OBSIDIAN" keybinding add Shift-O "job-material OBSIDIAN"
keybinding add Shift-T "job-material ORTHOCLASE"
keybinding add Shift-G "job-material GLASS_GREEN" keybinding add Shift-G "job-material GLASS_GREEN"
# sort units and items
keybinding add Alt-Shift-N "sort-units name" "sort-items description"
keybinding add Alt-Shift-R "sort-units arrival"
keybinding add Alt-Shift-T "sort-units profession" "sort-items type material"
keybinding add Alt-Shift-Q "sort-units squad_position" "sort-items quality"
# browse linked mechanisms # browse linked mechanisms
keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
@ -62,6 +69,15 @@ keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter
# siege engine control # siege engine control
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine
# military weapon auto-select
keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons
# minecart Guide path
keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
# workshop job details
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job
############################ ############################
# UI and game logic tweaks # # UI and game logic tweaks #
############################ ############################
@ -90,3 +106,12 @@ tweak fix-dimensions
# make reactions requiring containers usable in advmode - the issue is # make reactions requiring containers usable in advmode - the issue is
# that the screen asks for those reagents to be selected directly # that the screen asks for those reagents to be selected directly
tweak advmode-contained tweak advmode-contained
# support Shift-Enter in Trade and Move Goods to Depot screens for faster
# selection; it selects the current item or stack and scrolls down one line
tweak fast-trade
# stop the right list in military->positions from resetting to top all the time
tweak military-stable-assign
# in same list, color units already assigned to squads in brown & green
tweak military-color-assigned

@ -1,5 +1,18 @@
#!/bin/bash #!/bin/bash
rst2html README.rst > Readme.html # regenerate documentation after editing the .rst files. Requires python and docutils.
rst2html COMPILE.rst > Compile.html
rst2html DEVEL.rst > Devel.html cd `dirname $0`
rst2html LUA_API.rst > Lua\ API.html
function process() {
if [ "$1" -nt "$2" ]; then
rst2html --no-generator --no-datestamp "$1" "$2"
else
echo "$2 - up to date."
fi
}
process Readme.rst Readme.html
process Compile.rst Compile.html
process Lua\ API.rst Lua\ API.html
process Contributors.rst Contributors.html

@ -250,6 +250,9 @@ ADD_DEPENDENCIES(dfhack-client dfhack)
ADD_EXECUTABLE(dfhack-run dfhack-run.cpp) ADD_EXECUTABLE(dfhack-run dfhack-run.cpp)
ADD_EXECUTABLE(binpatch binpatch.cpp)
TARGET_LINK_LIBRARIES(binpatch dfhack-md5)
IF(BUILD_EGGY) IF(BUILD_EGGY)
SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" ) SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" )
else() else()
@ -329,7 +332,7 @@ install(FILES xml/symbols.xml
install(FILES ../dfhack.init-example install(FILES ../dfhack.init-example
DESTINATION ${DFHACK_BINARY_DESTINATION}) DESTINATION ${DFHACK_BINARY_DESTINATION})
install(TARGETS dfhack-run dfhack-client install(TARGETS dfhack-run dfhack-client binpatch
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -126,8 +126,34 @@ struct Core::Private
void Core::cheap_tokenise(string const& input, vector<string> &output) void Core::cheap_tokenise(string const& input, vector<string> &output)
{ {
string *cur = NULL; string *cur = NULL;
size_t i = 0;
for (size_t i = 0; i < input.size(); i++) { // Check the first non-space character
while (i < input.size() && isspace(input[i])) i++;
// Special verbatim argument mode?
if (i < input.size() && input[i] == ':')
{
// Read the command
std::string cmd;
i++;
while (i < input.size() && !isspace(input[i]))
cmd.push_back(input[i++]);
if (!cmd.empty())
output.push_back(cmd);
// Find the argument
while (i < input.size() && isspace(input[i])) i++;
if (i < input.size())
output.push_back(input.substr(i));
return;
}
// Otherwise, parse in the regular quoted mode
for (; i < input.size(); i++)
{
unsigned char c = input[i]; unsigned char c = input[i];
if (isspace(c)) { if (isspace(c)) {
cur = NULL; cur = NULL;
@ -348,6 +374,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" unload PLUGIN|all - Unload a plugin or all loaded plugins.\n" " unload PLUGIN|all - Unload a plugin or all loaded plugins.\n"
" reload PLUGIN|all - Reload a plugin or all loaded plugins.\n" " reload PLUGIN|all - Reload a plugin or all loaded plugins.\n"
); );
con.print("\nDFHack version " DFHACK_VERSION ".\n");
} }
else if (parts.size() == 1) else if (parts.size() == 1)
{ {
@ -601,8 +629,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
} }
else if(first == "fpause") else if(first == "fpause")
{ {
World * w = getWorld(); World::SetPauseState(true);
w->SetPauseState(true);
con.print("The game was forced to pause!\n"); con.print("The game was forced to pause!\n");
} }
else if(first == "cls") else if(first == "cls")
@ -795,6 +822,8 @@ std::string Core::getHackPath()
#endif #endif
} }
void init_screen_module(Core *);
bool Core::Init() bool Core::Init()
{ {
if(started) if(started)
@ -840,6 +869,7 @@ bool Core::Init()
// Init global object pointers // Init global object pointers
df::global::InitGlobals(); df::global::InitGlobals();
init_screen_module(this);
cerr << "Initializing Console.\n"; cerr << "Initializing Console.\n";
// init the console. // init the console.
@ -1096,7 +1126,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
last_world_data_ptr = new_wdata; last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata; last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache(); World::ClearPersistentCache();
// and if the world is going away, we report the map change first // and if the world is going away, we report the map change first
if(had_map) if(had_map)
@ -1114,7 +1144,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
if (isMapLoaded() != had_map) if (isMapLoaded() != had_map)
{ {
getWorld()->ClearPersistentCache(); World::ClearPersistentCache();
onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
} }
} }
@ -1652,7 +1682,6 @@ TYPE * Core::get##TYPE() \
return s_mods.p##TYPE;\ return s_mods.p##TYPE;\
} }
MODULE_GETTER(World);
MODULE_GETTER(Materials); MODULE_GETTER(Materials);
MODULE_GETTER(Notes); MODULE_GETTER(Notes);
MODULE_GETTER(Graphic); MODULE_GETTER(Graphic);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h" #include "MemAccess.h"
#include "Core.h" #include "Core.h"
#include "Error.h"
#include "VersionInfo.h" #include "VersionInfo.h"
#include "tinythread.h" #include "tinythread.h"
// must be last due to MS stupidity // must be last due to MS stupidity
@ -81,6 +82,7 @@ distribution.
#include "df/flow_info.h" #include "df/flow_info.h"
#include "df/unit_misc_trait.h" #include "df/unit_misc_trait.h"
#include "df/proj_itemst.h" #include "df/proj_itemst.h"
#include "df/itemdef.h"
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
@ -258,7 +260,7 @@ static PersistentDataItem persistent_by_struct(lua_State *state, int idx)
int id = lua_tointeger(state, -1); int id = lua_tointeger(state, -1);
lua_pop(state, 1); lua_pop(state, 1);
PersistentDataItem ref = Core::getInstance().getWorld()->GetPersistentData(id); PersistentDataItem ref = World::GetPersistentData(id);
if (ref.isValid()) if (ref.isValid())
{ {
@ -311,19 +313,19 @@ static PersistentDataItem get_persistent(lua_State *state)
if (lua_istable(state, 1)) if (lua_istable(state, 1))
{ {
Lua::StackUnwinder frame(state);
if (!lua_getmetatable(state, 1) || if (!lua_getmetatable(state, 1) ||
!lua_rawequal(state, -1, lua_upvalueindex(1))) !lua_rawequal(state, -1, lua_upvalueindex(1)))
luaL_argerror(state, 1, "invalid table type"); luaL_argerror(state, 1, "invalid table type");
lua_settop(state, 1);
return persistent_by_struct(state, 1); return persistent_by_struct(state, 1);
} }
else else
{ {
const char *str = luaL_checkstring(state, 1); const char *str = luaL_checkstring(state, 1);
return Core::getInstance().getWorld()->GetPersistentData(str); return World::GetPersistentData(str);
} }
} }
@ -342,7 +344,7 @@ static int dfhack_persistent_delete(lua_State *state)
auto ref = get_persistent(state); auto ref = get_persistent(state);
bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref); bool ok = World::DeletePersistentData(ref);
lua_pushboolean(state, ok); lua_pushboolean(state, ok);
return 1; return 1;
@ -356,7 +358,7 @@ static int dfhack_persistent_get_all(lua_State *state)
bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false); bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false);
std::vector<PersistentDataItem> data; std::vector<PersistentDataItem> data;
Core::getInstance().getWorld()->GetPersistentData(&data, str, prefix); World::GetPersistentData(&data, str, prefix);
if (data.empty()) if (data.empty())
{ {
@ -396,7 +398,7 @@ static int dfhack_persistent_save(lua_State *state)
if (add) if (add)
{ {
ref = Core::getInstance().getWorld()->AddPersistentData(str); ref = World::AddPersistentData(str);
added = true; added = true;
} }
else if (lua_getmetatable(state, 1)) else if (lua_getmetatable(state, 1))
@ -409,13 +411,13 @@ static int dfhack_persistent_save(lua_State *state)
} }
else else
{ {
ref = Core::getInstance().getWorld()->GetPersistentData(str); ref = World::GetPersistentData(str);
} }
// Auto-add if not found // Auto-add if not found
if (!ref.isValid()) if (!ref.isValid())
{ {
ref = Core::getInstance().getWorld()->AddPersistentData(str); ref = World::AddPersistentData(str);
if (!ref.isValid()) if (!ref.isValid())
luaL_error(state, "cannot create persistent entry"); luaL_error(state, "cannot create persistent entry");
added = true; added = true;
@ -446,11 +448,38 @@ static int dfhack_persistent_save(lua_State *state)
return 2; return 2;
} }
static int dfhack_persistent_getTilemask(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 3);
auto ref = get_persistent(state);
auto block = Lua::CheckDFObject<df::map_block>(state, 2);
bool create = lua_toboolean(state, 3);
Lua::PushDFObject(state, World::getPersistentTilemask(ref, block, create));
return 1;
}
static int dfhack_persistent_deleteTilemask(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 2);
auto ref = get_persistent(state);
auto block = Lua::CheckDFObject<df::map_block>(state, 2);
lua_pushboolean(state, World::deletePersistentTilemask(ref, block));
return 1;
}
static const luaL_Reg dfhack_persistent_funcs[] = { static const luaL_Reg dfhack_persistent_funcs[] = {
{ "get", dfhack_persistent_get }, { "get", dfhack_persistent_get },
{ "delete", dfhack_persistent_delete }, { "delete", dfhack_persistent_delete },
{ "get_all", dfhack_persistent_get_all }, { "get_all", dfhack_persistent_get_all },
{ "save", dfhack_persistent_save }, { "save", dfhack_persistent_save },
{ "getTilemask", dfhack_persistent_getTilemask },
{ "deleteTilemask", dfhack_persistent_deleteTilemask },
{ NULL, NULL } { NULL, NULL }
}; };
@ -781,6 +810,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,getWorker), WRAPM(Job,getWorker),
WRAPM(Job,checkBuildingsNow), WRAPM(Job,checkBuildingsNow),
WRAPM(Job,checkDesignationsNow), WRAPM(Job,checkDesignationsNow),
WRAPM(Job,isSuitableItem),
WRAPM(Job,isSuitableMaterial),
WRAPN(is_equal, jobEqual), WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual), WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL } { NULL, NULL }
@ -828,6 +859,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDwarf), WRAPM(Units, isDwarf),
WRAPM(Units, isCitizen), WRAPM(Units, isCitizen),
WRAPM(Units, getAge), WRAPM(Units, getAge),
WRAPM(Units, getNominalSkill),
WRAPM(Units, getEffectiveSkill), WRAPM(Units, getEffectiveSkill),
WRAPM(Units, computeMovementSpeed), WRAPM(Units, computeMovementSpeed),
WRAPM(Units, getProfessionName), WRAPM(Units, getProfessionName),
@ -906,6 +938,9 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, setOwner), WRAPM(Items, setOwner),
WRAPM(Items, getContainer), WRAPM(Items, getContainer),
WRAPM(Items, getDescription), WRAPM(Items, getDescription),
WRAPM(Items, isCasteMaterial),
WRAPM(Items, getSubtypeCount),
WRAPM(Items, getSubtypeDef),
WRAPN(moveToGround, items_moveToGround), WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer), WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToBuilding, items_moveToBuilding), WRAPN(moveToBuilding, items_moveToBuilding),
@ -936,6 +971,22 @@ static const luaL_Reg dfhack_items_funcs[] = {
/***** Maps module *****/ /***** Maps module *****/
static bool hasTileAssignment(df::tile_bitmask *bm) {
return bm && bm->has_assignments();
}
static bool getTileAssignment(df::tile_bitmask *bm, int x, int y) {
return bm && bm->getassignment(x,y);
}
static void setTileAssignment(df::tile_bitmask *bm, int x, int y, bool val) {
CHECK_NULL_POINTER(bm);
bm->setassignment(x,y,val);
}
static void resetTileAssignment(df::tile_bitmask *bm, bool val) {
CHECK_NULL_POINTER(bm);
if (val) bm->set_all();
else bm->clear();
}
static const LuaWrapper::FunctionReg dfhack_maps_module[] = { static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, enableBlockUpdates), WRAPM(Maps, enableBlockUpdates),
@ -943,6 +994,10 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getLocalInitFeature), WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween), WRAPM(Maps, canWalkBetween),
WRAPM(Maps, spawnFlow), WRAPM(Maps, spawnFlow),
WRAPN(hasTileAssignment, hasTileAssignment),
WRAPN(getTileAssignment, getTileAssignment),
WRAPN(setTileAssignment, setTileAssignment),
WRAPN(resetTileAssignment, resetTileAssignment),
{ NULL, NULL } { NULL, NULL }
}; };
@ -967,6 +1022,25 @@ static int maps_ensureTileBlock(lua_State *L)
return 1; return 1;
} }
static int maps_getTileType(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
auto ptype = Maps::getTileType(pos);
if (ptype)
lua_pushinteger(L, *ptype);
else
lua_pushnil(L);
return 1;
}
static int maps_getTileFlags(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
Lua::PushDFObject(L, Maps::getTileDesignation(pos));
Lua::PushDFObject(L, Maps::getTileOccupancy(pos));
return 2;
}
static int maps_getRegionBiome(lua_State *L) static int maps_getRegionBiome(lua_State *L)
{ {
auto pos = CheckCoordXY(L, 1, true); auto pos = CheckCoordXY(L, 1, true);
@ -984,6 +1058,8 @@ static const luaL_Reg dfhack_maps_funcs[] = {
{ "isValidTilePos", maps_isValidTilePos }, { "isValidTilePos", maps_isValidTilePos },
{ "getTileBlock", maps_getTileBlock }, { "getTileBlock", maps_getTileBlock },
{ "ensureTileBlock", maps_ensureTileBlock }, { "ensureTileBlock", maps_ensureTileBlock },
{ "getTileType", maps_getTileType },
{ "getTileFlags", maps_getTileFlags },
{ "getRegionBiome", maps_getRegionBiome }, { "getRegionBiome", maps_getRegionBiome },
{ "getTileBiomeRgn", maps_getTileBiomeRgn }, { "getTileBiomeRgn", maps_getTileBiomeRgn },
{ NULL, NULL } { NULL, NULL }
@ -1145,6 +1221,7 @@ static const LuaWrapper::FunctionReg dfhack_screen_module[] = {
WRAPM(Screen, inGraphicsMode), WRAPM(Screen, inGraphicsMode),
WRAPM(Screen, clear), WRAPM(Screen, clear),
WRAPM(Screen, invalidate), WRAPM(Screen, invalidate),
WRAPM(Screen, getKeyDisplay),
{ NULL, NULL } { NULL, NULL }
}; };

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -1265,6 +1265,51 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type)
* Recursive walk of scopes to construct the df... tree. * Recursive walk of scopes to construct the df... tree.
*/ */
static int wtype_pnext(lua_State *L)
{
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, lua_upvalueindex(1)))
return 2;
lua_pushnil(L);
return 1;
}
static int wtype_pairs(lua_State *state)
{
lua_pushvalue(state, lua_upvalueindex(1));
lua_pushcclosure(state, wtype_pnext, 1);
lua_pushnil(state);
lua_pushnil(state);
return 3;
}
static int wtype_inext(lua_State *L)
{
int i = luaL_checkint(L, 2);
i++; /* next value */
if (i <= lua_tointeger(L, lua_upvalueindex(2)))
{
lua_pushinteger(L, i);
lua_rawgeti(L, lua_upvalueindex(1), i);
return 2;
}
else
{
lua_pushnil(L);
return 1;
}
}
static int wtype_ipairs(lua_State *state)
{
lua_pushvalue(state, lua_upvalueindex(1));
lua_pushvalue(state, lua_upvalueindex(3));
lua_pushcclosure(state, wtype_inext, 2);
lua_pushnil(state);
lua_pushvalue(state, lua_upvalueindex(2));
return 3;
}
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children); static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children);
void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name) void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name)
@ -1278,7 +1323,7 @@ void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *n
lua_rawset(state, table); lua_rawset(state, table);
} }
static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) static void FillEnumKeys(lua_State *state, int ix_meta, int ftable, enum_identity *eid)
{ {
const char *const *keys = eid->getKeys(); const char *const *keys = eid->getKeys();
@ -1296,11 +1341,17 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
if (eid->getFirstItem() <= eid->getLastItem()) if (eid->getFirstItem() <= eid->getLastItem())
{ {
lua_pushvalue(state, base+1);
lua_pushinteger(state, eid->getFirstItem()-1);
lua_pushinteger(state, eid->getLastItem());
lua_pushcclosure(state, wtype_ipairs, 3);
lua_setfield(state, ix_meta, "__ipairs");
lua_pushinteger(state, eid->getFirstItem()); lua_pushinteger(state, eid->getFirstItem());
lua_setfield(state, base+1, "_first_item"); lua_setfield(state, ftable, "_first_item");
lua_pushinteger(state, eid->getLastItem()); lua_pushinteger(state, eid->getLastItem());
lua_setfield(state, base+1, "_last_item"); lua_setfield(state, ftable, "_last_item");
} }
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
@ -1321,7 +1372,7 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
lua_setmetatable(state, ftable); lua_setmetatable(state, ftable);
} }
static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *eid) static void FillBitfieldKeys(lua_State *state, int ix_meta, int ftable, bitfield_identity *eid)
{ {
// Create a new table attached to ftable as __index // Create a new table attached to ftable as __index
lua_newtable(state); lua_newtable(state);
@ -1338,11 +1389,17 @@ static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *ei
i += bits[i].size-1; i += bits[i].size-1;
} }
lua_pushvalue(state, base+1);
lua_pushinteger(state, -1);
lua_pushinteger(state, eid->getNumBits()-1);
lua_pushcclosure(state, wtype_ipairs, 3);
lua_setfield(state, ix_meta, "__ipairs");
lua_pushinteger(state, 0); lua_pushinteger(state, 0);
lua_setfield(state, base+1, "_first_item"); lua_setfield(state, ftable, "_first_item");
lua_pushinteger(state, eid->getNumBits()-1); lua_pushinteger(state, eid->getNumBits()-1);
lua_setfield(state, base+1, "_last_item"); lua_setfield(state, ftable, "_last_item");
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
@ -1355,7 +1412,12 @@ static void RenderType(lua_State *state, compound_identity *node)
assert(node->getName()); assert(node->getName());
std::string name = node->getFullName(); std::string name = node->getFullName();
int base = lua_gettop(state); // Frame:
// base+1 - outer table
// base+2 - metatable of outer table
// base+3 - inner table
// base+4 - pairs table
Lua::StackUnwinder base(state);
lua_newtable(state); lua_newtable(state);
if (!lua_checkstack(state, 20)) if (!lua_checkstack(state, 20))
@ -1365,51 +1427,59 @@ static void RenderType(lua_State *state, compound_identity *node)
// metatable // metatable
lua_newtable(state); lua_newtable(state);
int ix_meta = base+2;
lua_dup(state); lua_dup(state);
lua_setmetatable(state, base+1); lua_setmetatable(state, base+1);
lua_pushstring(state, name.c_str()); lua_pushstring(state, name.c_str());
lua_setfield(state, base+2, "__metatable"); lua_setfield(state, ix_meta, "__metatable");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
lua_setfield(state, base+2, "__tostring"); lua_setfield(state, ix_meta, "__tostring");
lua_pushlightuserdata(state, node); lua_pushlightuserdata(state, node);
lua_rawsetp(state, base+2, &DFHACK_IDENTITY_FIELD_TOKEN); lua_rawsetp(state, ix_meta, &DFHACK_IDENTITY_FIELD_TOKEN);
// inner table // inner table
lua_newtable(state); lua_newtable(state);
int ftable = base+3;
lua_dup(state); lua_dup(state);
lua_setfield(state, base+2, "__index"); lua_setfield(state, ix_meta, "__index");
int ftable = base+3; // pairs table
lua_newtable(state);
int ptable = base+4;
lua_pushvalue(state, ptable);
lua_pushcclosure(state, wtype_pairs, 1);
lua_setfield(state, ix_meta, "__pairs");
switch (node->type()) switch (node->type())
{ {
case IDTYPE_STRUCT: case IDTYPE_STRUCT:
lua_pushstring(state, "struct-type"); lua_pushstring(state, "struct-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node); IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break; break;
case IDTYPE_CLASS: case IDTYPE_CLASS:
lua_pushstring(state, "class-type"); lua_pushstring(state, "class-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node); IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break; break;
case IDTYPE_ENUM: case IDTYPE_ENUM:
lua_pushstring(state, "enum-type"); lua_pushstring(state, "enum-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
FillEnumKeys(state, ftable, (enum_identity*)node); FillEnumKeys(state, ix_meta, ftable, (enum_identity*)node);
break; break;
case IDTYPE_BITFIELD: case IDTYPE_BITFIELD:
lua_pushstring(state, "bitfield-type"); lua_pushstring(state, "bitfield-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
FillBitfieldKeys(state, ftable, (bitfield_identity*)node); FillBitfieldKeys(state, ix_meta, ftable, (bitfield_identity*)node);
break; break;
case IDTYPE_GLOBAL: case IDTYPE_GLOBAL:
@ -1425,14 +1495,14 @@ static void RenderType(lua_State *state, compound_identity *node)
BuildTypeMetatable(state, node); BuildTypeMetatable(state, node);
lua_dup(state); lua_dup(state);
lua_setmetatable(state, base+3); lua_setmetatable(state, ftable);
lua_getfield(state, -1, "__newindex"); lua_getfield(state, -1, "__newindex");
lua_setfield(state, base+2, "__newindex"); lua_setfield(state, ix_meta, "__newindex");
lua_getfield(state, -1, "__pairs"); lua_getfield(state, -1, "__pairs");
lua_setfield(state, base+2, "__pairs"); lua_setfield(state, ix_meta, "__pairs");
lua_pop(state, 3); base += 1;
return; return;
} }
@ -1454,17 +1524,25 @@ static void RenderType(lua_State *state, compound_identity *node)
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
lua_setfield(state, ftable, "is_instance"); lua_setfield(state, ftable, "is_instance");
lua_pop(state, 2); base += 1;
} }
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children) static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children)
{ {
// fieldtable pairstable |
int base = lua_gettop(state);
for (size_t i = 0; i < children.size(); i++) for (size_t i = 0; i < children.size(); i++)
{ {
RenderType(state, children[i]); RenderType(state, children[i]);
lua_pushstring(state, children[i]->getName()); lua_pushstring(state, children[i]->getName());
lua_swap(state); lua_swap(state);
lua_rawset(state, -3);
// save in both tables
lua_pushvalue(state, -2);
lua_pushvalue(state, -2);
lua_rawset(state, base);
lua_rawset(state, base-1);
} }
} }
@ -1524,10 +1602,13 @@ static int DoAttach(lua_State *state)
{ {
// Assign df a metatable with read-only contents // Assign df a metatable with read-only contents
lua_newtable(state); lua_newtable(state);
lua_newtable(state);
// Render the type structure // Render the type structure
RenderTypeChildren(state, compound_identity::getTopScope()); RenderTypeChildren(state, compound_identity::getTopScope());
lua_swap(state); // -> pairstable fieldtable
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
lua_setfield(state, -2, "sizeof"); lua_setfield(state, -2, "sizeof");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
@ -1558,7 +1639,15 @@ static int DoAttach(lua_State *state)
lua_pushcfunction(state, meta_isnull); lua_pushcfunction(state, meta_isnull);
lua_setfield(state, -2, "isnull"); lua_setfield(state, -2, "isnull");
freeze_table(state, false, "df"); freeze_table(state, true, "df");
// pairstable dftable dfmeta
lua_pushvalue(state, -3);
lua_pushcclosure(state, wtype_pairs, 1);
lua_setfield(state, -2, "__pairs");
lua_pop(state, 1);
lua_remove(state, -2);
} }
return 1; return 1;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -292,9 +292,9 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (!unit->custom_profession.empty()) if (!unit->custom_profession.empty())
info->set_custom_profession(unit->custom_profession); info->set_custom_profession(unit->custom_profession);
if (unit->military.squad_index >= 0) if (unit->military.squad_id >= 0)
{ {
info->set_squad_id(unit->military.squad_index); info->set_squad_id(unit->military.squad_id);
info->set_squad_position(unit->military.squad_position); info->set_squad_position(unit->military.squad_position);
} }
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -281,9 +281,10 @@ VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_
return item; return item;
} }
void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr) bool VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr)
{ {
auto &children = cur->getChildren(); auto &children = cur->getChildren();
bool found = false;
for (size_t i = 0; i < children.size(); i++) for (size_t i = 0; i < children.size(); i++)
{ {
@ -298,17 +299,32 @@ void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmp
continue; continue;
child_next.insert(base); child_next.insert(base);
found = true;
} }
else else if (child->vtable_ptr)
{ {
void *cptr = child->get_vmethod_ptr(vmethod_idx); void *cptr = child->get_vmethod_ptr(vmethod_idx);
if (cptr != vmptr) if (cptr != vmptr)
continue; continue;
child_hosts.insert(child); child_hosts.insert(child);
found = true;
find_child_hosts(child, vmptr); find_child_hosts(child, vmptr);
} }
else
{
// If this vtable is not known, but any of the children
// have the same vmethod, this one definitely does too
if (find_child_hosts(child, vmptr))
{
child_hosts.insert(child);
found = true;
}
}
} }
return found;
} }
void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from) void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from)

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -0,0 +1,314 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2011 Petr Mrázek <peterix@gmail.com>
A thread-safe logging console with a line editor for windows.
Based on linenoise win32 port,
copyright 2010, Jon Griffiths <jon_p_griffiths at yahoo dot com>.
All rights reserved.
Based on linenoise, copyright 2010, Salvatore Sanfilippo <antirez at gmail dot com>.
The original linenoise can be found at: http://github.com/antirez/linenoise
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>
#include <memory>
#include <md5wrapper.h>
using std::cout;
using std::cerr;
using std::endl;
typedef unsigned char patch_byte;
struct BinaryPatch {
struct Byte {
unsigned offset;
patch_byte old_val, new_val;
};
enum State {
Conflict = 0,
Unapplied = 1,
Applied = 2,
Partial = 3
};
std::vector<Byte> entries;
bool loadDIF(std::string name);
State checkState(const patch_byte *ptr, size_t len);
void apply(patch_byte *ptr, size_t len, bool newv);
};
inline bool is_hex(char c)
{
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
bool BinaryPatch::loadDIF(std::string name)
{
entries.clear();
std::ifstream infile(name);
if(infile.bad())
{
cerr << "Cannot open file: " << name << endl;
return false;
}
std::string s;
while(std::getline(infile, s))
{
// Parse lines that begin with "[0-9a-f]+:"
size_t idx = s.find(':');
if (idx == std::string::npos || idx == 0 || idx > 8)
continue;
bool ok = true;
for (size_t i = 0; i < idx; i++)
if (!is_hex(s[i]))
ok = false;
if (!ok)
continue;
unsigned off, oval, nval;
int nchar = 0;
int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);
if (cnt < 3)
{
cerr << "Could not parse: " << s << endl;
return false;
}
for (size_t i = nchar; i < s.size(); i++)
{
if (!isspace(s[i]))
{
cerr << "Garbage at end of line: " << s << endl;
return false;
}
}
if (oval >= 256 || nval >= 256)
{
cerr << "Invalid byte values: " << s << endl;
return false;
}
Byte bv = { off, patch_byte(oval), patch_byte(nval) };
entries.push_back(bv);
}
if (entries.empty())
{
cerr << "No lines recognized." << endl;
return false;
}
return true;
}
BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
int state = 0;
for (size_t i = 0; i < entries.size(); i++)
{
Byte &bv = entries[i];
if (bv.offset >= len)
{
cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
return Conflict;
}
patch_byte cv = ptr[bv.offset];
if (bv.old_val == cv)
state |= Unapplied;
else if (bv.new_val == cv)
state |= Applied;
else
{
cerr << std::hex << bv.offset << ": " << bv.old_val << " " << bv.new_val
<< ", but currently " << cv << std::dec << endl;
return Conflict;
}
}
return State(state);
}
void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
for (size_t i = 0; i < entries.size(); i++)
{
Byte &bv = entries[i];
assert (bv.offset < len);
ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
}
}
bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
FILE *f = fopen(fname.c_str(), "rb");
if (!f)
{
cerr << "Cannot open file: " << fname << endl;
return false;
}
fseek(f, 0, SEEK_END);
pvec->resize(ftell(f));
fseek(f, 0, SEEK_SET);
size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
fclose(f);
return cnt == pvec->size();
}
bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
FILE *f = fopen(fname.c_str(), "wb");
if (!f)
{
cerr << "Cannot open file: " << fname << endl;
return false;
}
size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
fclose(f);
return cnt == pvec.size();
}
std::string compute_hash(const std::vector<patch_byte> &pvec)
{
md5wrapper md5;
return md5.getHashFromBytes(pvec.data(), pvec.size());
}
int main (int argc, char *argv[])
{
if (argc <= 3)
{
cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
return 2;
}
std::string cmd = argv[1];
if (cmd != "check" && cmd != "apply" && cmd != "remove")
{
cerr << "Invalid command: " << cmd << endl;
return 2;
}
std::string exe_file = argv[2];
std::vector<patch_byte> bindata;
if (!load_file(&bindata, exe_file))
return 2;
BinaryPatch patch;
if (!patch.loadDIF(argv[3]))
return 2;
BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
if (state == BinaryPatch::Conflict)
return 1;
if (cmd == "check")
{
switch (state)
{
case BinaryPatch::Unapplied:
cout << "Currently not applied." << endl;
break;
case BinaryPatch::Applied:
cout << "Currently applied." << endl;
break;
case BinaryPatch::Partial:
cout << "Currently partially applied." << endl;
break;
default:
break;
}
return 0;
}
else if (cmd == "apply")
{
if (state == BinaryPatch::Applied)
{
cout << "Already applied." << endl;
return 0;
}
patch.apply(bindata.data(), bindata.size(), true);
}
else if (cmd == "remove")
{
if (state == BinaryPatch::Unapplied)
{
cout << "Already removed." << endl;
return 0;
}
patch.apply(bindata.data(), bindata.size(), false);
}
if (!save_file(bindata, exe_file + ".bak"))
{
cerr << "Could not create backup." << endl;
return 1;
}
if (!save_file(bindata, exe_file))
return 1;
cout << "Patched " << patch.entries.size()
<< " bytes, new hash: " << compute_hash(bindata) << endl;
return 0;
}

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -54,7 +54,6 @@ namespace DFHack
{ {
class Process; class Process;
class Module; class Module;
class World;
class Materials; class Materials;
class Notes; class Notes;
struct VersionInfo; struct VersionInfo;
@ -120,8 +119,6 @@ namespace DFHack
/// Is everything OK? /// Is everything OK?
bool isValid(void) { return !errorstate; } bool isValid(void) { return !errorstate; }
/// get the world module
World * getWorld();
/// get the materials module /// get the materials module
Materials * getMaterials(); Materials * getMaterials();
/// get the notes module /// get the notes module
@ -205,7 +202,6 @@ namespace DFHack
// Module storage // Module storage
struct struct
{ {
World * pWorld;
Materials * pMaterials; Materials * pMaterials;
Notes * pNotes; Notes * pNotes;
Graphic * pGraphic; Graphic * pGraphic;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -707,6 +707,9 @@ namespace DFHack {
// Global object pointers // Global object pointers
#include "df/global_objects.h" #include "df/global_objects.h"
#define DF_GLOBAL_VALUE(name,defval) (df::global::name ? *df::global::name : defval)
#define DF_GLOBAL_FIELD(name,fname,defval) (df::global::name ? df::global::name->fname : defval)
// A couple of headers that have to be included at once // A couple of headers that have to be included at once
#include "df/coord2d.h" #include "df/coord2d.h"
#include "df/coord.h" #include "df/coord.h"

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -65,6 +65,7 @@ namespace DFHack
RPCService *owner, const char *name, int flags) RPCService *owner, const char *name, int flags)
: RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1) : RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1)
{} {}
virtual ~ServerFunctionBase() {}
RPCService *owner; RPCService *owner;
int16_t id; int16_t id;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -159,7 +159,7 @@ namespace DFHack
void on_host_delete(virtual_identity *host); void on_host_delete(virtual_identity *host);
VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id); VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id);
void find_child_hosts(virtual_identity *cur, void *vmptr); bool find_child_hosts(virtual_identity *cur, void *vmptr);
public: public:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority); VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
~VMethodInterposeLinkBase(); ~VMethodInterposeLinkBase();

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,5 +1,12 @@
bool empty() const { return x.empty(); }
unsigned size() const { return x.size(); } unsigned size() const { return x.size(); }
void clear() {
x.clear();
y.clear();
z.clear();
}
coord operator[] (unsigned idx) const { coord operator[] (unsigned idx) const {
if (idx >= x.size()) if (idx >= x.size())
return coord(); return coord();

@ -16,7 +16,7 @@ inline bool getassignment( const df::coord2d &xy )
} }
inline bool getassignment( int x, int y ) inline bool getassignment( int x, int y )
{ {
return (bits[y] & (1 << x)); return (bits[(y&15)] & (1 << (x&15)));
} }
inline void setassignment( const df::coord2d &xy, bool bit ) inline void setassignment( const df::coord2d &xy, bool bit )
{ {
@ -25,9 +25,9 @@ inline void setassignment( const df::coord2d &xy, bool bit )
inline void setassignment( int x, int y, bool bit ) inline void setassignment( int x, int y, bool bit )
{ {
if(bit) if(bit)
bits[y] |= (1 << x); bits[(y&15)] |= (1 << (x&15));
else else
bits[y] &= ~(1 << x); bits[(y&15)] &= ~(1 << (x&15));
} }
bool has_assignments() bool has_assignments()
{ {

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mr<EFBFBD>zek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mr<EFBFBD>zek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -40,6 +40,7 @@ distribution.
#include "df/building_actual.h" #include "df/building_actual.h"
#include "df/body_part_raw.h" #include "df/body_part_raw.h"
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/job_item_vector_id.h"
namespace df namespace df
{ {
@ -86,7 +87,8 @@ namespace DFHack
bool find(const std::string &token); bool find(const std::string &token);
bool matches(const df::job_item &item, MaterialInfo *mat = NULL); bool matches(df::job_item_vector_id vec_id);
bool matches(const df::job_item &item, MaterialInfo *mat = NULL, bool skip_vector = false);
}; };
inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) { inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) {
@ -123,6 +125,10 @@ struct dfh_item
namespace Items namespace Items
{ {
DFHACK_EXPORT bool isCasteMaterial(df::item_type itype);
DFHACK_EXPORT int getSubtypeCount(df::item_type itype);
DFHACK_EXPORT df::itemdef *getSubtypeDef(df::item_type itype, int subtype);
/// Look for a particular item by ID /// Look for a particular item by ID
DFHACK_EXPORT df::item * findItemByID(int32_t id); DFHACK_EXPORT df::item * findItemByID(int32_t id);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -32,6 +32,7 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "df/job_item_ref.h" #include "df/job_item_ref.h"
#include "df/item_type.h"
namespace df namespace df
{ {
@ -69,6 +70,9 @@ namespace DFHack
DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item, DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item,
df::job_item_ref::T_role role, df::job_item_ref::T_role role,
int filter_idx = -1, int insert_idx = -1); int filter_idx = -1, int insert_idx = -1);
DFHACK_EXPORT bool isSuitableItem(df::job_item *item, df::item_type itype, int isubtype);
DFHACK_EXPORT bool isSuitableMaterial(df::job_item *item, int mat_type, int mat_index);
} }
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -47,14 +47,6 @@ namespace MapExtras
class DFHACK_EXPORT MapCache; class DFHACK_EXPORT MapCache;
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
return v[p.x&15][p.y&15];
}
inline bool is_valid_tile_coord(df::coord2d p) {
return (p.x & ~15) == 0 && (p.y & ~15) == 0;
}
class Block; class Block;
class BlockInfo class BlockInfo

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -151,6 +151,21 @@ typedef uint8_t biome_indices40d [9];
*/ */
typedef uint16_t t_temperatures [16][16]; typedef uint16_t t_temperatures [16][16];
/**
* Index a tile array by a 2D coordinate, clipping it to mod 16
*/
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
return v[p.x&15][p.y&15];
}
/**
* Check if a 2D coordinate is in the 0-15 range.
*/
inline bool is_valid_tile_coord(df::coord2d p) {
return (p.x & ~15) == 0 && (p.y & ~15) == 0;
}
/** /**
* The Maps module * The Maps module
* \ingroup grp_modules * \ingroup grp_modules

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -128,6 +128,9 @@ namespace DFHack
DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL); DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL);
DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false); DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false);
DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); DFHACK_EXPORT bool isDismissed(df::viewscreen *screen);
/// Retrieve the string representation of the bound key.
DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key);
} }
class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen { class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen {

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -233,6 +233,7 @@ DFHACK_EXPORT bool isDwarf(df::unit *unit);
DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false);
DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false);
DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id); DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id);
DFHACK_EXPORT int computeMovementSpeed(df::unit *unit); DFHACK_EXPORT int computeMovementSpeed(df::unit *unit);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -37,6 +37,12 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
namespace df
{
struct tile_bitmask;
struct map_block;
}
namespace DFHack namespace DFHack
{ {
typedef df::game_mode GameMode; typedef df::game_mode GameMode;
@ -55,8 +61,6 @@ namespace DFHack
class DFContextShared; class DFContextShared;
class DFHACK_EXPORT PersistentDataItem { class DFHACK_EXPORT PersistentDataItem {
friend class World;
int id; int id;
std::string key_value; std::string key_value;
@ -65,13 +69,17 @@ namespace DFHack
public: public:
static const int NumInts = 7; static const int NumInts = 7;
bool isValid() { return id != 0; } bool isValid() const { return id != 0; }
int entry_id() { return -id; } int entry_id() const { return -id; }
const std::string &key() { return key_value; } int raw_id() const { return id; }
const std::string &key() const { return key_value; }
std::string &val() { return *str_value; } std::string &val() { return *str_value; }
const std::string &val() const { return *str_value; }
int &ival(int i) { return int_values[i]; } int &ival(int i) { return int_values[i]; }
int ival(int i) const { return int_values[i]; }
PersistentDataItem() : id(0), str_value(0), int_values(0) {} PersistentDataItem() : id(0), str_value(0), int_values(0) {}
PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv) PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv)
@ -83,54 +91,45 @@ namespace DFHack
* \ingroup grp_modules * \ingroup grp_modules
* \ingroup grp_world * \ingroup grp_world
*/ */
class DFHACK_EXPORT World : public Module namespace World
{ {
public:
World();
~World();
bool Start();
bool Finish();
///true if paused, false if not ///true if paused, false if not
bool ReadPauseState(); DFHACK_EXPORT bool ReadPauseState();
///true if paused, false if not ///true if paused, false if not
void SetPauseState(bool paused); DFHACK_EXPORT void SetPauseState(bool paused);
uint32_t ReadCurrentTick(); DFHACK_EXPORT uint32_t ReadCurrentTick();
uint32_t ReadCurrentYear(); DFHACK_EXPORT uint32_t ReadCurrentYear();
uint32_t ReadCurrentMonth(); DFHACK_EXPORT uint32_t ReadCurrentMonth();
uint32_t ReadCurrentDay(); DFHACK_EXPORT uint32_t ReadCurrentDay();
uint8_t ReadCurrentWeather(); DFHACK_EXPORT uint8_t ReadCurrentWeather();
void SetCurrentWeather(uint8_t weather); DFHACK_EXPORT void SetCurrentWeather(uint8_t weather);
bool ReadGameMode(t_gamemodes& rd); DFHACK_EXPORT bool ReadGameMode(t_gamemodes& rd);
bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous DFHACK_EXPORT bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous
std::string ReadWorldFolder(); DFHACK_EXPORT std::string ReadWorldFolder();
// Store data in fake historical figure names. // Store data in fake historical figure names.
// This ensures that the values are stored in save games. // This ensures that the values are stored in save games.
PersistentDataItem AddPersistentData(const std::string &key); DFHACK_EXPORT PersistentDataItem AddPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(const std::string &key); DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(int entry_id); DFHACK_EXPORT PersistentDataItem GetPersistentData(int entry_id);
// Calls GetPersistentData(key); if not found, adds and sets added to true. // Calls GetPersistentData(key); if not found, adds and sets added to true.
// The result can still be not isValid() e.g. if the world is not loaded. // The result can still be not isValid() e.g. if the world is not loaded.
PersistentDataItem GetPersistentData(const std::string &key, bool *added); DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key, bool *added);
// Lists all items with the given key. // Lists all items with the given key.
// If prefix is true, search for keys starting with key+"/". // If prefix is true, search for keys starting with key+"/".
// GetPersistentData(&vec,"",true) returns all items. // GetPersistentData(&vec,"",true) returns all items.
// Items have alphabetic order by key; same key ordering is undefined. // Items have alphabetic order by key; same key ordering is undefined.
void GetPersistentData(std::vector<PersistentDataItem> *vec, DFHACK_EXPORT void GetPersistentData(std::vector<PersistentDataItem> *vec,
const std::string &key, bool prefix = false); const std::string &key, bool prefix = false);
// Deletes the item; returns true if success. // Deletes the item; returns true if success.
bool DeletePersistentData(const PersistentDataItem &item); DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item);
void ClearPersistentCache(); DFHACK_EXPORT void ClearPersistentCache();
private: DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false);
struct Private; DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block);
Private *d; }
bool BuildPersistentCache();
};
} }
#endif #endif

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -3,16 +3,16 @@
local _ENV = mkmodule('class') local _ENV = mkmodule('class')
-- Metatable template for a class -- Metatable template for a class
class_obj = {} or class_obj class_obj = class_obj or {}
-- Methods shared by all classes -- Methods shared by all classes
common_methods = {} or common_methods common_methods = common_methods or {}
-- Forbidden names for class fields and methods. -- Forbidden names for class fields and methods.
reserved_names = { super = true, ATTRS = true } reserved_names = { super = true, ATTRS = true }
-- Attribute table metatable -- Attribute table metatable
attrs_meta = {} or attrs_meta attrs_meta = attrs_meta or {}
-- Create or updates a class; a class has metamethods and thus own metatable. -- Create or updates a class; a class has metamethods and thus own metatable.
function defclass(class,parent) function defclass(class,parent)
@ -133,6 +133,14 @@ function common_methods:callback(method, ...)
return dfhack.curry(self[method], self, ...) return dfhack.curry(self[method], self, ...)
end end
function common_methods:cb_getfield(field)
return function() return self[field] end
end
function common_methods:cb_setfield(field)
return function(val) self[field] = val end
end
function common_methods:assign(data) function common_methods:assign(data)
for k,v in pairs(data) do for k,v in pairs(data) do
self[k] = v self[k] = v

@ -125,6 +125,10 @@ end
-- Misc functions -- Misc functions
NEWLINE = "\n"
COMMA = ","
PERIOD = "."
function printall(table) function printall(table)
local ok,f,t,k = pcall(pairs,table) local ok,f,t,k = pcall(pairs,table)
if ok then if ok then
@ -157,6 +161,14 @@ function xyz2pos(x,y,z)
end end
end end
function same_xyz(a,b)
return a and b and a.x == b.x and a.y == b.y and a.z == b.z
end
function get_path_xyz(path,i)
return path.x[i], path.y[i], path.z[i]
end
function pos2xy(pos) function pos2xy(pos)
if pos then if pos then
local x = pos.x local x = pos.x
@ -174,6 +186,14 @@ function xy2pos(x,y)
end end
end end
function same_xy(a,b)
return a and b and a.x == b.x and a.y == b.y
end
function get_path_xy(path,i)
return path.x[i], path.y[i]
end
function safe_index(obj,idx,...) function safe_index(obj,idx,...)
if obj == nil or idx == nil then if obj == nil or idx == nil then
return nil return nil

@ -48,17 +48,75 @@ end
function mkdims_wh(x1,y1,w,h) function mkdims_wh(x1,y1,w,h)
return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h } return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
end end
function inset(rect,dx1,dy1,dx2,dy2)
return mkdims_xy(
rect.x1+dx1, rect.y1+dy1,
rect.x2-(dx2 or dx1), rect.y2-(dy2 or dy1)
)
end
function is_in_rect(rect,x,y) function is_in_rect(rect,x,y)
return x and y and x >= rect.x1 and x <= rect.x2 and y >= rect.y1 and y <= rect.y2 return x and y and x >= rect.x1 and x <= rect.x2 and y >= rect.y1 and y <= rect.y2
end end
local function to_pen(default, pen, bg, bold) local function align_coord(gap,align,lv,rv)
if gap <= 0 then
return 0
end
if not align then
if rv and not lv then
align = 1.0
elseif lv and not rv then
align = 0.0
else
align = 0.5
end
end
return math.floor(gap*align)
end
function compute_frame_rect(wavail,havail,spec,xgap,ygap)
if not spec then
return mkdims_wh(0,0,wavail,havail)
end
local sw = wavail - (spec.l or 0) - (spec.r or 0)
local sh = havail - (spec.t or 0) - (spec.b or 0)
local rqw = math.min(sw, (spec.w or sw)+xgap)
local rqh = math.min(sh, (spec.h or sh)+ygap)
local ax = align_coord(sw - rqw, spec.xalign, spec.l, spec.r)
local ay = align_coord(sh - rqh, spec.yalign, spec.t, spec.b)
local rect = mkdims_wh((spec.l or 0) + ax, (spec.t or 0) + ay, rqw, rqh)
rect.wgap = sw - rqw
rect.hgap = sh - rqh
return rect
end
local function parse_inset(inset)
local l,r,t,b
if type(inset) == 'table' then
l,r = inset.l or inset.x, inset.r or inset.x
t,b = inset.t or inset.y, inset.b or inset.y
else
l = inset or 0
t,r,b = l,l,l
end
return l,r,t,b
end
function inset_frame(rect, inset, gap)
gap = gap or 0
local l,t,r,b = parse_inset(inset)
return mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
end
function compute_frame_body(wavail, havail, spec, inset, gap)
gap = gap or 0
local l,t,r,b = parse_inset(inset)
local rect = compute_frame_rect(wavail, havail, spec, gap*2+l+r, gap*2+t+b)
local body = mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
return rect, body
end
function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end
function to_pen(default, pen, bg, bold)
if pen == nil then if pen == nil then
return default or {} return default or {}
elseif type(pen) ~= 'table' then elseif type(pen) ~= 'table' then
@ -68,37 +126,68 @@ local function to_pen(default, pen, bg, bold)
end end
end end
---------------------------- function getKeyDisplay(code)
-- Clipped painter object -- if type(code) == 'string' then
---------------------------- code = df.interface_key[code]
end
return dscreen.getKeyDisplay(code)
end
Painter = defclass(Painter, nil) -----------------------------------
-- Clipped view rectangle object --
-----------------------------------
function Painter:init(args) ViewRect = defclass(ViewRect, nil)
local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize())
local crect = args.clip_rect or rect function ViewRect:init(args)
self:assign{ if args.view_rect then
x = rect.x1, y = rect.y1, self:assign(args.view_rect)
x1 = rect.x1, clip_x1 = crect.x1, else
y1 = rect.y1, clip_y1 = crect.y1, local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize())
x2 = rect.x2, clip_x2 = crect.x2, local crect = args.clip_rect or rect
y2 = rect.y2, clip_y2 = crect.y2, self:assign{
width = rect.x2-rect.x1+1, x1 = rect.x1, clip_x1 = crect.x1,
height = rect.y2-rect.y1+1, y1 = rect.y1, clip_y1 = crect.y1,
cur_pen = to_pen(nil, args.pen or COLOR_GREY) x2 = rect.x2, clip_x2 = crect.x2,
} y2 = rect.y2, clip_y2 = crect.y2,
width = rect.x2-rect.x1+1,
height = rect.y2-rect.y1+1,
}
end
if args.clip_view then
local cr = args.clip_view
self:assign{
clip_x1 = math.max(self.clip_x1, cr.clip_x1),
clip_y1 = math.max(self.clip_y1, cr.clip_y1),
clip_x2 = math.min(self.clip_x2, cr.clip_x2),
clip_y2 = math.min(self.clip_y2, cr.clip_y2),
}
end
end end
function Painter.new(rect, pen) function ViewRect:isDefunct()
return Painter{ rect = rect, pen = pen } return (self.clip_x1 > self.clip_x2 or self.clip_y1 > self.clip_y2)
end end
function Painter:isValidPos() function ViewRect:inClipGlobalXY(x,y)
return self.x >= self.clip_x1 and self.x <= self.clip_x2 return x >= self.clip_x1 and x <= self.clip_x2
and self.y >= self.clip_y1 and self.y <= self.clip_y2 and y >= self.clip_y1 and y <= self.clip_y2
end end
function Painter:viewport(x,y,w,h) function ViewRect:inClipLocalXY(x,y)
return (x+self.x1) >= self.clip_x1 and (x+self.x1) <= self.clip_x2
and (y+self.y1) >= self.clip_y1 and (y+self.y1) <= self.clip_y2
end
function ViewRect:localXY(x,y)
return x-self.x1, y-self.y1
end
function ViewRect:globalXY(x,y)
return x+self.x1, y+self.y1
end
function ViewRect:viewport(x,y,w,h)
if type(x) == 'table' then if type(x) == 'table' then
x,y,w,h = x.x1, x.y1, x.width, x.height x,y,w,h = x.x1, x.y1, x.width, x.height
end end
@ -113,17 +202,57 @@ function Painter:viewport(x,y,w,h)
clip_y1 = math.max(self.clip_y1, y1), clip_y1 = math.max(self.clip_y1, y1),
clip_x2 = math.min(self.clip_x2, x2), clip_x2 = math.min(self.clip_x2, x2),
clip_y2 = math.min(self.clip_y2, y2), clip_y2 = math.min(self.clip_y2, y2),
-- Pen
cur_pen = self.cur_pen
} }
return mkinstance(ViewRect, vp)
end
----------------------------
-- Clipped painter object --
----------------------------
Painter = defclass(Painter, ViewRect)
function Painter:init(args)
self.x = self.x1
self.y = self.y1
self.cur_pen = to_pen(nil, args.pen or COLOR_GREY)
end
function Painter.new(rect, pen)
return Painter{ rect = rect, pen = pen }
end
function Painter.new_view(view_rect, pen)
return Painter{ view_rect = view_rect, pen = pen }
end
function Painter.new_xy(x1,y1,x2,y2,pen)
return Painter{ rect = mkdims_xy(x1,y1,x2,y2), pen = pen }
end
function Painter.new_wh(x,y,w,h,pen)
return Painter{ rect = mkdims_wh(x,y,w,h), pen = pen }
end
function Painter:isValidPos()
return self:inClipGlobalXY(self.x, self.y)
end
function Painter:viewport(x,y,w,h)
local vp = ViewRect.viewport(x,y,w,h)
vp.cur_pen = self.cur_pen
return mkinstance(Painter, vp):seek(0,0) return mkinstance(Painter, vp):seek(0,0)
end end
function Painter:localX() function Painter:cursor()
return self.x - self.x1, self.y - self.y1
end
function Painter:cursorX()
return self.x - self.x1 return self.x - self.x1
end end
function Painter:localY() function Painter:cursorY()
return self.y - self.y1 return self.y - self.y1
end end
@ -210,16 +339,150 @@ function Painter:string(text,pen,...)
return self:advance(#text, nil) return self:advance(#text, nil)
end end
function Painter:key(code,pen,bg,...)
return self:string(
getKeyDisplay(code),
pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ...
)
end
--------------------------
-- Abstract view object --
--------------------------
View = defclass(View)
View.ATTRS {
active = true,
visible = true,
view_id = DEFAULT_NIL,
}
function View:init(args)
self.subviews = {}
end
function View:addviews(list)
if not list then return end
local sv = self.subviews
for _,obj in ipairs(list) do
table.insert(sv, obj)
local id = obj.view_id
if id and type(id) ~= 'number' and sv[id] == nil then
sv[id] = obj
end
end
for _,dir in ipairs(list) do
for id,obj in pairs(dir.subviews) do
if id and type(id) ~= 'number' and sv[id] == nil then
sv[id] = obj
end
end
end
end
function View:getWindowSize()
local rect = self.frame_body
return rect.width, rect.height
end
function View:getMousePos()
local rect = self.frame_body
local x,y = dscreen.getMousePos()
if rect and rect:inClipGlobalXY(x,y) then
return rect:localXY(x,y)
end
end
function View:computeFrame(parent_rect)
return mkdims_wh(0,0,parent_rect.width,parent_rect.height)
end
function View:updateSubviewLayout(frame_body)
for _,child in ipairs(self.subviews) do
child:updateLayout(frame_body)
end
end
function View:updateLayout(parent_rect)
if not parent_rect then
parent_rect = self.frame_parent_rect
else
self.frame_parent_rect = parent_rect
end
self:invoke_before('preUpdateLayout', parent_rect)
local frame_rect,body_rect = self:computeFrame(parent_rect)
self.frame_rect = frame_rect
self.frame_body = parent_rect:viewport(body_rect or frame_rect)
self:invoke_after('postComputeFrame', self.frame_body)
self:updateSubviewLayout(self.frame_body)
self:invoke_after('postUpdateLayout', self.frame_body)
end
function View:renderSubviews(dc)
for _,child in ipairs(self.subviews) do
if child.visible then
child:render(dc)
end
end
end
function View:render(dc)
self:onRenderFrame(dc, self.frame_rect)
local sub_dc = Painter{
view_rect = self.frame_body,
clip_view = dc
}
self:onRenderBody(sub_dc)
self:renderSubviews(sub_dc)
end
function View:onRenderFrame(dc,rect)
end
function View:onRenderBody(dc)
end
function View:inputToSubviews(keys)
local children = self.subviews
for i=#children,1,-1 do
local child = children[i]
if child.visible and child.active and child:onInput(keys) then
return true
end
end
return false
end
function View:onInput(keys)
return self:inputToSubviews(keys)
end
------------------------ ------------------------
-- Base screen object -- -- Base screen object --
------------------------ ------------------------
Screen = defclass(Screen) Screen = defclass(Screen, View)
Screen.text_input_mode = false Screen.text_input_mode = false
function Screen:postinit() function Screen:postinit()
self:updateLayout() self:onResize(dscreen.getWindowSize())
end end
Screen.isDismissed = dscreen.isDismissed Screen.isDismissed = dscreen.isDismissed
@ -236,14 +499,6 @@ function Screen:invalidate()
dscreen.invalidate() dscreen.invalidate()
end end
function Screen:getWindowSize()
return dscreen.getWindowSize()
end
function Screen:getMousePos()
return dscreen.getMousePos()
end
function Screen:renderParent() function Screen:renderParent()
if self._native and self._native.parent then if self._native and self._native.parent then
self._native.parent:render() self._native.parent:render()
@ -272,7 +527,7 @@ function Screen:onAboutToShow()
end end
function Screen:onShow() function Screen:onShow()
self:updateLayout() self:onResize(dscreen.getWindowSize())
end end
function Screen:dismiss() function Screen:dismiss()
@ -288,10 +543,11 @@ function Screen:onDestroy()
end end
function Screen:onResize(w,h) function Screen:onResize(w,h)
self:updateLayout() self:updateLayout(ViewRect{ rect = mkdims_wh(0,0,w,h) })
end end
function Screen:updateLayout() function Screen:onRender()
self:render(Painter.new())
end end
------------------------ ------------------------
@ -353,68 +609,31 @@ FramedScreen.ATTRS{
frame_title = DEFAULT_NIL, frame_title = DEFAULT_NIL,
frame_width = DEFAULT_NIL, frame_width = DEFAULT_NIL,
frame_height = DEFAULT_NIL, frame_height = DEFAULT_NIL,
frame_inset = 0,
frame_background = CLEAR_PEN,
} }
local function hint_coord(gap,hint)
if hint and hint > 0 then
return math.min(hint,gap)
elseif hint and hint < 0 then
return math.max(0,gap-hint)
else
return math.floor(gap/2)
end
end
function FramedScreen:getWantedFrameSize() function FramedScreen:getWantedFrameSize()
return self.frame_width, self.frame_height return self.frame_width, self.frame_height
end end
function FramedScreen:updateFrameSize() function FramedScreen:computeFrame(parent_rect)
local sw, sh = dscreen.getWindowSize() local sw, sh = parent_rect.width, parent_rect.height
local iw, ih = sw-2, sh-2 local fw, fh = self:getWantedFrameSize(parent_rect)
local fw, fh = self:getWantedFrameSize() return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1)
local width = math.min(fw or iw, iw)
local height = math.min(fh or ih, ih)
local gw, gh = iw-width, ih-height
local x1, y1 = hint_coord(gw,self.frame_xhint), hint_coord(gh,self.frame_yhint)
self.frame_rect = mkdims_wh(x1+1,y1+1,width,height)
self.frame_opaque = (gw == 0 and gh == 0)
end
function FramedScreen:updateLayout()
self:updateFrameSize()
end end
function FramedScreen:getWindowSize() function FramedScreen:onRenderFrame(dc, rect)
local rect = self.frame_rect local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2
return rect.width, rect.height
end
function FramedScreen:getMousePos() if rect.wgap <= 0 and rect.hgap <= 0 then
local rect = self.frame_rect dc:clear()
local x,y = dscreen.getMousePos()
if is_in_rect(rect,x,y) then
return x-rect.x1, y-rect.y1
end
end
function FramedScreen:onRender()
local rect = self.frame_rect
local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1
if self.frame_opaque then
dscreen.clear()
else else
self:renderParent() self:renderParent()
dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2) dc:fill(rect, self.frame_background)
end end
paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title) paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title)
self:onRenderBody(Painter.new(rect))
end
function FramedScreen:onRenderBody(dc)
end end
return _ENV return _ENV

@ -3,6 +3,7 @@
local _ENV = mkmodule('gui.dialogs') local _ENV = mkmodule('gui.dialogs')
local gui = require('gui') local gui = require('gui')
local widgets = require('gui.widgets')
local utils = require('utils') local utils = require('utils')
local dscreen = dfhack.screen local dscreen = dfhack.screen
@ -13,48 +14,35 @@ MessageBox.focus_path = 'MessageBox'
MessageBox.ATTRS{ MessageBox.ATTRS{
frame_style = gui.GREY_LINE_FRAME, frame_style = gui.GREY_LINE_FRAME,
frame_inset = 1,
-- new attrs -- new attrs
text = {},
on_accept = DEFAULT_NIL, on_accept = DEFAULT_NIL,
on_cancel = DEFAULT_NIL, on_cancel = DEFAULT_NIL,
on_close = DEFAULT_NIL, on_close = DEFAULT_NIL,
text_pen = DEFAULT_NIL,
} }
function MessageBox:preinit(info) function MessageBox:init(info)
if type(info.text) == 'string' then self:addviews{
info.text = utils.split_string(info.text, "\n") widgets.Label{
end view_id = 'label',
text = info.text,
text_pen = info.text_pen,
frame = { l = 0, t = 0 },
auto_height = true
}
}
end end
function MessageBox:getWantedFrameSize() function MessageBox:getWantedFrameSize()
local text = self.text local label = self.subviews.label
local w = #(self.frame_title or '') + 4 local width = math.max(self.frame_width or 0, 20, #(self.frame_title or '') + 4)
w = math.max(w, 20) return math.max(width, label:getTextWidth()), label:getTextHeight()
w = math.max(self.frame_width or w, w)
for _, l in ipairs(text) do
w = math.max(w, #l)
end
local h = #text+1
if h > 1 then
h = h+1
end
return w+2, #text+2
end end
function MessageBox:onRenderBody(dc) function MessageBox:onRenderFrame(dc,rect)
if #self.text > 0 then MessageBox.super.onRenderFrame(self,dc,rect)
dc:newline(1):pen(self.text_pen or COLOR_GREY)
for _, l in ipairs(self.text or {}) do
dc:string(l):newline(1)
end
end
if self.on_accept then if self.on_accept then
local x,y = self.frame_rect.x1+1, self.frame_rect.y2+1 dc:seek(rect.x1+2,rect.y2):key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM')
dscreen.paintString({fg=COLOR_LIGHTGREEN},x,y,'ESC')
dscreen.paintString({fg=COLOR_GREY},x+3,y,'/')
dscreen.paintString({fg=COLOR_LIGHTGREEN},x+4,y,'y')
end end
end end
@ -75,6 +63,8 @@ function MessageBox:onInput(keys)
if self.on_cancel then if self.on_cancel then
self.on_cancel() self.on_cancel()
end end
else
self:inputToSubviews(keys)
end end
end end
@ -102,8 +92,6 @@ InputBox = defclass(InputBox, MessageBox)
InputBox.focus_path = 'InputBox' InputBox.focus_path = 'InputBox'
InputBox.ATTRS{ InputBox.ATTRS{
input = '',
input_pen = DEFAULT_NIL,
on_input = DEFAULT_NIL, on_input = DEFAULT_NIL,
} }
@ -111,46 +99,36 @@ function InputBox:preinit(info)
info.on_accept = nil info.on_accept = nil
end end
function InputBox:init(info)
self:addviews{
widgets.EditField{
view_id = 'edit',
text = info.input,
text_pen = info.input_pen,
frame = { l = 0, r = 0, h = 1 },
}
}
end
function InputBox:getWantedFrameSize() function InputBox:getWantedFrameSize()
local mw, mh = InputBox.super.getWantedFrameSize(self) local mw, mh = InputBox.super.getWantedFrameSize(self)
self.subviews.edit.frame.t = mh+1
return mw, mh+2 return mw, mh+2
end end
function InputBox:onRenderBody(dc)
InputBox.super.onRenderBody(self, dc)
dc:newline(1)
dc:pen(self.input_pen or COLOR_LIGHTCYAN)
dc:fill(1,dc:localY(),dc.width-2,dc:localY())
local cursor = '_'
if math.floor(dfhack.getTickCount()/300) % 2 == 0 then
cursor = ' '
end
local txt = self.input .. cursor
if #txt > dc.width-2 then
txt = string.char(27)..string.sub(txt, #txt-dc.width+4)
end
dc:string(txt)
end
function InputBox:onInput(keys) function InputBox:onInput(keys)
if keys.SELECT then if keys.SELECT then
self:dismiss() self:dismiss()
if self.on_input then if self.on_input then
self.on_input(self.input) self.on_input(self.subviews.edit.text)
end end
elseif keys.LEAVESCREEN then elseif keys.LEAVESCREEN then
self:dismiss() self:dismiss()
if self.on_cancel then if self.on_cancel then
self.on_cancel() self.on_cancel()
end end
elseif keys._STRING then else
if keys._STRING == 0 then self:inputToSubviews(keys)
self.input = string.sub(self.input, 1, #self.input-1)
else
self.input = self.input .. string.char(keys._STRING)
end
end end
end end
@ -171,97 +149,72 @@ ListBox = defclass(ListBox, MessageBox)
ListBox.focus_path = 'ListBox' ListBox.focus_path = 'ListBox'
ListBox.ATTRS{ ListBox.ATTRS{
selection = 0, with_filter = false,
choices = {}, cursor_pen = DEFAULT_NIL,
select_pen = DEFAULT_NIL, select_pen = DEFAULT_NIL,
on_input = DEFAULT_NIL on_select = DEFAULT_NIL
} }
function InputBox:preinit(info) function ListBox:preinit(info)
info.on_accept = nil info.on_accept = nil
end end
function ListBox:init(info) function ListBox:init(info)
self.page_top = 0 local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false)
end local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
function ListBox:getWantedFrameSize()
local mw, mh = ListBox.super.getWantedFrameSize(self)
return mw, mh+#self.choices
end
function ListBox:onRenderBody(dc)
ListBox.super.onRenderBody(self, dc)
dc:newline(1)
if self.selection>dc.height-3 then local list_widget = widgets.List
self.page_top=self.selection-(dc.height-3) if self.with_filter then
elseif self.selection<self.page_top and self.selection >0 then list_widget = widgets.FilteredList
self.page_top=self.selection-1
end
for i,entry in ipairs(self.choices) do
if type(entry)=="table" then
entry=entry[1]
end
if i>self.page_top then
if i == self.selection then
dc:pen(self.select_pen or COLOR_LIGHTCYAN)
else
dc:pen(self.text_pen or COLOR_GREY)
end
dc:string(entry)
dc:newline(1)
end
end end
self:addviews{
list_widget{
view_id = 'list',
selected = info.selected,
choices = info.choices,
icon_width = info.icon_width,
text_pen = spen,
cursor_pen = cpen,
on_submit = function(sel,obj)
self:dismiss()
if self.on_select then self.on_select(sel, obj) end
local cb = obj.on_select or obj[2]
if cb then cb(obj, sel) end
end,
frame = { l = 0, r = 0 },
}
}
end end
function ListBox:moveCursor(delta) function ListBox:getWantedFrameSize()
local newsel=self.selection+delta local mw, mh = InputBox.super.getWantedFrameSize(self)
if #self.choices ~=0 then local list = self.subviews.list
if newsel<1 or newsel>#self.choices then list.frame.t = mh+1
newsel=newsel % #self.choices return math.max(mw, list:getContentWidth()), mh+1+math.min(20,list:getContentHeight())
end
end
self.selection=newsel
end end
function ListBox:onInput(keys) function ListBox:onInput(keys)
if keys.SELECT then if keys.LEAVESCREEN then
self:dismiss()
local choice=self.choices[self.selection]
if self.on_input then
self.on_input(self.selection,choice)
end
if choice and choice[2] then
choice[2](choice,self.selection) -- maybe reverse the arguments?
end
elseif keys.LEAVESCREEN then
self:dismiss() self:dismiss()
if self.on_cancel then if self.on_cancel then
self.on_cancel() self.on_cancel()
end end
elseif keys.CURSOR_UP then else
self:moveCursor(-1) self:inputToSubviews(keys)
elseif keys.CURSOR_DOWN then
self:moveCursor(1)
elseif keys.CURSOR_UP_FAST then
self:moveCursor(-10)
elseif keys.CURSOR_DOWN_FAST then
self:moveCursor(10)
end end
end end
function showListPrompt(title, text, tcolor, choices, on_input, on_cancel, min_width) function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width, filter)
ListBox{ ListBox{
frame_title = title, frame_title = title,
text = text, text = text,
text_pen = tcolor, text_pen = tcolor,
choices = choices, choices = choices,
on_input = on_input, on_select = on_select,
on_cancel = on_cancel, on_cancel = on_cancel,
frame_width = min_width, frame_width = min_width,
with_filter = filter,
}:show() }:show()
end end

@ -238,6 +238,19 @@ local function get_movement_delta(key, delta, big_step)
end end
end end
HOTKEY_KEYS = {}
for i,v in ipairs(df.global.ui.main.hotkeys) do
HOTKEY_KEYS['D_HOTKEY'..(i+1)] = v
end
local function get_hotkey_target(key)
local hk = HOTKEY_KEYS[key]
if hk and hk.cmd == df.ui_hotkey.T_cmd.Zoom then
return xyz2pos(hk.x, hk.y, hk.z)
end
end
function Viewport:scrollByKey(key) function Viewport:scrollByKey(key)
local dx, dy, dz = get_movement_delta(key, 10, 20) local dx, dy, dz = get_movement_delta(key, 10, 20)
if dx then if dx then
@ -247,14 +260,20 @@ function Viewport:scrollByKey(key)
self.z + dz self.z + dz
) )
else else
return self local hk_pos = get_hotkey_target(key)
if hk_pos then
return self:centerOn(hk_pos)
else
return self
end
end end
end end
DwarfOverlay = defclass(DwarfOverlay, gui.Screen) DwarfOverlay = defclass(DwarfOverlay, gui.Screen)
function DwarfOverlay:updateLayout() function DwarfOverlay:updateLayout(parent_rect)
self.df_layout = getPanelLayout() self.df_layout = getPanelLayout()
DwarfOverlay.super.updateLayout(self, parent_rect)
end end
function DwarfOverlay:getViewport(old_vp) function DwarfOverlay:getViewport(old_vp)
@ -285,9 +304,11 @@ function DwarfOverlay:selectBuilding(building,cursor,viewport,gap)
end end
function DwarfOverlay:propagateMoveKeys(keys) function DwarfOverlay:propagateMoveKeys(keys)
for code,_ in pairs(MOVEMENT_KEYS) do for code,_ in pairs(keys) do
if keys[code] then if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then
self:sendInputToParent(code) if not HOTKEY_KEYS[code] or get_hotkey_target(code) then
self:sendInputToParent(code)
end
return code return code
end end
end end
@ -304,8 +325,8 @@ function DwarfOverlay:simulateViewScroll(keys, anchor, no_clip_cursor)
return 'A_MOVE_SAME_SQUARE' return 'A_MOVE_SAME_SQUARE'
end end
for code,_ in pairs(MOVEMENT_KEYS) do for code,_ in pairs(keys) do
if keys[code] then if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then
local vp = self:getViewport():scrollByKey(code) local vp = self:getViewport():scrollByKey(code)
if (cursor and not no_clip_cursor) or no_clip_cursor == false then if (cursor and not no_clip_cursor) or no_clip_cursor == false then
vp = vp:reveal(anchor,4,20,4,true) vp = vp:reveal(anchor,4,20,4,true)
@ -328,16 +349,26 @@ function DwarfOverlay:simulateCursorMovement(keys, anchor)
return 'A_MOVE_SAME_SQUARE' return 'A_MOVE_SAME_SQUARE'
end end
for code,_ in pairs(MOVEMENT_KEYS) do for code,_ in pairs(keys) do
if keys[code] then if MOVEMENT_KEYS[code] then
local dx, dy, dz = get_movement_delta(code, 1, 10) local dx, dy, dz = get_movement_delta(code, 1, 10)
local ncur = xyz2pos(cx+dx, cy+dy, cz+dz) local ncur = xyz2pos(cx+dx, cy+dy, cz+dz)
if dfhack.maps.isValidTilePos(ncur) then if dfhack.maps.isValidTilePos(ncur) then
setCursorPos(ncur) setCursorPos(ncur)
self:getViewport():reveal(ncur,4,10,6,true):set() self:getViewport():reveal(ncur,4,10,6,true):set()
return code
end end
return code
elseif HOTKEY_KEYS[code] then
local pos = get_hotkey_target(code)
if pos and dfhack.maps.isValidTilePos(pos) then
setCursorPos(pos)
self:getViewport():centerOn(pos):set()
end
return code
end end
end end
end end
@ -352,13 +383,14 @@ end
MenuOverlay = defclass(MenuOverlay, DwarfOverlay) MenuOverlay = defclass(MenuOverlay, DwarfOverlay)
function MenuOverlay:updateLayout() MenuOverlay.ATTRS {
MenuOverlay.super.updateLayout(self) frame_inset = 0,
self.frame_rect = self.df_layout.menu frame_background = CLEAR_PEN,
end }
MenuOverlay.getWindowSize = gui.FramedScreen.getWindowSize function MenuOverlay:computeFrame(parent_rect)
MenuOverlay.getMousePos = gui.FramedScreen.getMousePos return self.df_layout.menu, gui.inset_frame(self.df_layout.menu, self.frame_inset)
end
function MenuOverlay:onAboutToShow(below) function MenuOverlay:onAboutToShow(below)
MenuOverlay.super.onAboutToShow(self,below) MenuOverlay.super.onAboutToShow(self,below)
@ -369,7 +401,7 @@ function MenuOverlay:onAboutToShow(below)
end end
end end
function MenuOverlay:onRender() function MenuOverlay:render(dc)
self:renderParent() self:renderParent()
local menu = self.df_layout.menu local menu = self.df_layout.menu
@ -380,7 +412,11 @@ function MenuOverlay:onRender()
menu.x1+1, menu.y2+1, "DFHack" menu.x1+1, menu.y2+1, "DFHack"
) )
self:onRenderBody(gui.Painter.new(menu)) if self.frame_background then
dc:fill(menu, self.frame_background)
end
MenuOverlay.super.render(self, dc)
end end
end end

@ -0,0 +1,329 @@
-- Stock dialog for selecting materials
local _ENV = mkmodule('gui.materials')
local gui = require('gui')
local widgets = require('gui.widgets')
local dlg = require('gui.dialogs')
local utils = require('utils')
ARROW = string.char(26)
CREATURE_BASE = 19
PLANT_BASE = 419
MaterialDialog = defclass(MaterialDialog, gui.FramedScreen)
MaterialDialog.focus_path = 'MaterialDialog'
MaterialDialog.ATTRS{
prompt = 'Type or select a material from this list',
frame_style = gui.GREY_LINE_FRAME,
frame_inset = 1,
frame_title = 'Select Material',
-- new attrs
none_caption = 'none',
use_inorganic = true,
use_creature = true,
use_plant = true,
mat_filter = DEFAULT_NIL,
on_select = DEFAULT_NIL,
on_cancel = DEFAULT_NIL,
on_close = DEFAULT_NIL,
}
function MaterialDialog:init(info)
self:addviews{
widgets.Label{
text = {
self.prompt, '\n\n',
'Category: ', { text = self:cb_getfield('context_str'), pen = COLOR_CYAN }
},
text_pen = COLOR_WHITE,
frame = { l = 0, t = 0 },
},
widgets.Label{
view_id = 'back',
visible = false,
text = { { key = 'LEAVESCREEN', text = ': Back' } },
frame = { r = 0, b = 0 },
auto_width = true,
},
widgets.FilteredList{
view_id = 'list',
not_found_label = 'No matching materials',
frame = { l = 0, r = 0, t = 4, b = 2 },
icon_width = 2,
on_submit = self:callback('onSubmitItem'),
},
widgets.Label{
text = { {
key = 'SELECT', text = ': Select',
disabled = function() return not self.subviews.list:canSubmit() end
} },
frame = { l = 0, b = 0 },
}
}
self:initBuiltinMode()
end
function MaterialDialog:getWantedFrameSize(rect)
return math.max(40, #self.prompt), math.min(28, rect.height-8)
end
function MaterialDialog:onDestroy()
if self.on_close then
self.on_close()
end
end
function MaterialDialog:initBuiltinMode()
local choices = {
{ text = self.none_caption, mat_type = -1, mat_index = -1 },
}
if self.use_inorganic then
table.insert(choices, {
icon = ARROW, text = 'inorganic', key = 'CUSTOM_SHIFT_I',
cb = self:callback('initInorganicMode')
})
end
if self.use_creature then
table.insert(choices, {
icon = ARROW, text = 'creature', key = 'CUSTOM_SHIFT_C',
cb = self:callback('initCreatureMode')
})
end
if self.use_plant then
table.insert(choices, {
icon = ARROW, text = 'plant', key = 'CUSTOM_SHIFT_P',
cb = self:callback('initPlantMode')
})
end
local table = df.global.world.raws.mat_table.builtin
for i=0,df.builtin_mats._last_item do
self:addMaterial(choices, table[i], i, -1, false, nil)
end
self:pushContext('Any material', choices)
end
function MaterialDialog:initInorganicMode()
local choices = {}
for i,mat in ipairs(df.global.world.raws.inorganics) do
self:addMaterial(choices, mat.material, 0, i, false, mat)
end
self:pushContext('Inorganic materials', choices)
end
function MaterialDialog:initCreatureMode()
local choices = {}
for i,v in ipairs(df.global.world.raws.creatures.all) do
self:addObjectChoice(choices, v, v.name[0], CREATURE_BASE, i)
end
self:pushContext('Creature materials', choices)
end
function MaterialDialog:initPlantMode()
local choices = {}
for i,v in ipairs(df.global.world.raws.plants.all) do
self:addObjectChoice(choices, v, v.name, PLANT_BASE, i)
end
self:pushContext('Plant materials', choices)
end
function MaterialDialog:addObjectChoice(choices, obj, name, typ, index)
-- Check if any eligible children
local count = #obj.material
local idx = 0
if self.mat_filter then
count = 0
for i,v in ipairs(obj.material) do
if self.mat_filter(v, obj, typ+i, index) then
count = count + 1
if count > 1 then break end
idx = i
end
end
end
-- Create an entry
if count < 1 then
return
elseif count == 1 then
self:addMaterial(choices, obj.material[idx], typ+idx, index, true, obj)
else
table.insert(choices, {
icon = ARROW, text = name, mat_type = typ, mat_index = index,
obj = obj, cb = self:callback('onSelectObj')
})
end
end
function MaterialDialog:onSelectObj(item)
local choices = {}
for i,v in ipairs(item.obj.material) do
self:addMaterial(choices, v, item.mat_type+i, item.mat_index, false, item.obj)
end
self:pushContext(item.text, choices)
end
function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix, parent)
-- Check the filter
if self.mat_filter and not self.mat_filter(mat, parent, typ, idx) then
return
end
-- Find the material name
local state = 0
if mat.heat.melting_point <= 10015 then
state = 1
end
local name = mat.state_name[state]
name = string.gsub(name, '^frozen ','')
name = string.gsub(name, '^molten ','')
name = string.gsub(name, '^condensed ','')
-- Add prefix if requested
local key
if pfix and mat.prefix ~= '' then
name = mat.prefix .. ' ' .. name
key = mat.prefix
end
table.insert(choices, {
text = name,
search_key = key,
material = mat,
mat_type = typ, mat_index = idx
})
end
function MaterialDialog:pushContext(name, choices)
if not self.back_stack then
self.back_stack = {}
self.subviews.back.visible = false
else
table.insert(self.back_stack, {
context_str = self.context_str,
all_choices = self.subviews.list:getChoices(),
edit_text = self.subviews.list:getFilter(),
selected = self.subviews.list:getSelected(),
})
self.subviews.back.visible = true
end
self.context_str = name
self.subviews.list:setChoices(choices, 1)
end
function MaterialDialog:onGoBack()
local save = table.remove(self.back_stack)
self.subviews.back.visible = (#self.back_stack > 0)
self.context_str = save.context_str
self.subviews.list:setChoices(save.all_choices)
self.subviews.list:setFilter(save.edit_text, save.selected)
end
function MaterialDialog:submitMaterial(typ, index)
self:dismiss()
if self.on_select then
self.on_select(typ, index)
end
end
function MaterialDialog:onSubmitItem(idx, item)
if item.cb then
item:cb(idx)
else
self:submitMaterial(item.mat_type, item.mat_index)
end
end
function MaterialDialog:onInput(keys)
if keys.LEAVESCREEN or keys.LEAVESCREEN_ALL then
if self.subviews.back.visible and not keys.LEAVESCREEN_ALL then
self:onGoBack()
else
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
end
else
self:inputToSubviews(keys)
end
end
function showMaterialPrompt(title, prompt, on_select, on_cancel, mat_filter)
MaterialDialog{
frame_title = title,
prompt = prompt,
mat_filter = mat_filter,
on_select = on_select,
on_cancel = on_cancel,
}:show()
end
function ItemTypeDialog(args)
args.text = args.prompt or 'Type or select an item type'
args.text_pen = COLOR_WHITE
args.with_filter = true
args.icon_width = 2
local choices = { {
icon = '?', text = args.none_caption or 'none', item_type = -1, item_subtype = -1
} }
local filter = args.item_filter
for itype = 0,df.item_type._last_item do
local attrs = df.item_type.attrs[itype]
local defcnt = dfhack.items.getSubtypeCount(itype)
if not filter or filter(itype,-1) then
local name = attrs.caption or df.item_type[itype]
local icon
if defcnt >= 0 then
name = 'any '..name
icon = '+'
end
table.insert(choices, {
icon = icon, text = string.lower(name), item_type = itype, item_subtype = -1
})
end
if defcnt > 0 then
for subtype = 0,defcnt-1 do
local def = dfhack.items.getSubtypeDef(itype, subtype)
if not filter or filter(itype,subtype,def) then
table.insert(choices, {
icon = '\x1e', text = ' '..def.name, item_type = itype, item_subtype = subtype
})
end
end
end
end
args.choices = choices
if args.on_select then
local cb = args.on_select
args.on_select = function(idx, obj)
return cb(obj.item_type, obj.item_subtype)
end
end
return dlg.ListBox(args)
end
return _ENV

@ -0,0 +1,164 @@
-- Support for scripted interaction sequences via coroutines.
local _ENV = mkmodule('gui.script')
local dlg = require('gui.dialogs')
--[[
Example:
start(function()
sleep(100, 'frames')
print(showYesNoPrompt('test', 'print true?'))
end)
]]
-- Table of running background scripts.
if not scripts then
scripts = {}
setmetatable(scripts, { __mode = 'k' })
end
local function do_resume(inst, ...)
inst.gen = inst.gen + 1
return (dfhack.saferesume(inst.coro, ...))
end
-- Starts a new background script by calling the function.
function start(fn,...)
local coro = coroutine.create(fn)
local inst = {
coro = coro,
gen = 0,
}
scripts[coro] = inst
return do_resume(inst, ...)
end
-- Checks if called from a background script
function in_script()
return scripts[coroutine.running()] ~= nil
end
local function getinst()
local inst = scripts[coroutine.running()]
if not inst then
error('Not in a gui script coroutine.')
end
return inst
end
local function invoke_resume(inst,gen,quiet,...)
local state = coroutine.status(inst.coro)
if state ~= 'suspended' then
if state ~= 'dead' then
dfhack.printerr(debug.traceback('resuming a non-waiting continuation'))
end
elseif inst.gen > gen then
if not quiet then
dfhack.printerr(debug.traceback('resuming an expired continuation'))
end
else
do_resume(inst, ...)
end
end
-- Returns a callback that resumes the script from wait with given return values
function mkresume(...)
local inst = getinst()
return curry(invoke_resume, inst, inst.gen, false, ...)
end
-- Like mkresume, but does not complain if already resumed from this wait
function qresume(...)
local inst = getinst()
return curry(invoke_resume, inst, inst.gen, true, ...)
end
-- Wait until a mkresume callback is called, then return its arguments.
-- Once it returns, all mkresume callbacks created before are invalidated.
function wait()
getinst() -- check that the context is right
return coroutine.yield()
end
-- Wraps dfhack.timeout for coroutines.
function sleep(time, quantity)
if dfhack.timeout(time, quantity, mkresume()) then
wait()
return true
else
return false
end
end
-- Some dialog wrappers:
function showMessage(title, text, tcolor)
dlg.MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_close = qresume(nil)
}:show()
return wait()
end
function showYesNoPrompt(title, text, tcolor)
dlg.MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_accept = mkresume(true),
on_cancel = mkresume(false),
on_close = qresume(nil)
}:show()
return wait()
end
function showInputPrompt(title, text, tcolor, input, min_width)
dlg.InputBox{
frame_title = title,
text = text,
text_pen = tcolor,
input = input,
frame_width = min_width,
on_input = mkresume(true),
on_cancel = mkresume(false),
on_close = qresume(nil)
}:show()
return wait()
end
function showListPrompt(title, text, tcolor, choices, min_width, filter)
dlg.ListBox{
frame_title = title,
text = text,
text_pen = tcolor,
choices = choices,
frame_width = min_width,
with_filter = filter,
on_select = mkresume(true),
on_cancel = mkresume(false),
on_close = qresume(nil)
}:show()
return wait()
end
function showMaterialPrompt(title, prompt)
require('gui.materials').MaterialDialog{
frame_title = title,
prompt = prompt,
on_select = mkresume(true,
on_cancel = mkresume(false),
on_close = qresume(nil)
}:show()
return wait()
end
return _ENV

@ -0,0 +1,696 @@
-- Simple widgets for screens
local _ENV = mkmodule('gui.widgets')
local gui = require('gui')
local utils = require('utils')
local dscreen = dfhack.screen
local function show_view(view,vis)
if view then
view.visible = vis
end
end
local function getval(obj)
if type(obj) == 'function' then
return obj()
else
return obj
end
end
local function map_opttab(tab,idx)
if tab then
return tab[idx]
else
return idx
end
end
------------
-- Widget --
------------
Widget = defclass(Widget, gui.View)
Widget.ATTRS {
frame = DEFAULT_NIL,
frame_inset = DEFAULT_NIL,
frame_background = DEFAULT_NIL,
}
function Widget:computeFrame(parent_rect)
local sw, sh = parent_rect.width, parent_rect.height
return gui.compute_frame_body(sw, sh, self.frame, self.frame_inset)
end
function Widget:onRenderFrame(dc, rect)
if self.frame_background then
dc:fill(rect, self.frame_background)
end
end
-----------
-- Panel --
-----------
Panel = defclass(Panel, Widget)
Panel.ATTRS {
on_render = DEFAULT_NIL,
}
function Panel:init(args)
self:addviews(args.subviews)
end
function Panel:onRenderBody(dc)
if self.on_render then self.on_render(dc) end
end
-----------
-- Pages --
-----------
Pages = defclass(Pages, Panel)
function Pages:init(args)
for _,v in ipairs(self.subviews) do
v.visible = false
end
self:setSelected(args.selected or 1)
end
function Pages:setSelected(idx)
if type(idx) ~= 'number' then
local key = idx
if type(idx) == 'string' then
key = self.subviews[key]
end
idx = utils.linear_index(self.subviews, key)
if not idx then
error('Unknown page: '..key)
end
end
show_view(self.subviews[self.selected], false)
self.selected = math.min(math.max(1, idx), #self.subviews)
show_view(self.subviews[self.selected], true)
end
function Pages:getSelected()
return self.selected, self.subviews[self.selected]
end
----------------
-- Edit field --
----------------
EditField = defclass(EditField, Widget)
EditField.ATTRS{
text = '',
text_pen = DEFAULT_NIL,
on_char = DEFAULT_NIL,
on_change = DEFAULT_NIL,
on_submit = DEFAULT_NIL,
}
function EditField:onRenderBody(dc)
dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0)
local cursor = '_'
if not self.active or gui.blink_visible(300) then
cursor = ' '
end
local txt = self.text .. cursor
if #txt > dc.width then
txt = string.char(27)..string.sub(txt, #txt-dc.width+2)
end
dc:string(txt)
end
function EditField:onInput(keys)
if self.on_submit and keys.SELECT then
self.on_submit(self.text)
return true
elseif keys._STRING then
local old = self.text
if keys._STRING == 0 then
self.text = string.sub(old, 1, #old-1)
else
local cv = string.char(keys._STRING)
if not self.on_char or self.on_char(cv, old) then
self.text = old .. cv
end
end
if self.on_change and self.text ~= old then
self.on_change(self.text, old)
end
return true
end
end
-----------
-- Label --
-----------
function parse_label_text(obj)
local text = obj.text or {}
if type(text) ~= 'table' then
text = { text }
end
local curline = nil
local out = { }
local active = nil
local idtab = nil
for _,v in ipairs(text) do
local vv
if type(v) == 'string' then
vv = utils.split_string(v, NEWLINE)
else
vv = { v }
end
for i = 1,#vv do
local cv = vv[i]
if i > 1 then
if not curline then
table.insert(out, {})
end
curline = nil
end
if cv ~= '' then
if not curline then
curline = {}
table.insert(out, curline)
end
if type(cv) == 'string' then
table.insert(curline, { text = cv })
else
table.insert(curline, cv)
if cv.on_activate then
active = active or {}
table.insert(active, cv)
end
if cv.id then
idtab = idtab or {}
idtab[cv.id] = cv
end
end
end
end
end
obj.text_lines = out
obj.text_active = active
obj.text_ids = idtab
end
local function is_disabled(token)
return (token.disabled ~= nil and getval(token.disabled)) or
(token.enabled ~= nil and not getval(token.enabled))
end
function render_text(obj,dc,x0,y0,pen,dpen)
local width = 0
for iline,line in ipairs(obj.text_lines) do
local x = 0
if dc then
dc:seek(x+x0,y0+iline-1)
end
for _,token in ipairs(line) do
token.line = iline
token.x1 = x
if token.gap then
x = x + token.gap
if dc then
dc:advance(token.gap)
end
end
if token.tile then
x = x + 1
if dc then
dc:char(nil, token.tile)
end
end
if token.text or token.key then
local text = getval(token.text) or ''
local keypen
if dc then
if is_disabled(token) then
dc:pen(getval(token.dpen) or dpen)
keypen = COLOR_GREEN
else
dc:pen(getval(token.pen) or pen)
keypen = COLOR_LIGHTGREEN
end
end
x = x + #text
if token.key then
local keystr = gui.getKeyDisplay(token.key)
local sep = token.key_sep or ''
x = x + #keystr
if sep == '()' then
if dc then
dc:string(text)
dc:string(' ('):string(keystr,keypen):string(')')
end
x = x + 3
else
if dc then
dc:string(keystr,keypen):string(sep):string(text)
end
x = x + #sep
end
else
if dc then
dc:string(text)
end
end
end
token.x2 = x
end
width = math.max(width, x)
end
obj.text_width = width
end
function check_text_keys(self, keys)
if self.text_active then
for _,item in ipairs(self.text_active) do
if item.key and keys[item.key] and not is_disabled(item) then
item.on_activate()
return true
end
end
end
end
Label = defclass(Label, Widget)
Label.ATTRS{
text_pen = COLOR_WHITE,
text_dpen = COLOR_DARKGREY,
auto_height = true,
auto_width = false,
}
function Label:init(args)
self:setText(args.text)
end
function Label:setText(text)
self.text = text
parse_label_text(self)
if self.auto_height then
self.frame = self.frame or {}
self.frame.h = self:getTextHeight()
end
end
function Label:preUpdateLayout()
if self.auto_width then
self.frame = self.frame or {}
self.frame.w = self:getTextWidth()
end
end
function Label:itemById(id)
if self.text_ids then
return self.text_ids[id]
end
end
function Label:getTextHeight()
return #self.text_lines
end
function Label:getTextWidth()
render_text(self)
return self.text_width
end
function Label:onRenderBody(dc)
render_text(self,dc,0,0,self.text_pen,self.text_dpen)
end
function Label:onInput(keys)
return check_text_keys(self, keys)
end
----------
-- List --
----------
List = defclass(List, Widget)
STANDARDSCROLL = {
STANDARDSCROLL_UP = -1,
STANDARDSCROLL_DOWN = 1,
STANDARDSCROLL_PAGEUP = '-page',
STANDARDSCROLL_PAGEDOWN = '+page',
}
SECONDSCROLL = {
SECONDSCROLL_UP = -1,
SECONDSCROLL_DOWN = 1,
SECONDSCROLL_PAGEUP = '-page',
SECONDSCROLL_PAGEDOWN = '+page',
}
List.ATTRS{
text_pen = COLOR_CYAN,
cursor_pen = COLOR_LIGHTCYAN,
cursor_dpen = DEFAULT_NIL,
inactive_pen = DEFAULT_NIL,
on_select = DEFAULT_NIL,
on_submit = DEFAULT_NIL,
row_height = 1,
scroll_keys = STANDARDSCROLL,
icon_width = DEFAULT_NIL,
}
function List:init(info)
self.page_top = 1
self.page_size = 1
self:setChoices(info.choices, info.selected)
end
function List:setChoices(choices, selected)
self.choices = choices or {}
for i,v in ipairs(self.choices) do
if type(v) ~= 'table' then
v = { text = v }
self.choices[i] = v
end
v.text = v.text or v.caption or v[1]
parse_label_text(v)
end
self:setSelected(selected)
end
function List:setSelected(selected)
self.selected = selected or self.selected or 1
self:moveCursor(0, true)
return self.selected
end
function List:getChoices()
return self.choices
end
function List:getSelected()
return self.selected, self.choices[self.selected]
end
function List:getContentWidth()
local width = 0
for i,v in ipairs(self.choices) do
render_text(v)
local roww = v.text_width
if v.key then
roww = roww + 3 + #gui.getKeyDisplay(v.key)
end
width = math.max(width, roww)
end
return width + (self.icon_width or 0)
end
function List:getContentHeight()
return #self.choices * self.row_height
end
function List:postComputeFrame(body)
self.page_size = math.max(1, math.floor(body.height / self.row_height))
self:moveCursor(0)
end
function List:moveCursor(delta, force_cb)
local page = math.max(1, self.page_size)
local cnt = #self.choices
if cnt < 1 then
self.page_top = 1
self.selected = 1
return
end
local off = self.selected+delta-1
local ds = math.abs(delta)
if ds > 1 then
if off >= cnt+ds-1 then
off = 0
else
off = math.min(cnt-1, off)
end
if off <= -ds then
off = cnt-1
else
off = math.max(0, off)
end
end
self.selected = 1 + off % cnt
self.page_top = 1 + page * math.floor((self.selected-1) / page)
if (force_cb or delta ~= 0) and self.on_select then
self.on_select(self:getSelected())
end
end
function List:onRenderBody(dc)
local choices = self.choices
local top = self.page_top
local iend = math.min(#choices, top+self.page_size-1)
local iw = self.icon_width
for i = top,iend do
local obj = choices[i]
local current = (i == self.selected)
local cur_pen = self.text_pen
local cur_dpen = cur_pen
if current and active then
cur_pen = self.cursor_pen
cur_dpen = self.cursor_dpen or self.text_pen
elseif current then
cur_pen = self.inactive_pen or self.cursor_pen
cur_dpen = self.inactive_pen or self.text_pen
end
local y = (i - top)*self.row_height
if iw and obj.icon then
dc:seek(0, y)
if type(obj.icon) == 'table' then
dc:char(nil,obj.icon)
else
dc:string(obj.icon, obj.icon_pen or cur_pen)
end
end
render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen)
if obj.key then
local keystr = gui.getKeyDisplay(obj.key)
dc:seek(dc.width-2-#keystr,y):pen(self.text_pen)
dc:string('('):string(keystr,COLOR_LIGHTGREEN):string(')')
end
end
end
function List:submit()
if self.on_submit and #self.choices > 0 then
self.on_submit(self:getSelected())
end
end
function List:onInput(keys)
if self.on_submit and keys.SELECT then
self:submit()
return true
else
for k,v in pairs(self.scroll_keys) do
if keys[k] then
if v == '+page' then
v = self.page_size
elseif v == '-page' then
v = -self.page_size
end
self:moveCursor(v)
return true
end
end
for i,v in ipairs(self.choices) do
if v.key and keys[v.key] then
self:setSelected(i)
self:submit()
return true
end
end
local current = self.choices[self.selected]
if current then
return check_text_keys(current, keys)
end
end
end
-------------------
-- Filtered List --
-------------------
FilteredList = defclass(FilteredList, Widget)
function FilteredList:init(info)
self.edit = EditField{
text_pen = info.cursor_pen,
frame = { l = info.icon_width, t = 0 },
on_change = self:callback('onFilterChange'),
on_char = self:callback('onFilterChar'),
}
self.list = List{
frame = { t = 2 },
text_pen = info.text_pen,
cursor_pen = info.cursor_pen,
inactive_pen = info.inactive_pen,
row_height = info.row_height,
scroll_keys = info.scroll_keys,
icon_width = info.icon_width,
}
if info.on_select then
self.list.on_select = function()
return info.on_select(self:getSelected())
end
end
if info.on_submit then
self.list.on_submit = function()
return info.on_submit(self:getSelected())
end
end
self.not_found = Label{
visible = false,
text = info.not_found_label or 'No matches',
text_pen = COLOR_LIGHTRED,
frame = { l = info.icon_width, t = 2 },
}
self:addviews{ self.edit, self.list, self.not_found }
self:setChoices(info.choices, info.selected)
end
function FilteredList:getChoices()
return self.choices
end
function FilteredList:setChoices(choices, pos)
choices = choices or {}
self.choices = choices
self.edit.text = ''
self.list:setChoices(choices, pos)
self.not_found.visible = (#choices == 0)
end
function FilteredList:submit()
return self.list:submit()
end
function FilteredList:canSubmit()
return not self.not_found.visible
end
function FilteredList:getSelected()
local i,v = self.list:getSelected()
return map_opttab(self.choice_index, i), v
end
function FilteredList:getContentWidth()
return self.list:getContentWidth()
end
function FilteredList:getContentHeight()
return self.list:getContentHeight() + 2
end
function FilteredList:getFilter()
return self.edit.text, self.list.choices
end
function FilteredList:setFilter(filter, pos)
local choices = self.choices
local cidx = nil
filter = filter or ''
self.edit.text = filter
if filter ~= '' then
local tokens = utils.split_string(filter, ' ')
local ipos = pos
choices = {}
cidx = {}
pos = nil
for i,v in ipairs(self.choices) do
local ok = true
local search_key = v.search_key or v.text
for _,key in ipairs(tokens) do
if key ~= '' and not string.match(search_key, '%f[^%s\x00]'..key) then
ok = false
break
end
end
if ok then
table.insert(choices, v)
cidx[#choices] = i
if ipos == i then
pos = #choices
end
end
end
end
self.choice_index = cidx
self.list:setChoices(choices, pos)
self.not_found.visible = (#choices == 0)
end
function FilteredList:onFilterChange(text)
self:setFilter(text)
end
local bad_chars = {
['%'] = true, ['.'] = true, ['+'] = true, ['*'] = true,
['['] = true, [']'] = true, ['('] = true, [')'] = true,
}
function FilteredList:onFilterChar(char, text)
if bad_chars[char] then
return false
end
if char == ' ' then
return string.match(text, '%S$')
end
return true
end
return _ENV

@ -358,7 +358,7 @@ end
-- Interactive search utility -- Interactive search utility
function DiffSearcher:find_interactive(prompt,data_type,condition_cb) function DiffSearcher:find_interactive(prompt,data_type,condition_cb,iter_limit)
enum = enum or {} enum = enum or {}
-- Loop for restarting search from scratch -- Loop for restarting search from scratch
@ -374,6 +374,11 @@ function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
while true do while true do
print('') print('')
if iter_limit and ccursor >= iter_limit then
dfhack.printerr(' Iteration limit reached without a solution.')
break
end
local ok, value, delta = condition_cb(ccursor) local ok, value, delta = condition_cb(ccursor)
ccursor = ccursor + 1 ccursor = ccursor + 1

@ -302,6 +302,24 @@ function sort_vector(vector,field,cmp)
return vector return vector
end end
-- Linear search
function linear_index(vector,obj)
local min,max
if df.isvalid(vector) then
min,max = 0,#vector-1
else
min,max = 1,#vector
end
for i=min,max do
if vector[i] == obj then
return i
end
end
return nil
end
-- Binary search in a vector or lua table -- Binary search in a vector or lua table
function binsearch(vector,key,field,cmp,min,max) function binsearch(vector,key,field,cmp,min,max)
if not(min and max) then if not(min and max) then
@ -361,6 +379,27 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos return added,cur,pos
end end
-- Binary search and erase
function erase_sorted_key(vector,key,field,cmp)
local cur,found,pos = binsearch(vector,key,field,cmp)
if found then
if df.isvalid(vector) then
vector:erase(pos)
else
table.remove(vector, pos)
end
end
return found,cur,pos
end
function erase_sorted(vector,item,field,cmp)
local key = item
if field and item then
key = item[field]
end
return erase_sorted_key(vector,key,field,cmp)
end
-- Calls a method with a string temporary -- Calls a method with a string temporary
function call_with_string(obj,methodname,...) function call_with_string(obj,methodname,...)
return dfhack.with_temp_object( return dfhack.with_temp_object(

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

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