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/depends
build/install_manifest.txt
build/README.html
build/LUA_API.html
build/COMPILE.html
#ignore Kdevelop stuff
.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 "${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}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
@ -145,12 +145,33 @@ include_directories(depends/clsocket/src)
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
IF(BUILD_LIBRARY)
add_subdirectory (library)
## 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()
#build the plugins

@ -3,13 +3,13 @@
<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: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>Building DFHACK</title>
<style type="text/css">
/*
: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.
Default cascading style sheet for the HTML output of Docutils.
@ -249,10 +249,18 @@ pre.address {
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math {
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 }
@ -326,20 +334,21 @@ ul.auto-toc {
<li><a class="reference internal" href="#build" id="id7">Build</a></li>
</ul>
</li>
<li><a class="reference internal" href="#windows" id="id8">Windows</a><ul>
<li><a class="reference internal" href="#id1" id="id9">How to get the code</a></li>
<li><a class="reference internal" href="#id2" id="id10">Dependencies</a></li>
<li><a class="reference internal" href="#id3" id="id11">Build</a></li>
<li><a class="reference internal" href="#mac-os-x" id="id8">Mac OS X</a></li>
<li><a class="reference internal" href="#windows" id="id9">Windows</a><ul>
<li><a class="reference internal" href="#id1" id="id10">How to get the code</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>
</li>
<li><a class="reference internal" href="#build-types" id="id12">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="#df-data-structure-definitions" id="id14">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="#contributing-to-dfhack" id="id16">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="#how-to-get-new-code-into-dfhack" id="id18">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="#build-types" id="id13">Build types</a></li>
<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="id15">DF data structure definitions</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="id17">Contributing to DFHack</a><ul>
<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="id19">How to get new code into DFHack</a></li>
<li><a class="reference internal" href="#memory-research" id="id20">Memory research</a></li>
</ul>
</li>
</ul>
@ -395,11 +404,66 @@ extra options.</p>
program.</p>
</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">
<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>
<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.
You will need some sort of Windows port of git, or a GUI. Some examples:</p>
<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>
</div>
<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
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
@ -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>
</div>
<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>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>
@ -459,7 +523,7 @@ So pick either Release or RelWithDebInfo build and build the INSTALL target.</p>
</div>
</div>
<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
variable: <tt class="docutils literal">CMAKE_BUILD_TYPE</tt></p>
<pre class="literal-block">
@ -471,7 +535,7 @@ cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
'RelWithDebInfo'. 'Debug' is not available on Windows.</p>
</div>
<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.
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>
@ -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
the dfhack repository is welcome and the right thing to do :)</p>
<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>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>
</div>
<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>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>
</div>
<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>
<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
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
I'll make <em>you</em> fix it ;)</p>
</div>
<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
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>
@ -521,7 +585,7 @@ this is also a good place to dump new ideas and/or bugs that need
fixing.</p>
</div>
<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.
In general, you'll need a good memory viewer and optionally something
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
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
=======

@ -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
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
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">
<head>
<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>
<style type="text/css">
/*
: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.
Default cascading style sheet for the HTML output of Docutils.
@ -249,10 +249,18 @@ pre.address {
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math {
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 }
@ -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
this, forcing the function to block on input with lock held.</p>
</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
string, global environment and command-line history file.</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
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>
<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 class="section" id="material-info-lookup">
<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>,
a lua list containing them.</p>
</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>
</div>
<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.
If <tt class="docutils literal">true_age</tt> is true, ignores false identities.</p>
</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>
<p>Computes the effective rating for the given skill, taking into account exhaustion, pain etc.</p>
</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>
<p>Turns the item into a projectile, and returns the new object, or <em>nil</em> if impossible.</p>
</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>
</div>
<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>
<p>Returns a map block object for given x,y,z in local block coordinates.</p>
</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>
</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>
@ -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>
<p>Like <tt class="docutils literal">getTileBlock</tt>, but if the block is not allocated, try creating it.</p>
</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>
<p>Returns the biome info struct for the given global map region.</p>
</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
burrows, or the presence of invaders.</p>
</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>
</div>
<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>
<p>Returns a table with x, y and z as fields.</p>
</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>
<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>
@ -1902,6 +1972,12 @@ utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
</pre>
<p>(For an explanation of <tt class="docutils literal">new=true</tt>, see table assignment in the wrapper section)</p>
</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>
<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.

@ -488,7 +488,7 @@ Input & Output
lock. Using an explicit ``dfhack.with_suspend`` will prevent
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
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.
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
--------------------
@ -845,6 +863,15 @@ Job module
if there are any jobs with ``first_id <= id < job_next_id``,
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
------------
@ -918,6 +945,11 @@ Units module
Returns the age of the unit in years as a floating-point value.
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)``
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.
* ``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
-----------
@ -1027,7 +1071,7 @@ Maps module
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.
@ -1039,6 +1083,14 @@ Maps module
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)``
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
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
--------------
@ -1688,6 +1756,18 @@ environment by the mandatory init file dfhack.lua:
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...)``
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)
* ``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)``
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:
- 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.
- prospect: pre-embark prospector accounts for caves & magma sea in its estimate.
- 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:
- 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.
@ -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 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 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:
- fixnaked: removes thoughts about nakedness.
- 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/power-meter: front-end for the Power Meter 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:
- can set nonidle hauler percentage.
- 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.
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.
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/'.
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!
=============================
@ -148,6 +178,24 @@ for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@fo
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
to retrieve further help without having to look at this document. Alternatively,
some accept a 'help'/'?' option on their command line.
@ -175,11 +223,14 @@ by 'reveal hell'. This is nice for digging under rivers.
fastdwarf
---------
Makes your minions move at ludicrous speeds.
* Activate with 'fastdwarf 1'
* Deactivate with 'fastdwarf 0'
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.
* '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
==============
@ -603,14 +654,20 @@ Options:
Pre-embark estimate
...................
If called during the embark selection screen, displays an estimate of layer
stone availability. If the 'all' option is specified, also estimates veins.
The estimate is computed either for 1 embark tile of the blinking biome, or
for all tiles of the embark rectangle.
If prospect is called during the embark selection screen, it displays an estimate of
layer stone availability.
.. 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:
:all: processes all tiles, even hidden ones.
:all: Also estimate vein mineral amounts.
reveal
------
@ -780,6 +837,22 @@ Examples:
* '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
-----------
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
-----
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.
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
for slaughter. Grabbing wagons results in some funny spam, then
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.
: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).
@ -986,6 +1062,13 @@ Options:
in advmode. The issue is that the screen tries to force you to select
the contents separately from the container. This forcefully skips child
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
@ -1476,6 +1559,17 @@ Confirmed working DFusion plugins:
* 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.
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
@ -1544,8 +1638,9 @@ siren
Wakes up sleeping units, cancels breaks and stops parties either everywhere,
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
break again a lot sooner.
noise, tiredness and lack of protection. Also, the units with interrupted
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
=========
@ -1567,16 +1662,18 @@ removebadthoughts
This script remove negative thoughts from your dwarves. Very useful against
tantrum spirals.
With a selected unit in 'v' mode, will clear this unit's mind, otherwise
clear all your fort's units minds.
The script can target a single creature, when used with the ``him`` argument,
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,
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
it removed.
Internals: the thoughts are set to be very old, so that the game remove them
quickly after you unpause.
slayrace
@ -1585,7 +1682,7 @@ Kills any unit of a given race.
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``
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.
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
d;d;u;d;d;skip this tile;d
d;d;d;i
@ -1656,8 +1753,8 @@ superdwarf
==========
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
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
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
=================
@ -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.
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
to toggle all labors within the selected category.
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,
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
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.
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
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
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.
Power Meter
===========
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
======
gui/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.
* ``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.
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,
either immediately or after opening the assign owner page.
To use, bind to a key and activate in the 'q' mode, either immediately or after opening
the assign owner page.
The script lists other rooms owned by the same owner, or by the unit selected in the assign
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
============
Front-end to the siege-engine plugin implemented by the gui/siege-engine script. Bind to a
key and activate after selecting a siege engine in 'q' mode.
The siege-engine plugin enables siege engines to be linked to stockpiles, and
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 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
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
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
menu.
.. admonition:: DISCLAIMER
Siege engines are a very interesting feature, but currently nearly useless
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.
Power Meter
===========
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.
=========
RAW hacks
=========
The configuration front-end is implemented by the gui/power-meter script. Bind it to a
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
@ -1855,7 +1996,7 @@ The magma version also needs magma.
.. admonition:: ISSUE
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
being passable, so use them.
@ -1866,7 +2007,7 @@ is extracted from the workshop raws.
.. admonition:: ISSUE
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.
This also means that engines cannot be chained without intermediate
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
in the 't' view of the workshop.
**NOTE**: The completion of the job will actually consume one unit
of the appropriate liquids from below the workshop.
.. note::
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,
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_``
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``
and ``tweak advmode-contained``.

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

@ -36,16 +36,14 @@
* internal hash function, calling
* 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;
//init md5
MD5Init(&ctx);
//update with our string
MD5Update(&ctx,
(unsigned char*)text.c_str(),
text.length());
MD5Update(&ctx, data, length);
//create the hash
unsigned char buff[16] = "";
@ -95,10 +93,9 @@ md5wrapper::~md5wrapper()
*/
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
* a file specified in "filename" and

@ -31,7 +31,7 @@ class md5wrapper
* internal hash function, calling
* 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
@ -52,6 +52,10 @@ class md5wrapper
*/
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
* a file specified in "filename" and

@ -18,7 +18,7 @@ keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
# gui/rename script
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 #
@ -45,8 +45,15 @@ keybinding add Shift-R "job-material RHYOLITE"
keybinding add Shift-I "job-material CINNABAR"
keybinding add Shift-B "job-material COBALTITE"
keybinding add Shift-O "job-material OBSIDIAN"
keybinding add Shift-T "job-material ORTHOCLASE"
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
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
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 #
############################
@ -90,3 +106,12 @@ tweak fix-dimensions
# make reactions requiring containers usable in advmode - the issue is
# that the screen asks for those reagents to be selected directly
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
rst2html README.rst > Readme.html
rst2html COMPILE.rst > Compile.html
rst2html DEVEL.rst > Devel.html
rst2html LUA_API.rst > Lua\ API.html
# regenerate documentation after editing the .rst files. Requires python and docutils.
cd `dirname $0`
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(binpatch binpatch.cpp)
TARGET_LINK_LIBRARIES(binpatch dfhack-md5)
IF(BUILD_EGGY)
SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" )
else()
@ -329,7 +332,7 @@ install(FILES xml/symbols.xml
install(FILES ../dfhack.init-example
DESTINATION ${DFHACK_BINARY_DESTINATION})
install(TARGETS dfhack-run dfhack-client
install(TARGETS dfhack-run dfhack-client binpatch
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})

@ -1,6 +1,6 @@
/*
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
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)
{
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];
if (isspace(c)) {
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"
" reload PLUGIN|all - Reload a plugin or all loaded plugins.\n"
);
con.print("\nDFHack version " DFHACK_VERSION ".\n");
}
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")
{
World * w = getWorld();
w->SetPauseState(true);
World::SetPauseState(true);
con.print("The game was forced to pause!\n");
}
else if(first == "cls")
@ -795,6 +822,8 @@ std::string Core::getHackPath()
#endif
}
void init_screen_module(Core *);
bool Core::Init()
{
if(started)
@ -840,6 +869,7 @@ bool Core::Init()
// Init global object pointers
df::global::InitGlobals();
init_screen_module(this);
cerr << "Initializing Console.\n";
// init the console.
@ -1096,7 +1126,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache();
World::ClearPersistentCache();
// and if the world is going away, we report the map change first
if(had_map)
@ -1114,7 +1144,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
if (isMapLoaded() != had_map)
{
getWorld()->ClearPersistentCache();
World::ClearPersistentCache();
onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
}
}
@ -1652,7 +1682,6 @@ TYPE * Core::get##TYPE() \
return s_mods.p##TYPE;\
}
MODULE_GETTER(World);
MODULE_GETTER(Materials);
MODULE_GETTER(Notes);
MODULE_GETTER(Graphic);

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
/*
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
warranty. In no event will the authors be held liable for any
@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h"
#include "Core.h"
#include "Error.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
@ -81,6 +82,7 @@ distribution.
#include "df/flow_info.h"
#include "df/unit_misc_trait.h"
#include "df/proj_itemst.h"
#include "df/itemdef.h"
#include <lua.h>
#include <lauxlib.h>
@ -258,7 +260,7 @@ static PersistentDataItem persistent_by_struct(lua_State *state, int idx)
int id = lua_tointeger(state, -1);
lua_pop(state, 1);
PersistentDataItem ref = Core::getInstance().getWorld()->GetPersistentData(id);
PersistentDataItem ref = World::GetPersistentData(id);
if (ref.isValid())
{
@ -311,19 +313,19 @@ static PersistentDataItem get_persistent(lua_State *state)
if (lua_istable(state, 1))
{
Lua::StackUnwinder frame(state);
if (!lua_getmetatable(state, 1) ||
!lua_rawequal(state, -1, lua_upvalueindex(1)))
luaL_argerror(state, 1, "invalid table type");
lua_settop(state, 1);
return persistent_by_struct(state, 1);
}
else
{
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);
bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref);
bool ok = World::DeletePersistentData(ref);
lua_pushboolean(state, ok);
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);
std::vector<PersistentDataItem> data;
Core::getInstance().getWorld()->GetPersistentData(&data, str, prefix);
World::GetPersistentData(&data, str, prefix);
if (data.empty())
{
@ -396,7 +398,7 @@ static int dfhack_persistent_save(lua_State *state)
if (add)
{
ref = Core::getInstance().getWorld()->AddPersistentData(str);
ref = World::AddPersistentData(str);
added = true;
}
else if (lua_getmetatable(state, 1))
@ -409,13 +411,13 @@ static int dfhack_persistent_save(lua_State *state)
}
else
{
ref = Core::getInstance().getWorld()->GetPersistentData(str);
ref = World::GetPersistentData(str);
}
// Auto-add if not found
if (!ref.isValid())
{
ref = Core::getInstance().getWorld()->AddPersistentData(str);
ref = World::AddPersistentData(str);
if (!ref.isValid())
luaL_error(state, "cannot create persistent entry");
added = true;
@ -446,11 +448,38 @@ static int dfhack_persistent_save(lua_State *state)
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[] = {
{ "get", dfhack_persistent_get },
{ "delete", dfhack_persistent_delete },
{ "get_all", dfhack_persistent_get_all },
{ "save", dfhack_persistent_save },
{ "getTilemask", dfhack_persistent_getTilemask },
{ "deleteTilemask", dfhack_persistent_deleteTilemask },
{ NULL, NULL }
};
@ -781,6 +810,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,getWorker),
WRAPM(Job,checkBuildingsNow),
WRAPM(Job,checkDesignationsNow),
WRAPM(Job,isSuitableItem),
WRAPM(Job,isSuitableMaterial),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
@ -828,6 +859,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDwarf),
WRAPM(Units, isCitizen),
WRAPM(Units, getAge),
WRAPM(Units, getNominalSkill),
WRAPM(Units, getEffectiveSkill),
WRAPM(Units, computeMovementSpeed),
WRAPM(Units, getProfessionName),
@ -906,6 +938,9 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, setOwner),
WRAPM(Items, getContainer),
WRAPM(Items, getDescription),
WRAPM(Items, isCasteMaterial),
WRAPM(Items, getSubtypeCount),
WRAPM(Items, getSubtypeDef),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToBuilding, items_moveToBuilding),
@ -936,6 +971,22 @@ static const luaL_Reg dfhack_items_funcs[] = {
/***** 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[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, enableBlockUpdates),
@ -943,6 +994,10 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween),
WRAPM(Maps, spawnFlow),
WRAPN(hasTileAssignment, hasTileAssignment),
WRAPN(getTileAssignment, getTileAssignment),
WRAPN(setTileAssignment, setTileAssignment),
WRAPN(resetTileAssignment, resetTileAssignment),
{ NULL, NULL }
};
@ -967,6 +1022,25 @@ static int maps_ensureTileBlock(lua_State *L)
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)
{
auto pos = CheckCoordXY(L, 1, true);
@ -984,6 +1058,8 @@ static const luaL_Reg dfhack_maps_funcs[] = {
{ "isValidTilePos", maps_isValidTilePos },
{ "getTileBlock", maps_getTileBlock },
{ "ensureTileBlock", maps_ensureTileBlock },
{ "getTileType", maps_getTileType },
{ "getTileFlags", maps_getTileFlags },
{ "getRegionBiome", maps_getRegionBiome },
{ "getTileBiomeRgn", maps_getTileBiomeRgn },
{ NULL, NULL }
@ -1145,6 +1221,7 @@ static const LuaWrapper::FunctionReg dfhack_screen_module[] = {
WRAPM(Screen, inGraphicsMode),
WRAPM(Screen, clear),
WRAPM(Screen, invalidate),
WRAPM(Screen, getKeyDisplay),
{ NULL, NULL }
};

@ -1,6 +1,6 @@
/*
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
/*
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
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.
*/
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);
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);
}
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();
@ -1296,11 +1341,17 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
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_setfield(state, base+1, "_first_item");
lua_setfield(state, ftable, "_first_item");
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);
@ -1321,7 +1372,7 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
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
lua_newtable(state);
@ -1338,11 +1389,17 @@ static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *ei
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_setfield(state, base+1, "_first_item");
lua_setfield(state, ftable, "_first_item");
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);
@ -1355,7 +1412,12 @@ static void RenderType(lua_State *state, compound_identity *node)
assert(node->getName());
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);
if (!lua_checkstack(state, 20))
@ -1365,51 +1427,59 @@ static void RenderType(lua_State *state, compound_identity *node)
// metatable
lua_newtable(state);
int ix_meta = base+2;
lua_dup(state);
lua_setmetatable(state, base+1);
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_setfield(state, base+2, "__tostring");
lua_setfield(state, ix_meta, "__tostring");
lua_pushlightuserdata(state, node);
lua_rawsetp(state, base+2, &DFHACK_IDENTITY_FIELD_TOKEN);
lua_rawsetp(state, ix_meta, &DFHACK_IDENTITY_FIELD_TOKEN);
// inner table
lua_newtable(state);
int ftable = base+3;
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())
{
case IDTYPE_STRUCT:
lua_pushstring(state, "struct-type");
lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node);
IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break;
case IDTYPE_CLASS:
lua_pushstring(state, "class-type");
lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node);
IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break;
case IDTYPE_ENUM:
lua_pushstring(state, "enum-type");
lua_setfield(state, ftable, "_kind");
FillEnumKeys(state, ftable, (enum_identity*)node);
FillEnumKeys(state, ix_meta, ftable, (enum_identity*)node);
break;
case IDTYPE_BITFIELD:
lua_pushstring(state, "bitfield-type");
lua_setfield(state, ftable, "_kind");
FillBitfieldKeys(state, ftable, (bitfield_identity*)node);
FillBitfieldKeys(state, ix_meta, ftable, (bitfield_identity*)node);
break;
case IDTYPE_GLOBAL:
@ -1425,14 +1495,14 @@ static void RenderType(lua_State *state, compound_identity *node)
BuildTypeMetatable(state, node);
lua_dup(state);
lua_setmetatable(state, base+3);
lua_setmetatable(state, ftable);
lua_getfield(state, -1, "__newindex");
lua_setfield(state, base+2, "__newindex");
lua_setfield(state, ix_meta, "__newindex");
lua_getfield(state, -1, "__pairs");
lua_setfield(state, base+2, "__pairs");
lua_setfield(state, ix_meta, "__pairs");
lua_pop(state, 3);
base += 1;
return;
}
@ -1454,17 +1524,25 @@ static void RenderType(lua_State *state, compound_identity *node)
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
lua_setfield(state, ftable, "is_instance");
lua_pop(state, 2);
base += 1;
}
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++)
{
RenderType(state, children[i]);
lua_pushstring(state, children[i]->getName());
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
lua_newtable(state);
lua_newtable(state);
// Render the type structure
RenderTypeChildren(state, compound_identity::getTopScope());
lua_swap(state); // -> pairstable fieldtable
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
lua_setfield(state, -2, "sizeof");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
@ -1558,7 +1639,15 @@ static int DoAttach(lua_State *state)
lua_pushcfunction(state, meta_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;

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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())
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);
}
}

@ -1,6 +1,6 @@
/*
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -281,9 +281,10 @@ VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_
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();
bool found = false;
for (size_t i = 0; i < children.size(); i++)
{
@ -298,17 +299,32 @@ void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmp
continue;
child_next.insert(base);
found = true;
}
else
else if (child->vtable_ptr)
{
void *cptr = child->get_vmethod_ptr(vmethod_idx);
if (cptr != vmptr)
continue;
child_hosts.insert(child);
found = true;
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)

@ -1,6 +1,6 @@
/*
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
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
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -54,7 +54,6 @@ namespace DFHack
{
class Process;
class Module;
class World;
class Materials;
class Notes;
struct VersionInfo;
@ -120,8 +119,6 @@ namespace DFHack
/// Is everything OK?
bool isValid(void) { return !errorstate; }
/// get the world module
World * getWorld();
/// get the materials module
Materials * getMaterials();
/// get the notes module
@ -205,7 +202,6 @@ namespace DFHack
// Module storage
struct
{
World * pWorld;
Materials * pMaterials;
Notes * pNotes;
Graphic * pGraphic;

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -707,6 +707,9 @@ namespace DFHack {
// Global object pointers
#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
#include "df/coord2d.h"
#include "df/coord.h"

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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)
: RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1)
{}
virtual ~ServerFunctionBase() {}
RPCService *owner;
int16_t id;

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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);
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:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
~VMethodInterposeLinkBase();

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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(); }
void clear() {
x.clear();
y.clear();
z.clear();
}
coord operator[] (unsigned idx) const {
if (idx >= x.size())
return coord();

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

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -40,6 +40,7 @@ distribution.
#include "df/building_actual.h"
#include "df/body_part_raw.h"
#include "df/unit_inventory_item.h"
#include "df/job_item_vector_id.h"
namespace df
{
@ -86,7 +87,8 @@ namespace DFHack
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) {
@ -123,6 +125,10 @@ struct dfh_item
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
DFHACK_EXPORT df::item * findItemByID(int32_t id);

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -32,6 +32,7 @@ distribution.
#include "DataDefs.h"
#include "df/job_item_ref.h"
#include "df/item_type.h"
namespace df
{
@ -69,6 +70,9 @@ namespace DFHack
DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item,
df::job_item_ref::T_role role,
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);

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -47,14 +47,6 @@ namespace MapExtras
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 BlockInfo

@ -1,6 +1,6 @@
/*
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
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];
/**
* 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
* \ingroup grp_modules

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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 void dismiss(df::viewscreen *screen, bool to_first = false);
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 {

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
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 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 computeMovementSpeed(df::unit *unit);

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any
@ -37,6 +37,12 @@ distribution.
#include "DataDefs.h"
namespace df
{
struct tile_bitmask;
struct map_block;
}
namespace DFHack
{
typedef df::game_mode GameMode;
@ -55,8 +61,6 @@ namespace DFHack
class DFContextShared;
class DFHACK_EXPORT PersistentDataItem {
friend class World;
int id;
std::string key_value;
@ -65,13 +69,17 @@ namespace DFHack
public:
static const int NumInts = 7;
bool isValid() { return id != 0; }
int entry_id() { return -id; }
bool isValid() const { return id != 0; }
int entry_id() const { return -id; }
int raw_id() const { return id; }
const std::string &key() { return key_value; }
const std::string &key() const { return key_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) const { return int_values[i]; }
PersistentDataItem() : id(0), str_value(0), int_values(0) {}
PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv)
@ -83,54 +91,45 @@ namespace DFHack
* \ingroup grp_modules
* \ingroup grp_world
*/
class DFHACK_EXPORT World : public Module
namespace World
{
public:
World();
~World();
bool Start();
bool Finish();
///true if paused, false if not
bool ReadPauseState();
DFHACK_EXPORT bool ReadPauseState();
///true if paused, false if not
void SetPauseState(bool paused);
uint32_t ReadCurrentTick();
uint32_t ReadCurrentYear();
uint32_t ReadCurrentMonth();
uint32_t ReadCurrentDay();
uint8_t ReadCurrentWeather();
void SetCurrentWeather(uint8_t weather);
bool ReadGameMode(t_gamemodes& rd);
bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous
std::string ReadWorldFolder();
DFHACK_EXPORT void SetPauseState(bool paused);
DFHACK_EXPORT uint32_t ReadCurrentTick();
DFHACK_EXPORT uint32_t ReadCurrentYear();
DFHACK_EXPORT uint32_t ReadCurrentMonth();
DFHACK_EXPORT uint32_t ReadCurrentDay();
DFHACK_EXPORT uint8_t ReadCurrentWeather();
DFHACK_EXPORT void SetCurrentWeather(uint8_t weather);
DFHACK_EXPORT bool ReadGameMode(t_gamemodes& rd);
DFHACK_EXPORT bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous
DFHACK_EXPORT std::string ReadWorldFolder();
// Store data in fake historical figure names.
// This ensures that the values are stored in save games.
PersistentDataItem AddPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(int entry_id);
DFHACK_EXPORT PersistentDataItem AddPersistentData(const std::string &key);
DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key);
DFHACK_EXPORT PersistentDataItem GetPersistentData(int entry_id);
// 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.
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.
// If prefix is true, search for keys starting with key+"/".
// GetPersistentData(&vec,"",true) returns all items.
// 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);
// 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:
struct Private;
Private *d;
bool BuildPersistentCache();
};
DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false);
DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block);
}
}
#endif

@ -1,6 +1,6 @@
/*
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
warranty. In no event will the authors be held liable for any

@ -3,16 +3,16 @@
local _ENV = mkmodule('class')
-- Metatable template for a class
class_obj = {} or class_obj
class_obj = class_obj or {}
-- Methods shared by all classes
common_methods = {} or common_methods
common_methods = common_methods or {}
-- Forbidden names for class fields and methods.
reserved_names = { super = true, ATTRS = true }
-- 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.
function defclass(class,parent)
@ -133,6 +133,14 @@ function common_methods:callback(method, ...)
return dfhack.curry(self[method], self, ...)
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)
for k,v in pairs(data) do
self[k] = v

@ -125,6 +125,10 @@ end
-- Misc functions
NEWLINE = "\n"
COMMA = ","
PERIOD = "."
function printall(table)
local ok,f,t,k = pcall(pairs,table)
if ok then
@ -157,6 +161,14 @@ function xyz2pos(x,y,z)
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)
if pos then
local x = pos.x
@ -174,6 +186,14 @@ function xy2pos(x,y)
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,...)
if obj == nil or idx == nil then
return nil

@ -48,17 +48,75 @@ end
function mkdims_wh(x1,y1,w,h)
return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
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)
return x and y and x >= rect.x1 and x <= rect.x2 and y >= rect.y1 and y <= rect.y2
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
return default or {}
elseif type(pen) ~= 'table' then
@ -68,37 +126,68 @@ local function to_pen(default, pen, bg, bold)
end
end
----------------------------
-- Clipped painter object --
----------------------------
function getKeyDisplay(code)
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)
function ViewRect:init(args)
if args.view_rect then
self:assign(args.view_rect)
else
local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize())
local crect = args.clip_rect or rect
self:assign{
x = rect.x1, y = rect.y1,
x1 = rect.x1, clip_x1 = crect.x1,
y1 = rect.y1, clip_y1 = crect.y1,
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,
cur_pen = to_pen(nil, args.pen or COLOR_GREY)
}
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
function Painter.new(rect, pen)
return Painter{ rect = rect, pen = pen }
function ViewRect:isDefunct()
return (self.clip_x1 > self.clip_x2 or self.clip_y1 > self.clip_y2)
end
function Painter:isValidPos()
return self.x >= self.clip_x1 and self.x <= self.clip_x2
and self.y >= self.clip_y1 and self.y <= self.clip_y2
function ViewRect:inClipGlobalXY(x,y)
return x >= self.clip_x1 and x <= self.clip_x2
and y >= self.clip_y1 and y <= self.clip_y2
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
x,y,w,h = x.x1, x.y1, x.width, x.height
end
@ -113,17 +202,57 @@ function Painter:viewport(x,y,w,h)
clip_y1 = math.max(self.clip_y1, y1),
clip_x2 = math.min(self.clip_x2, x2),
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)
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
end
function Painter:localY()
function Painter:cursorY()
return self.y - self.y1
end
@ -210,16 +339,150 @@ function Painter:string(text,pen,...)
return self:advance(#text, nil)
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 --
------------------------
Screen = defclass(Screen)
Screen = defclass(Screen, View)
Screen.text_input_mode = false
function Screen:postinit()
self:updateLayout()
self:onResize(dscreen.getWindowSize())
end
Screen.isDismissed = dscreen.isDismissed
@ -236,14 +499,6 @@ function Screen:invalidate()
dscreen.invalidate()
end
function Screen:getWindowSize()
return dscreen.getWindowSize()
end
function Screen:getMousePos()
return dscreen.getMousePos()
end
function Screen:renderParent()
if self._native and self._native.parent then
self._native.parent:render()
@ -272,7 +527,7 @@ function Screen:onAboutToShow()
end
function Screen:onShow()
self:updateLayout()
self:onResize(dscreen.getWindowSize())
end
function Screen:dismiss()
@ -288,10 +543,11 @@ function Screen:onDestroy()
end
function Screen:onResize(w,h)
self:updateLayout()
self:updateLayout(ViewRect{ rect = mkdims_wh(0,0,w,h) })
end
function Screen:updateLayout()
function Screen:onRender()
self:render(Painter.new())
end
------------------------
@ -353,68 +609,31 @@ FramedScreen.ATTRS{
frame_title = DEFAULT_NIL,
frame_width = 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()
return self.frame_width, self.frame_height
end
function FramedScreen:updateFrameSize()
local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2
local fw, fh = self:getWantedFrameSize()
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()
function FramedScreen:computeFrame(parent_rect)
local sw, sh = parent_rect.width, parent_rect.height
local fw, fh = self:getWantedFrameSize(parent_rect)
return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1)
end
function FramedScreen:getWindowSize()
local rect = self.frame_rect
return rect.width, rect.height
end
function FramedScreen:onRenderFrame(dc, rect)
local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2
function FramedScreen:getMousePos()
local rect = self.frame_rect
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()
if rect.wgap <= 0 and rect.hgap <= 0 then
dc:clear()
else
self:renderParent()
dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2)
dc:fill(rect, self.frame_background)
end
paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title)
self:onRenderBody(Painter.new(rect))
end
function FramedScreen:onRenderBody(dc)
end
return _ENV

@ -3,6 +3,7 @@
local _ENV = mkmodule('gui.dialogs')
local gui = require('gui')
local widgets = require('gui.widgets')
local utils = require('utils')
local dscreen = dfhack.screen
@ -13,48 +14,35 @@ MessageBox.focus_path = 'MessageBox'
MessageBox.ATTRS{
frame_style = gui.GREY_LINE_FRAME,
frame_inset = 1,
-- new attrs
text = {},
on_accept = DEFAULT_NIL,
on_cancel = DEFAULT_NIL,
on_close = DEFAULT_NIL,
text_pen = DEFAULT_NIL,
}
function MessageBox:preinit(info)
if type(info.text) == 'string' then
info.text = utils.split_string(info.text, "\n")
end
function MessageBox:init(info)
self:addviews{
widgets.Label{
view_id = 'label',
text = info.text,
text_pen = info.text_pen,
frame = { l = 0, t = 0 },
auto_height = true
}
}
end
function MessageBox:getWantedFrameSize()
local text = self.text
local w = #(self.frame_title or '') + 4
w = math.max(w, 20)
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
local label = self.subviews.label
local width = math.max(self.frame_width or 0, 20, #(self.frame_title or '') + 4)
return math.max(width, label:getTextWidth()), label:getTextHeight()
end
function MessageBox:onRenderBody(dc)
if #self.text > 0 then
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
function MessageBox:onRenderFrame(dc,rect)
MessageBox.super.onRenderFrame(self,dc,rect)
if self.on_accept then
local x,y = self.frame_rect.x1+1, self.frame_rect.y2+1
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')
dc:seek(rect.x1+2,rect.y2):key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM')
end
end
@ -75,6 +63,8 @@ function MessageBox:onInput(keys)
if self.on_cancel then
self.on_cancel()
end
else
self:inputToSubviews(keys)
end
end
@ -102,8 +92,6 @@ InputBox = defclass(InputBox, MessageBox)
InputBox.focus_path = 'InputBox'
InputBox.ATTRS{
input = '',
input_pen = DEFAULT_NIL,
on_input = DEFAULT_NIL,
}
@ -111,46 +99,36 @@ function InputBox:preinit(info)
info.on_accept = nil
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()
local mw, mh = InputBox.super.getWantedFrameSize(self)
self.subviews.edit.frame.t = mh+1
return mw, mh+2
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)
if keys.SELECT then
self:dismiss()
if self.on_input then
self.on_input(self.input)
self.on_input(self.subviews.edit.text)
end
elseif keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
elseif keys._STRING then
if keys._STRING == 0 then
self.input = string.sub(self.input, 1, #self.input-1)
else
self.input = self.input .. string.char(keys._STRING)
end
self:inputToSubviews(keys)
end
end
@ -171,97 +149,72 @@ ListBox = defclass(ListBox, MessageBox)
ListBox.focus_path = 'ListBox'
ListBox.ATTRS{
selection = 0,
choices = {},
with_filter = false,
cursor_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
end
function ListBox:init(info)
self.page_top = 0
local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false)
local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
local list_widget = widgets.List
if self.with_filter then
list_widget = widgets.FilteredList
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
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
self.page_top=self.selection-(dc.height-3)
elseif self.selection<self.page_top and self.selection >0 then
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
function ListBox:moveCursor(delta)
local newsel=self.selection+delta
if #self.choices ~=0 then
if newsel<1 or newsel>#self.choices then
newsel=newsel % #self.choices
end
end
self.selection=newsel
local mw, mh = InputBox.super.getWantedFrameSize(self)
local list = self.subviews.list
list.frame.t = mh+1
return math.max(mw, list:getContentWidth()), mh+1+math.min(20,list:getContentHeight())
end
function ListBox:onInput(keys)
if keys.SELECT 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
if keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
elseif keys.CURSOR_UP then
self:moveCursor(-1)
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)
else
self:inputToSubviews(keys)
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{
frame_title = title,
text = text,
text_pen = tcolor,
choices = choices,
on_input = on_input,
on_select = on_select,
on_cancel = on_cancel,
frame_width = min_width,
with_filter = filter,
}:show()
end

@ -238,6 +238,19 @@ local function get_movement_delta(key, delta, big_step)
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)
local dx, dy, dz = get_movement_delta(key, 10, 20)
if dx then
@ -246,15 +259,21 @@ function Viewport:scrollByKey(key)
self.y1 + dy,
self.z + dz
)
else
local hk_pos = get_hotkey_target(key)
if hk_pos then
return self:centerOn(hk_pos)
else
return self
end
end
end
DwarfOverlay = defclass(DwarfOverlay, gui.Screen)
function DwarfOverlay:updateLayout()
function DwarfOverlay:updateLayout(parent_rect)
self.df_layout = getPanelLayout()
DwarfOverlay.super.updateLayout(self, parent_rect)
end
function DwarfOverlay:getViewport(old_vp)
@ -285,9 +304,11 @@ function DwarfOverlay:selectBuilding(building,cursor,viewport,gap)
end
function DwarfOverlay:propagateMoveKeys(keys)
for code,_ in pairs(MOVEMENT_KEYS) do
if keys[code] then
for code,_ in pairs(keys) do
if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then
if not HOTKEY_KEYS[code] or get_hotkey_target(code) then
self:sendInputToParent(code)
end
return code
end
end
@ -304,8 +325,8 @@ function DwarfOverlay:simulateViewScroll(keys, anchor, no_clip_cursor)
return 'A_MOVE_SAME_SQUARE'
end
for code,_ in pairs(MOVEMENT_KEYS) do
if keys[code] then
for code,_ in pairs(keys) do
if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then
local vp = self:getViewport():scrollByKey(code)
if (cursor and not no_clip_cursor) or no_clip_cursor == false then
vp = vp:reveal(anchor,4,20,4,true)
@ -328,16 +349,26 @@ function DwarfOverlay:simulateCursorMovement(keys, anchor)
return 'A_MOVE_SAME_SQUARE'
end
for code,_ in pairs(MOVEMENT_KEYS) do
if keys[code] then
for code,_ in pairs(keys) do
if MOVEMENT_KEYS[code] then
local dx, dy, dz = get_movement_delta(code, 1, 10)
local ncur = xyz2pos(cx+dx, cy+dy, cz+dz)
if dfhack.maps.isValidTilePos(ncur) then
setCursorPos(ncur)
self:getViewport():reveal(ncur,4,10,6,true):set()
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
@ -352,13 +383,14 @@ end
MenuOverlay = defclass(MenuOverlay, DwarfOverlay)
function MenuOverlay:updateLayout()
MenuOverlay.super.updateLayout(self)
self.frame_rect = self.df_layout.menu
end
MenuOverlay.ATTRS {
frame_inset = 0,
frame_background = CLEAR_PEN,
}
MenuOverlay.getWindowSize = gui.FramedScreen.getWindowSize
MenuOverlay.getMousePos = gui.FramedScreen.getMousePos
function MenuOverlay:computeFrame(parent_rect)
return self.df_layout.menu, gui.inset_frame(self.df_layout.menu, self.frame_inset)
end
function MenuOverlay:onAboutToShow(below)
MenuOverlay.super.onAboutToShow(self,below)
@ -369,7 +401,7 @@ function MenuOverlay:onAboutToShow(below)
end
end
function MenuOverlay:onRender()
function MenuOverlay:render(dc)
self:renderParent()
local menu = self.df_layout.menu
@ -380,7 +412,11 @@ function MenuOverlay:onRender()
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

@ -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
function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
function DiffSearcher:find_interactive(prompt,data_type,condition_cb,iter_limit)
enum = enum or {}
-- Loop for restarting search from scratch
@ -374,6 +374,11 @@ function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
while true do
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)
ccursor = ccursor + 1

@ -302,6 +302,24 @@ function sort_vector(vector,field,cmp)
return vector
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
function binsearch(vector,key,field,cmp,min,max)
if not(min and max) then
@ -361,6 +379,27 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos
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
function call_with_string(obj,methodname,...)
return dfhack.with_temp_object(

@ -1,6 +1,6 @@
/*
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
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