Merge branch 'master' of git://github.com/peterix/dfhack

develop
RossM 2012-04-04 23:33:49 -07:00
commit b1c41c2c9f
27 changed files with 3648 additions and 169 deletions

@ -140,7 +140,7 @@ add_subdirectory(depends)
IF(BUILD_LIBRARY)
add_subdirectory (library)
## install the default documentation files
install(FILES LICENSE Readme.html Compile.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
install(FILES LICENSE Readme.html Compile.html Lua\ API.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif()
#build the plugins

@ -0,0 +1,499 @@
##############
DFHack Lua API
##############
.. contents::
====================
DF structure wrapper
====================
DF structures described by the xml files in library/xml are exported
to lua code as a tree of objects and functions under the ``df`` global,
which broadly maps to the ``df`` namespace in C++.
**WARNING**: The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
crash the game as equivalent plain C++ code would be. E.g. NULL
pointer access is safely detected, but dangling pointers aren't.
Objects managed by the wrapper can be broadly classified into the following groups:
1. Typed object pointers (references).
References represent objects in DF memory with a known type.
In addition to fields and methods defined by the wrapped type,
every reference has some built-in properties and methods.
2. Untyped pointers
Represented as lightuserdata.
In assignment to a pointer NULL can be represented either as
``nil``, or a NULL lightuserdata; reading a NULL pointer field
returns ``nil``.
3. Named types
Objects in the ``df`` tree that represent identity of struct, class,
enum and bitfield types. They host nested named types, static
methods, builtin properties & methods, and, for enums and bitfields,
the bi-directional mapping between key names and values.
4. The ``global`` object
``df.global`` corresponds to the ``df::global`` namespace, and
behaves as a mix between a named type and a reference, containing
both nested types and fields corresponding to global symbols.
In addition to the ``global`` object and top-level types the ``df``
global also contains a few global builtin utility functions.
Typed object references
=======================
The underlying primitive lua object is userdata with a metatable.
Every structured field access produces a new userdata instance.
All typed objects have the following built-in features:
* ``ref1 == ref2``, ``tostring(ref)``
References implement equality by type & pointer value, and string conversion.
* ``pairs(ref)``
Returns an iterator for the sequence of actual C++ field names
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.
* ``ref._kind``
Returns one of: ``primitive``, ``struct``, ``container``,
or ``bitfield``, as appropriate for the referenced object.
* ``ref._type``
Returns the named type object or a string that represents
the referenced object type.
* ``ref:sizeof()``
Returns *size, address*
* ``ref:new()``
Allocates a new instance of the same type, and copies data
from the current object.
* ``ref:delete()``
Destroys the object with the C++ ``delete`` operator.
If destructor is not available, returns *false*.
**WARNING**: the lua reference object remains as a dangling
pointer, like a raw C++ pointer would.
* ``ref:assign(object)``
Assigns data from object to ref. Object must either be another
ref of a compatible type, or a lua table; in the latter case
special recursive assignment rules are applied.
* ``ref:_displace(index[,step])``
Returns a new reference with the pointer adjusted by index*step.
Step defaults to the natural object size.
Primitive references
--------------------
References of the *_kind* ``'primitive'`` are used for objects
that don't fit any of the other reference types. Such
references can only appear as a value of a pointer field,
or as a result of calling the ``_field()`` method.
They behave as structs with one field ``value`` of the right type.
Struct references
-----------------
Struct references are used for class and struct objects.
They implement the following features:
* ``ref.field``, ``ref.field = value``
Valid fields of the structure may be accessed by subscript.
Primitive typed fields, i.e. numbers & strings, are converted
to/from matching lua values. The value of a pointer is a reference
to the target, or nil/NULL. Complex types are represented by
a reference to the field within the structure; unless recursive
lua table assignment is used, such fields can only be read.
**NOTE:** In case of inheritance, *superclass* fields have precedence
over the subclass, but fields shadowed in this way can still
be accessed as ``ref['subclasstype.field']``.
This shadowing order is necessary because vtable-based classes
are automatically exposed in their exact type, and the reverse
rule would make access to superclass fields unreliable.
* ``ref._field(field)``
Returns a reference to a valid field. That is, unlike regular
subscript, it returns a reference to the field within the structure
even for primitive typed fields and pointers.
* ``ref:vmethod(args...)``
Named virtual methods are also exposed, subject to the same
shadowing rules.
* ``pairs(ref)``
Enumerates all real fields (but not methods) in memory
(= declaration) order.
Container references
--------------------
Containers represent vectors and arrays, possibly resizable.
A container field can associate an enum to the container
reference, which allows accessing elements using string keys
instead of numerical indices.
Implemented features:
* ``ref._enum``
If the container has an associated enum, returns the matching
named type object.
* ``#ref``
Returns the *length* of the container.
* ``ref[index]``
Accesses the container element, using either a *0-based* numerical
index, or, if an enum is associated, a valid enum key string.
Accessing an invalid index is an error, but some container types
may return a default value, or auto-resize instead for convenience.
Currently this relaxed mode is implemented by df-flagarray aka BitArray.
* ``ref._field(index)``
Like with structs, returns a pointer to the array element, if possible.
Flag and bit arrays cannot return such pointer, so it fails with an error.
* ``pairs(ref)``, ``ipairs(ref)``
If the container has no associated enum, both behave identically,
iterating over numerical indices in order. Otherwise, ipairs still
uses numbers, while pairs tries to substitute enum keys whenever
possible.
* ``ref:resize(new_size)``
Resizes the container if supported, or fails with an error.
* ``ref:insert(index,item)``
Inserts a new item at the specified index. To add at the end,
use ``#ref`` as index.
* ``ref:erase(index)``
Removes the element at the given valid index.
Bitfield references
-------------------
Bitfields behave like special fixed-size containers.
The ``_enum`` property points to the bitfield type.
Numerical indices correspond to the shift value,
and if a subfield occupies multiple bits, the
``ipairs`` order would have a gap.
Named types
===========
Named types are exposed in the ``df`` tree with names identical
to the C++ version, except for the ``::`` vs ``.`` difference.
All types and the global object have the following features:
* ``type._kind``
Evaluates to one of ``struct-type``, ``class-type``, ``enum-type``,
``bitfield-type`` or ``global``.
* ``type._identity``
Contains a lightuserdata pointing to the underlying
DFHack::type_instance object.
Types excluding the global object also support:
* ``type:sizeof()``
Returns the size of an object of the type.
* ``type:new()``
Creates a new instance of an object of the type.
* ``type:is_instance(object)``
Returns true if object is same or subclass type, or a reference
to an object of same or subclass type. It is permissible to pass
nil, NULL or non-wrapper value as object; in this case the
method returns nil.
In addition to this, enum and bitfield types contain a
bi-directional mapping between key strings and values, and
also map ``_first_item`` and ``_last_item`` to the min and
max values.
Struct and class types with instance-vector attribute in the
xml have a ``type.find(key)`` function that wraps the find
method provided in C++.
Global functions
================
The ``df`` table itself contains the following functions and values:
* ``NULL``, ``df.NULL``
Contains the NULL lightuserdata.
* ``df.isnull(obj)``
Evaluates to true if obj is nil or NULL; false otherwise.
* ``df.isvalid(obj[,allow_null])``
For supported objects returns one of ``type``, ``voidptr``, ``ref``.
If *allow_null* is true, and obj is nil or NULL, returns ``null``.
Otherwise returns *nil*.
* ``df.sizeof(obj)``
For types and refs identical to ``obj:sizeof()``.
For lightuserdata returns *nil, address*
* ``df.new(obj)``, ``df.delete(obj)``, ``df.assign(obj, obj2)``
Equivalent to using the matching methods of obj.
* ``df._displace(obj,index[,step])``
For refs equivalent to the method, but also works with
lightuserdata (step is mandatory then).
* ``df.is_instance(type,obj)``
Equivalent to the method, but also allows a reference as proxy for its type.
Recursive table assignment
==========================
Recursive assignment is invoked when a lua table is assigned
to a C++ object or field, i.e. one of:
* ``ref:assign{...}``
* ``ref.field = {...}``
The general mode of operation is that all fields of the table
are assigned to the fields of the target structure, roughly
emulating the following code::
function rec_assign(ref,table)
for key,value in pairs(table) do
ref[key] = value
end
end
Since assigning a table to a field using = invokes the same
process, it is recursive.
There are however some variations to this process depending
on the type of the field being assigned to:
1. If the table contains an ``assign`` field, it is
applied first, using the ``ref:assign(value)`` method.
It is never assigned as a usual field.
2. When a table is assigned to a non-NULL pointer field
using the ``ref.field = {...}`` syntax, it is applied
to the target of the pointer instead.
If the pointer is NULL, the table is checked for a ``new`` field:
a. If it is *nil* or *false*, assignment fails with an error.
b. If it is *true*, the pointer is initialized with a newly
allocated object of the declared target type of the pointer.
c. Otherwise, ``table.new`` must be a named type, or an
object of a type compatible with the pointer. The pointer
is initialized with the result of calling ``table.new:new()``.
After this auto-vivification process, assignment proceeds
as if the pointer wasn't NULL.
Obviously, the ``new`` field inside the table is always skipped
during the actual per-field assignment processing.
3. If the target of the assignment is a container, a separate
rule set is used:
a. If the table contains neither ``assign`` nor ``resize``
fields, it is interpreted as an ordinary *1-based* lua
array. The container is resized to the #-size of the
table, and elements are assigned in numeric order::
ref:resize(#table);
for i=1,#table do ref[i-1] = table[i] end
b. Otherwise, ``resize`` must be *true*, *false*, or
an explicit number. If it is not false, the container
is resized. After that the usual struct-like 'pairs'
assignment is performed.
In case ``resize`` is *true*, the size is computed
by scanning the table for the largest numeric key.
This means that in order to reassign only one element of
a container using this system, it is necessary to use::
{ resize=false, [idx]=value }
Since nil inside a table is indistinguishable from missing key,
it is necessary to use ``df.NULL`` as a null pointer value.
This system is intended as a way to define a nested object
tree using pure lua data structures, and then materialize it in
C++ memory in one go. Note that if pointer auto-vivification
is used, an error in the middle of the recursive walk would
not destroy any objects allocated in this way, so the user
should be prepared to catch the error and do the necessary
cleanup.
================
DFHack utilities
================
DFHack utility functions are placed in the ``dfhack`` global tree.
Currently it defines the following features:
* ``dfhack.print(args...)``
Output tab-separated args as standard lua print would do,
but without a newline.
* ``print(args...)``, ``dfhack.println(args...)``
A replacement of the standard library print function that
works with DFHack output infrastructure.
* ``dfhack.printerr(args...)``
Same as println; intended for errors. Uses red color and logs to stderr.log.
* ``dfhack.color([color])``
Sets the current output color. If color is *nil* or *-1*, resets to default.
* ``dfhack.is_interactive()``
Checks if the thread can access the interactive console and returns *true* or *false*.
* ``dfhack.lineedit([prompt[,history_filename]])``
If the thread owns the interactive console, shows a prompt
and returns the entered string. Otherwise returns *nil, error*.
* ``dfhack.interpreter([prompt[,env[,history_filename]]])``
Starts an interactive lua interpreter, using the specified prompt
string, global environment and command-line history file.
If the interactive console is not accessible, returns *nil, error*.
* ``dfhack.pcall(f[,args...])``
Invokes f via xpcall, using an error function that attaches
a stack trace to the error. The same function is used by SafeCall
in C++, and dfhack.safecall.
The returned error is a table with separate ``message`` and
``stacktrace`` string fields; it implements ``__tostring``.
* ``safecall(f[,args...])``, ``dfhack.safecall(f[,args...])``
Just like pcall, but also prints the error using printerr before
returning. Intended as a convenience function.
* ``dfhack.with_suspend(f[,args...])``
Calls ``f`` with arguments after grabbing the DF core suspend lock.
Suspending is necessary for accessing a consistent state of DF memory.
Returned values and errors are propagated through after releasing
the lock. It is safe to nest suspends.
Every thread is allowed only one suspend per DF frame, so it is best
to group operations together in one big critical section. A plugin
can choose to run all lua code inside a C++-side suspend lock.
Persistent configuration storage
================================
This api is intended for storing configuration options in the world itself.
It probably should be restricted to data that is world-dependent.
Entries are identified by a string ``key``, but it is also possible to manage
multiple entries with the same key; their identity is determined by ``entry_id``.
Every entry has a mutable string ``value``, and an array of 7 mutable ``ints``.
* ``dfhack.persistent.get(key)``, ``entry:get()``
Retrieves a persistent config record with the given string key,
or refreshes an already retrieved entry. If there are multiple
entries with the same key, it is undefined which one is retrieved
by the first version of the call.
Returns entry, or *nil* if not found.
* ``dfhack.persistent.delete(key)``, ``entry:delete()``
Removes an existing entry. Returns *true* if succeeded.
* ``dfhack.persistent.get_all(key[,match_prefix])``
Retrieves all entries with the same key, or starting with key..'/'.
Calling ``get_all('',true)`` will match all entries.
If none found, returns nil; otherwise returns an array of entries.
* ``dfhack.persistent.save({key=str1, ...}[,new])``, ``entry:save([new])``
Saves changes in an entry, or creates a new one. Passing true as
new forces creation of a new entry even if one already exists;
otherwise the existing one is simply updated.
Returns *entry, did_create_new*
Since the data is hidden in data structures owned by the DF world,
and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.

@ -0,0 +1,759 @@
<?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.8.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 $
: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 {
margin-left: 2em ;
margin-right: 2em }
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="dfhack-lua-api">
<h1 class="title">DFHack Lua API</h1>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#df-structure-wrapper" id="id1">DF structure wrapper</a><ul>
<li><a class="reference internal" href="#typed-object-references" id="id2">Typed object references</a><ul>
<li><a class="reference internal" href="#primitive-references" id="id3">Primitive references</a></li>
<li><a class="reference internal" href="#struct-references" id="id4">Struct references</a></li>
<li><a class="reference internal" href="#container-references" id="id5">Container references</a></li>
<li><a class="reference internal" href="#bitfield-references" id="id6">Bitfield references</a></li>
</ul>
</li>
<li><a class="reference internal" href="#named-types" id="id7">Named types</a></li>
<li><a class="reference internal" href="#global-functions" id="id8">Global functions</a></li>
<li><a class="reference internal" href="#recursive-table-assignment" id="id9">Recursive table assignment</a></li>
</ul>
</li>
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="df-structure-wrapper">
<h1><a class="toc-backref" href="#id1">DF structure wrapper</a></h1>
<p>DF structures described by the xml files in library/xml are exported
to lua code as a tree of objects and functions under the <tt class="docutils literal">df</tt> global,
which broadly maps to the <tt class="docutils literal">df</tt> namespace in C++.</p>
<p><strong>WARNING</strong>: The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
crash the game as equivalent plain C++ code would be. E.g. NULL
pointer access is safely detected, but dangling pointers aren't.</p>
<p>Objects managed by the wrapper can be broadly classified into the following groups:</p>
<ol class="arabic">
<li><p class="first">Typed object pointers (references).</p>
<p>References represent objects in DF memory with a known type.</p>
<p>In addition to fields and methods defined by the wrapped type,
every reference has some built-in properties and methods.</p>
</li>
<li><p class="first">Untyped pointers</p>
<p>Represented as lightuserdata.</p>
<p>In assignment to a pointer NULL can be represented either as
<tt class="docutils literal">nil</tt>, or a NULL lightuserdata; reading a NULL pointer field
returns <tt class="docutils literal">nil</tt>.</p>
</li>
<li><p class="first">Named types</p>
<p>Objects in the <tt class="docutils literal">df</tt> tree that represent identity of struct, class,
enum and bitfield types. They host nested named types, static
methods, builtin properties &amp; methods, and, for enums and bitfields,
the bi-directional mapping between key names and values.</p>
</li>
<li><p class="first">The <tt class="docutils literal">global</tt> object</p>
<p><tt class="docutils literal">df.global</tt> corresponds to the <tt class="docutils literal"><span class="pre">df::global</span></tt> namespace, and
behaves as a mix between a named type and a reference, containing
both nested types and fields corresponding to global symbols.</p>
</li>
</ol>
<p>In addition to the <tt class="docutils literal">global</tt> object and top-level types the <tt class="docutils literal">df</tt>
global also contains a few global builtin utility functions.</p>
<div class="section" id="typed-object-references">
<h2><a class="toc-backref" href="#id2">Typed object references</a></h2>
<p>The underlying primitive lua object is userdata with a metatable.
Every structured field access produces a new userdata instance.</p>
<p>All typed objects have the following built-in features:</p>
<ul>
<li><p class="first"><tt class="docutils literal">ref1 == ref2</tt>, <tt class="docutils literal">tostring(ref)</tt></p>
<p>References implement equality by type &amp; pointer value, and string conversion.</p>
</li>
<li><p class="first"><tt class="docutils literal">pairs(ref)</tt></p>
<p>Returns an iterator for the sequence of actual C++ field names
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref._kind</tt></p>
<p>Returns one of: <tt class="docutils literal">primitive</tt>, <tt class="docutils literal">struct</tt>, <tt class="docutils literal">container</tt>,
or <tt class="docutils literal">bitfield</tt>, as appropriate for the referenced object.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref._type</tt></p>
<p>Returns the named type object or a string that represents
the referenced object type.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:sizeof()</tt></p>
<p>Returns <em>size, address</em></p>
</li>
<li><p class="first"><tt class="docutils literal">ref:new()</tt></p>
<p>Allocates a new instance of the same type, and copies data
from the current object.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:delete()</tt></p>
<p>Destroys the object with the C++ <tt class="docutils literal">delete</tt> operator.
If destructor is not available, returns <em>false</em>.</p>
<p><strong>WARNING</strong>: the lua reference object remains as a dangling
pointer, like a raw C++ pointer would.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:assign(object)</tt></p>
<p>Assigns data from object to ref. Object must either be another
ref of a compatible type, or a lua table; in the latter case
special recursive assignment rules are applied.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">ref:_displace(index[,step])</span></tt></p>
<p>Returns a new reference with the pointer adjusted by index*step.
Step defaults to the natural object size.</p>
</li>
</ul>
<div class="section" id="primitive-references">
<h3><a class="toc-backref" href="#id3">Primitive references</a></h3>
<p>References of the <em>_kind</em> <tt class="docutils literal">'primitive'</tt> are used for objects
that don't fit any of the other reference types. Such
references can only appear as a value of a pointer field,
or as a result of calling the <tt class="docutils literal">_field()</tt> method.</p>
<p>They behave as structs with one field <tt class="docutils literal">value</tt> of the right type.</p>
</div>
<div class="section" id="struct-references">
<h3><a class="toc-backref" href="#id4">Struct references</a></h3>
<p>Struct references are used for class and struct objects.</p>
<p>They implement the following features:</p>
<ul>
<li><p class="first"><tt class="docutils literal">ref.field</tt>, <tt class="docutils literal">ref.field = value</tt></p>
<p>Valid fields of the structure may be accessed by subscript.</p>
<p>Primitive typed fields, i.e. numbers &amp; strings, are converted
to/from matching lua values. The value of a pointer is a reference
to the target, or nil/NULL. Complex types are represented by
a reference to the field within the structure; unless recursive
lua table assignment is used, such fields can only be read.</p>
<p><strong>NOTE:</strong> In case of inheritance, <em>superclass</em> fields have precedence
over the subclass, but fields shadowed in this way can still
be accessed as <tt class="docutils literal"><span class="pre">ref['subclasstype.field']</span></tt>.
This shadowing order is necessary because vtable-based classes
are automatically exposed in their exact type, and the reverse
rule would make access to superclass fields unreliable.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref._field(field)</tt></p>
<p>Returns a reference to a valid field. That is, unlike regular
subscript, it returns a reference to the field within the structure
even for primitive typed fields and pointers.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">ref:vmethod(args...)</span></tt></p>
<p>Named virtual methods are also exposed, subject to the same
shadowing rules.</p>
</li>
<li><p class="first"><tt class="docutils literal">pairs(ref)</tt></p>
<p>Enumerates all real fields (but not methods) in memory
(= declaration) order.</p>
</li>
</ul>
</div>
<div class="section" id="container-references">
<h3><a class="toc-backref" href="#id5">Container references</a></h3>
<p>Containers represent vectors and arrays, possibly resizable.</p>
<p>A container field can associate an enum to the container
reference, which allows accessing elements using string keys
instead of numerical indices.</p>
<p>Implemented features:</p>
<ul>
<li><p class="first"><tt class="docutils literal">ref._enum</tt></p>
<p>If the container has an associated enum, returns the matching
named type object.</p>
</li>
<li><p class="first"><tt class="docutils literal">#ref</tt></p>
<p>Returns the <em>length</em> of the container.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref[index]</tt></p>
<p>Accesses the container element, using either a <em>0-based</em> numerical
index, or, if an enum is associated, a valid enum key string.</p>
<p>Accessing an invalid index is an error, but some container types
may return a default value, or auto-resize instead for convenience.
Currently this relaxed mode is implemented by df-flagarray aka BitArray.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref._field(index)</tt></p>
<p>Like with structs, returns a pointer to the array element, if possible.
Flag and bit arrays cannot return such pointer, so it fails with an error.</p>
</li>
<li><p class="first"><tt class="docutils literal">pairs(ref)</tt>, <tt class="docutils literal">ipairs(ref)</tt></p>
<p>If the container has no associated enum, both behave identically,
iterating over numerical indices in order. Otherwise, ipairs still
uses numbers, while pairs tries to substitute enum keys whenever
possible.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:resize(new_size)</tt></p>
<p>Resizes the container if supported, or fails with an error.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:insert(index,item)</tt></p>
<p>Inserts a new item at the specified index. To add at the end,
use <tt class="docutils literal">#ref</tt> as index.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref:erase(index)</tt></p>
<p>Removes the element at the given valid index.</p>
</li>
</ul>
</div>
<div class="section" id="bitfield-references">
<h3><a class="toc-backref" href="#id6">Bitfield references</a></h3>
<p>Bitfields behave like special fixed-size containers.
The <tt class="docutils literal">_enum</tt> property points to the bitfield type.</p>
<p>Numerical indices correspond to the shift value,
and if a subfield occupies multiple bits, the
<tt class="docutils literal">ipairs</tt> order would have a gap.</p>
</div>
</div>
<div class="section" id="named-types">
<h2><a class="toc-backref" href="#id7">Named types</a></h2>
<p>Named types are exposed in the <tt class="docutils literal">df</tt> tree with names identical
to the C++ version, except for the <tt class="docutils literal">::</tt> vs <tt class="docutils literal">.</tt> difference.</p>
<p>All types and the global object have the following features:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type._kind</tt></p>
<p>Evaluates to one of <tt class="docutils literal"><span class="pre">struct-type</span></tt>, <tt class="docutils literal"><span class="pre">class-type</span></tt>, <tt class="docutils literal"><span class="pre">enum-type</span></tt>,
<tt class="docutils literal"><span class="pre">bitfield-type</span></tt> or <tt class="docutils literal">global</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">type._identity</tt></p>
<p>Contains a lightuserdata pointing to the underlying
DFHack::type_instance object.</p>
</li>
</ul>
<p>Types excluding the global object also support:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type:sizeof()</tt></p>
<p>Returns the size of an object of the type.</p>
</li>
<li><p class="first"><tt class="docutils literal">type:new()</tt></p>
<p>Creates a new instance of an object of the type.</p>
</li>
<li><p class="first"><tt class="docutils literal">type:is_instance(object)</tt></p>
<p>Returns true if object is same or subclass type, or a reference
to an object of same or subclass type. It is permissible to pass
nil, NULL or non-wrapper value as object; in this case the
method returns nil.</p>
</li>
</ul>
<p>In addition to this, enum and bitfield types contain a
bi-directional mapping between key strings and values, and
also map <tt class="docutils literal">_first_item</tt> and <tt class="docutils literal">_last_item</tt> to the min and
max values.</p>
<p>Struct and class types with instance-vector attribute in the
xml have a <tt class="docutils literal">type.find(key)</tt> function that wraps the find
method provided in C++.</p>
</div>
<div class="section" id="global-functions">
<h2><a class="toc-backref" href="#id8">Global functions</a></h2>
<p>The <tt class="docutils literal">df</tt> table itself contains the following functions and values:</p>
<ul>
<li><p class="first"><tt class="docutils literal">NULL</tt>, <tt class="docutils literal">df.NULL</tt></p>
<p>Contains the NULL lightuserdata.</p>
</li>
<li><p class="first"><tt class="docutils literal">df.isnull(obj)</tt></p>
<p>Evaluates to true if obj is nil or NULL; false otherwise.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">df.isvalid(obj[,allow_null])</span></tt></p>
<p>For supported objects returns one of <tt class="docutils literal">type</tt>, <tt class="docutils literal">voidptr</tt>, <tt class="docutils literal">ref</tt>.</p>
<p>If <em>allow_null</em> is true, and obj is nil or NULL, returns <tt class="docutils literal">null</tt>.</p>
<p>Otherwise returns <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">df.sizeof(obj)</tt></p>
<p>For types and refs identical to <tt class="docutils literal">obj:sizeof()</tt>.
For lightuserdata returns <em>nil, address</em></p>
</li>
<li><p class="first"><tt class="docutils literal">df.new(obj)</tt>, <tt class="docutils literal">df.delete(obj)</tt>, <tt class="docutils literal">df.assign(obj, obj2)</tt></p>
<p>Equivalent to using the matching methods of obj.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">df._displace(obj,index[,step])</span></tt></p>
<p>For refs equivalent to the method, but also works with
lightuserdata (step is mandatory then).</p>
</li>
<li><p class="first"><tt class="docutils literal">df.is_instance(type,obj)</tt></p>
<p>Equivalent to the method, but also allows a reference as proxy for its type.</p>
</li>
</ul>
</div>
<div class="section" id="recursive-table-assignment">
<h2><a class="toc-backref" href="#id9">Recursive table assignment</a></h2>
<p>Recursive assignment is invoked when a lua table is assigned
to a C++ object or field, i.e. one of:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">ref:assign{...}</span></tt></li>
<li><tt class="docutils literal">ref.field = <span class="pre">{...}</span></tt></li>
</ul>
<p>The general mode of operation is that all fields of the table
are assigned to the fields of the target structure, roughly
emulating the following code:</p>
<pre class="literal-block">
function rec_assign(ref,table)
for key,value in pairs(table) do
ref[key] = value
end
end
</pre>
<p>Since assigning a table to a field using = invokes the same
process, it is recursive.</p>
<p>There are however some variations to this process depending
on the type of the field being assigned to:</p>
<ol class="arabic">
<li><p class="first">If the table contains an <tt class="docutils literal">assign</tt> field, it is
applied first, using the <tt class="docutils literal">ref:assign(value)</tt> method.
It is never assigned as a usual field.</p>
</li>
<li><p class="first">When a table is assigned to a non-NULL pointer field
using the <tt class="docutils literal">ref.field = <span class="pre">{...}</span></tt> syntax, it is applied
to the target of the pointer instead.</p>
<p>If the pointer is NULL, the table is checked for a <tt class="docutils literal">new</tt> field:</p>
<ol class="loweralpha simple">
<li>If it is <em>nil</em> or <em>false</em>, assignment fails with an error.</li>
<li>If it is <em>true</em>, the pointer is initialized with a newly
allocated object of the declared target type of the pointer.</li>
<li>Otherwise, <tt class="docutils literal">table.new</tt> must be a named type, or an
object of a type compatible with the pointer. The pointer
is initialized with the result of calling <tt class="docutils literal">table.new:new()</tt>.</li>
</ol>
<p>After this auto-vivification process, assignment proceeds
as if the pointer wasn't NULL.</p>
<p>Obviously, the <tt class="docutils literal">new</tt> field inside the table is always skipped
during the actual per-field assignment processing.</p>
</li>
<li><p class="first">If the target of the assignment is a container, a separate
rule set is used:</p>
<ol class="loweralpha">
<li><p class="first">If the table contains neither <tt class="docutils literal">assign</tt> nor <tt class="docutils literal">resize</tt>
fields, it is interpreted as an ordinary <em>1-based</em> lua
array. The container is resized to the #-size of the
table, and elements are assigned in numeric order:</p>
<pre class="literal-block">
ref:resize(#table);
for i=1,#table do ref[i-1] = table[i] end
</pre>
</li>
<li><p class="first">Otherwise, <tt class="docutils literal">resize</tt> must be <em>true</em>, <em>false</em>, or
an explicit number. If it is not false, the container
is resized. After that the usual struct-like 'pairs'
assignment is performed.</p>
<p>In case <tt class="docutils literal">resize</tt> is <em>true</em>, the size is computed
by scanning the table for the largest numeric key.</p>
</li>
</ol>
<p>This means that in order to reassign only one element of
a container using this system, it is necessary to use:</p>
<pre class="literal-block">
{ resize=false, [idx]=value }
</pre>
</li>
</ol>
<p>Since nil inside a table is indistinguishable from missing key,
it is necessary to use <tt class="docutils literal">df.NULL</tt> as a null pointer value.</p>
<p>This system is intended as a way to define a nested object
tree using pure lua data structures, and then materialize it in
C++ memory in one go. Note that if pointer auto-vivification
is used, an error in the middle of the recursive walk would
not destroy any objects allocated in this way, so the user
should be prepared to catch the error and do the necessary
cleanup.</p>
</div>
</div>
<div class="section" id="dfhack-utilities">
<h1><a class="toc-backref" href="#id10">DFHack utilities</a></h1>
<p>DFHack utility functions are placed in the <tt class="docutils literal">dfhack</tt> global tree.</p>
<p>Currently it defines the following features:</p>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.print(args...)</span></tt></p>
<p>Output tab-separated args as standard lua print would do,
but without a newline.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">print(args...)</span></tt>, <tt class="docutils literal"><span class="pre">dfhack.println(args...)</span></tt></p>
<p>A replacement of the standard library print function that
works with DFHack output infrastructure.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.printerr(args...)</span></tt></p>
<p>Same as println; intended for errors. Uses red color and logs to stderr.log.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.color([color])</span></tt></p>
<p>Sets the current output color. If color is <em>nil</em> or <em>-1</em>, resets to default.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.is_interactive()</tt></p>
<p>Checks if the thread can access the interactive console and returns <em>true</em> or <em>false</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.lineedit([prompt[,history_filename]])</span></tt></p>
<p>If the thread owns the interactive console, shows a prompt
and returns the entered string. Otherwise returns <em>nil, error</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.interpreter([prompt[,env[,history_filename]]])</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>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.pcall(f[,args...])</span></tt></p>
<p>Invokes f via xpcall, using an error function that attaches
a stack trace to the error. The same function is used by SafeCall
in C++, and dfhack.safecall.</p>
<p>The returned error is a table with separate <tt class="docutils literal">message</tt> and
<tt class="docutils literal">stacktrace</tt> string fields; it implements <tt class="docutils literal">__tostring</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">safecall(f[,args...])</span></tt>, <tt class="docutils literal"><span class="pre">dfhack.safecall(f[,args...])</span></tt></p>
<p>Just like pcall, but also prints the error using printerr before
returning. Intended as a convenience function.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_suspend(f[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">f</tt> with arguments after grabbing the DF core suspend lock.
Suspending is necessary for accessing a consistent state of DF memory.</p>
<p>Returned values and errors are propagated through after releasing
the lock. It is safe to nest suspends.</p>
<p>Every thread is allowed only one suspend per DF frame, so it is best
to group operations together in one big critical section. A plugin
can choose to run all lua code inside a C++-side suspend lock.</p>
</li>
</ul>
<div class="section" id="persistent-configuration-storage">
<h2><a class="toc-backref" href="#id11">Persistent configuration storage</a></h2>
<p>This api is intended for storing configuration options in the world itself.
It probably should be restricted to data that is world-dependent.</p>
<p>Entries are identified by a string <tt class="docutils literal">key</tt>, but it is also possible to manage
multiple entries with the same key; their identity is determined by <tt class="docutils literal">entry_id</tt>.
Every entry has a mutable string <tt class="docutils literal">value</tt>, and an array of 7 mutable <tt class="docutils literal">ints</tt>.</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.persistent.get(key)</tt>, <tt class="docutils literal">entry:get()</tt></p>
<p>Retrieves a persistent config record with the given string key,
or refreshes an already retrieved entry. If there are multiple
entries with the same key, it is undefined which one is retrieved
by the first version of the call.</p>
<p>Returns entry, or <em>nil</em> if not found.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.persistent.delete(key)</tt>, <tt class="docutils literal">entry:delete()</tt></p>
<p>Removes an existing entry. Returns <em>true</em> if succeeded.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.persistent.get_all(key[,match_prefix])</span></tt></p>
<p>Retrieves all entries with the same key, or starting with key..'/'.
Calling <tt class="docutils literal"><span class="pre">get_all('',true)</span></tt> will match all entries.</p>
<p>If none found, returns nil; otherwise returns an array of entries.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.persistent.save({key=str1,</span> <span class="pre">...}[,new])</span></tt>, <tt class="docutils literal"><span class="pre">entry:save([new])</span></tt></p>
<p>Saves changes in an entry, or creates a new one. Passing true as
new forces creation of a new entry even if one already exists;
otherwise the existing one is simply updated.
Returns <em>entry, did_create_new</em></p>
</li>
</ul>
<p>Since the data is hidden in data structures owned by the DF world,
and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p>
</div>
</div>
</div>
</body>
</html>

@ -798,3 +798,58 @@ Export the current loaded map as a file. This will be eventually usable with vis
dwarfexport
===========
Export dwarves to RuneSmith-compatible XML.
zone
====
Helps a bit with managing activity zones (pens, pastures and pits).
Options:
--------
:set: Set zone under cursor as default for future assigns.
:assign: Assign unit(s) to the pen or pit marked with the 'set' command. If no filters are set a unit must be selected in the in-game ui. Can also be followed by a valid zone id which will be set instead.
:unassign: Unassign selected creature from it's zone.
:autonestbox: Assign all (unless count is specified) unpastured female egg-layers to empty pens which contain a nestbox. If the pen is bigger than 1x1 the nestbox must be placed at the top left corner to be recognized. Only 1 unit will be assigned per pen.
:uinfo: Print info about unit(s). If no filters are set a unit must be selected in the in-game ui.
:zinfo: Print info about zone(s). If no filters are set zones under the cursor are listed.
:verbose: Print some more info.
:filters: Print list of valid filter options.
:examples: Print some usage examples.
Filters:
--------
:all: Process all units (to be used with additional filters).
:count: Must be followed by a number. Process only n units (to be used with additional filters).
:race: Must be followed by a race raw id (e.g. BIRD_TURKEY, ALPACA etc).
:unassigned: Not assigned to zone, chain or built cage.
:caged: In a built cage.
:uncaged: Not in a cage (in case you want your stockpiles to be left alone).
:foreign: Not of own civilization (i.e. own fortress).
:own: From own civilization (i.e. own fortress).
:war: Trained war creature.
:tamed: Creature is tame.
:trained: Creature is trained.
:untrained: Creature is untrained.
:male: Creature is male.
:female: Creature is female.
:egglayer: Race lays eggs.
:grazer: Race is a grazer.
:milkable: Race is milkable.
:minage: Minimum age. Must be followed by number.
:maxage: Maximum age. Must be followed by number.
Usage with single units
-----------------------
One convenient way to use the zone tool is to bind the command 'zone assign' to a hotkey, maybe also the command 'zone set'. Place the in-game cursor over a pen/pasture or pit, use 'zone set' to mark it. Then you can select units on the map (in 'v' or 'k' mode), in the unit list or from inside cages and use 'zone assign' to assign them to their new home. Allows pitting your own dwarves, by the way.
Usage with filters
------------------
All filters can be used together with the 'assign' command. The only restriction is that it's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. Usually you should always use the filter 'own' (which implies tame) unless you want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless you specify 'race DWARF' (so it's safe to use 'assign all own' to one big pasture if you want to have all your animals at the same place). 'egglayer' and 'milkable' should be used together with 'female' unless you have a mod with egg-laying male elves who give milk or whatever.
``zone assign all own ALPACA minage 3 maxage 10``
Assign all own alpacas who are between 3 and 10 years old to the selected pasture.
``zone assign all own caged grazer``
Assign all own grazers who are sitting in cages on stockpiles (e.g. after buying them from merchants) to the selected pasture.
``zone assign count 5 own female milkable``
Assign up to 5 own female milkable creatures to the selected pasture.
``zone assign all own race DWARF maxage 2``
Throw all useless kids into a pit :)

@ -467,33 +467,35 @@ access DF memory and allow for easier development of new tools.</p>
</ul>
</li>
<li><a class="reference internal" href="#tubefill" id="id125">tubefill</a></li>
<li><a class="reference internal" href="#vdig" id="id126">vdig</a></li>
<li><a class="reference internal" href="#vdigx" id="id127">vdigx</a></li>
<li><a class="reference internal" href="#expdig" id="id128">expdig</a><ul>
<li><a class="reference internal" href="#patterns" id="id129">Patterns:</a></li>
<li><a class="reference internal" href="#filters" id="id130">Filters:</a></li>
<li><a class="reference internal" href="#id23" id="id131">Examples:</a></li>
<li><a class="reference internal" href="#digv" id="id126">digv</a></li>
<li><a class="reference internal" href="#digvx" id="id127">digvx</a></li>
<li><a class="reference internal" href="#digl" id="id128">digl</a></li>
<li><a class="reference internal" href="#diglx" id="id129">diglx</a></li>
<li><a class="reference internal" href="#digexp" id="id130">digexp</a><ul>
<li><a class="reference internal" href="#patterns" id="id131">Patterns:</a></li>
<li><a class="reference internal" href="#filters" id="id132">Filters:</a></li>
<li><a class="reference internal" href="#id23" id="id133">Examples:</a></li>
</ul>
</li>
<li><a class="reference internal" href="#digcircle" id="id132">digcircle</a><ul>
<li><a class="reference internal" href="#shape" id="id133">Shape:</a></li>
<li><a class="reference internal" href="#action" id="id134">Action:</a></li>
<li><a class="reference internal" href="#designation-types" id="id135">Designation types:</a></li>
<li><a class="reference internal" href="#id24" id="id136">Examples:</a></li>
<li><a class="reference internal" href="#digcircle" id="id134">digcircle</a><ul>
<li><a class="reference internal" href="#shape" id="id135">Shape:</a></li>
<li><a class="reference internal" href="#action" id="id136">Action:</a></li>
<li><a class="reference internal" href="#designation-types" id="id137">Designation types:</a></li>
<li><a class="reference internal" href="#id24" id="id138">Examples:</a></li>
</ul>
</li>
<li><a class="reference internal" href="#weather" id="id137">weather</a><ul>
<li><a class="reference internal" href="#id25" id="id138">Options:</a></li>
<li><a class="reference internal" href="#weather" id="id139">weather</a><ul>
<li><a class="reference internal" href="#id25" id="id140">Options:</a></li>
</ul>
</li>
<li><a class="reference internal" href="#workflow" id="id139">workflow</a><ul>
<li><a class="reference internal" href="#id26" id="id140">Usage</a></li>
<li><a class="reference internal" href="#function" id="id141">Function</a></li>
<li><a class="reference internal" href="#constraint-examples" id="id142">Constraint examples</a></li>
<li><a class="reference internal" href="#workflow" id="id141">workflow</a><ul>
<li><a class="reference internal" href="#id26" id="id142">Usage</a></li>
<li><a class="reference internal" href="#function" id="id143">Function</a></li>
<li><a class="reference internal" href="#constraint-examples" id="id144">Constraint examples</a></li>
</ul>
</li>
<li><a class="reference internal" href="#mapexport" id="id143">mapexport</a></li>
<li><a class="reference internal" href="#dwarfexport" id="id144">dwarfexport</a></li>
<li><a class="reference internal" href="#mapexport" id="id145">mapexport</a></li>
<li><a class="reference internal" href="#dwarfexport" id="id146">dwarfexport</a></li>
</ul>
</li>
</ul>
@ -1330,21 +1332,29 @@ You can also paint only over tiles that match a set of properties (filter)</p>
<h2><a class="toc-backref" href="#id125">tubefill</a></h2>
<p>Fills all the adamantine veins again. Veins that were empty will be filled in too, but might still trigger a demon invasion (this is a known bug).</p>
</div>
<div class="section" id="vdig">
<h2><a class="toc-backref" href="#id126">vdig</a></h2>
<div class="section" id="digv">
<h2><a class="toc-backref" href="#id126">digv</a></h2>
<p>Designates a whole vein for digging. Requires an active in-game cursor placed over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles).</p>
</div>
<div class="section" id="vdigx">
<h2><a class="toc-backref" href="#id127">vdigx</a></h2>
<p>A permanent alias for 'vdig x'.</p>
<div class="section" id="digvx">
<h2><a class="toc-backref" href="#id127">digvx</a></h2>
<p>A permanent alias for 'digv x'.</p>
</div>
<div class="section" id="expdig">
<h2><a class="toc-backref" href="#id128">expdig</a></h2>
<div class="section" id="digl">
<h2><a class="toc-backref" href="#id128">digl</a></h2>
<p>Designates layer stone for digging. Requires an active in-game cursor placed over a layer stone tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles). With the 'undo' option it will remove the dig designation instead (if you realize that digging out a 50 z-level deep layer was not such a good idea after all).</p>
</div>
<div class="section" id="diglx">
<h2><a class="toc-backref" href="#id129">diglx</a></h2>
<p>A permanent alias for 'digl x'.</p>
</div>
<div class="section" id="digexp">
<h2><a class="toc-backref" href="#id130">digexp</a></h2>
<p>This command can be used for exploratory mining.</p>
<p>See: <a class="reference external" href="http://df.magmawiki.com/index.php/DF2010:Exploratory_mining">http://df.magmawiki.com/index.php/DF2010:Exploratory_mining</a></p>
<p>There are two variables that can be set: pattern and filter.</p>
<div class="section" id="patterns">
<h3><a class="toc-backref" href="#id129">Patterns:</a></h3>
<h3><a class="toc-backref" href="#id131">Patterns:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1365,7 +1375,7 @@ You can also paint only over tiles that match a set of properties (filter)</p>
</table>
</div>
<div class="section" id="filters">
<h3><a class="toc-backref" href="#id130">Filters:</a></h3>
<h3><a class="toc-backref" href="#id132">Filters:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1381,7 +1391,7 @@ You can also paint only over tiles that match a set of properties (filter)</p>
<p>After you have a pattern set, you can use 'expdig' to apply it again.</p>
</div>
<div class="section" id="id23">
<h3><a class="toc-backref" href="#id131">Examples:</a></h3>
<h3><a class="toc-backref" href="#id133">Examples:</a></h3>
<dl class="docutils">
<dt>designate the diagonal 5 patter over all hidden tiles:</dt>
<dd><ul class="first last simple">
@ -1402,11 +1412,11 @@ You can also paint only over tiles that match a set of properties (filter)</p>
</div>
</div>
<div class="section" id="digcircle">
<h2><a class="toc-backref" href="#id132">digcircle</a></h2>
<h2><a class="toc-backref" href="#id134">digcircle</a></h2>
<p>A command for easy designation of filled and hollow circles.
It has several types of options.</p>
<div class="section" id="shape">
<h3><a class="toc-backref" href="#id133">Shape:</a></h3>
<h3><a class="toc-backref" href="#id135">Shape:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1421,7 +1431,7 @@ It has several types of options.</p>
</table>
</div>
<div class="section" id="action">
<h3><a class="toc-backref" href="#id134">Action:</a></h3>
<h3><a class="toc-backref" href="#id136">Action:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1436,7 +1446,7 @@ It has several types of options.</p>
</table>
</div>
<div class="section" id="designation-types">
<h3><a class="toc-backref" href="#id135">Designation types:</a></h3>
<h3><a class="toc-backref" href="#id137">Designation types:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1459,7 +1469,7 @@ It has several types of options.</p>
repeats with the last selected parameters.</p>
</div>
<div class="section" id="id24">
<h3><a class="toc-backref" href="#id136">Examples:</a></h3>
<h3><a class="toc-backref" href="#id138">Examples:</a></h3>
<ul class="simple">
<li>'digcircle filled 3' = Dig a filled circle with radius = 3.</li>
<li>'digcircle' = Do it again.</li>
@ -1467,11 +1477,11 @@ repeats with the last selected parameters.</p>
</div>
</div>
<div class="section" id="weather">
<h2><a class="toc-backref" href="#id137">weather</a></h2>
<h2><a class="toc-backref" href="#id139">weather</a></h2>
<p>Prints the current weather map by default.</p>
<p>Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.</p>
<div class="section" id="id25">
<h3><a class="toc-backref" href="#id138">Options:</a></h3>
<h3><a class="toc-backref" href="#id140">Options:</a></h3>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
@ -1487,10 +1497,10 @@ repeats with the last selected parameters.</p>
</div>
</div>
<div class="section" id="workflow">
<h2><a class="toc-backref" href="#id139">workflow</a></h2>
<h2><a class="toc-backref" href="#id141">workflow</a></h2>
<p>Manage control of repeat jobs.</p>
<div class="section" id="id26">
<h3><a class="toc-backref" href="#id140">Usage</a></h3>
<h3><a class="toc-backref" href="#id142">Usage</a></h3>
<dl class="docutils">
<dt><tt class="docutils literal">workflow enable <span class="pre">[option...],</span> workflow disable <span class="pre">[option...]</span></tt></dt>
<dd><p class="first">If no options are specified, enables or disables the plugin.
@ -1511,7 +1521,7 @@ Otherwise, enables or disables any of the following options:</p>
</dl>
</div>
<div class="section" id="function">
<h3><a class="toc-backref" href="#id141">Function</a></h3>
<h3><a class="toc-backref" href="#id143">Function</a></h3>
<p>When the plugin is enabled, it protects all repeat jobs from removal.
If they do disappear due to any cause, they are immediately re-added to their
workshop and suspended.</p>
@ -1522,7 +1532,7 @@ the amount has to drop before jobs are resumed; this is intended to reduce
the frequency of jobs being toggled.</p>
</div>
<div class="section" id="constraint-examples">
<h3><a class="toc-backref" href="#id142">Constraint examples</a></h3>
<h3><a class="toc-backref" href="#id144">Constraint examples</a></h3>
<p>Keep metal bolts within 900-1000, and wood/bone within 150-200.</p>
<pre class="literal-block">
workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
@ -1559,11 +1569,11 @@ the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material' command
</div>
</div>
<div class="section" id="mapexport">
<h2><a class="toc-backref" href="#id143">mapexport</a></h2>
<h2><a class="toc-backref" href="#id145">mapexport</a></h2>
<p>Export the current loaded map as a file. This will be eventually usable with visualizers.</p>
</div>
<div class="section" id="dwarfexport">
<h2><a class="toc-backref" href="#id144">dwarfexport</a></h2>
<h2><a class="toc-backref" href="#id146">dwarfexport</a></h2>
<p>Export dwarves to RuneSmith-compatible XML.</p>
</div>
</div>

@ -2,3 +2,4 @@
rst2html README.rst > Readme.html
rst2html COMPILE.rst > Compile.html
rst2html DEVEL.rst > Devel.html
rst2html LUA_API.rst > Lua\ API.html

@ -868,6 +868,15 @@ int Core::Update()
color_ostream_proxy out(con);
// Pretend this thread has suspended the core in the usual way
{
lock_guard<mutex> lock(d->AccessMutex);
assert(d->df_suspend_depth == 0);
d->df_suspend_thread = this_thread::get_id();
d->df_suspend_depth = 1000;
}
// detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL;
void *new_mapdata = NULL;
@ -922,6 +931,14 @@ int Core::Update()
// notify all the plugins that a game tick is finished
plug_mgr->OnUpdate(out);
// Release the fake suspend lock
{
lock_guard<mutex> lock(d->AccessMutex);
assert(d->df_suspend_depth == 1000);
d->df_suspend_depth = 0;
}
out << std::flush;
// wake waiting tools

@ -76,6 +76,27 @@ static void set_dfhack_output(lua_State *L, color_ostream *p)
lua_rawsetp(L, LUA_REGISTRYINDEX, &DFHACK_OSTREAM_TOKEN);
}
static Console *get_console(lua_State *state)
{
color_ostream *pstream = Lua::GetOutput(state);
if (!pstream)
{
lua_pushnil(state);
lua_pushstring(state, "no output stream");
return NULL;
}
if (!pstream->is_console())
{
lua_pushnil(state);
lua_pushstring(state, "not an interactive console");
return NULL;
}
return static_cast<Console*>(pstream);
}
static std::string lua_print_fmt(lua_State *L)
{
/* Copied from lua source to fully replicate builtin print */
@ -120,37 +141,233 @@ static int lua_dfhack_println(lua_State *S)
return 0;
}
static int lua_dfhack_printerr(lua_State *S)
static void dfhack_printerr(lua_State *S, const std::string &str)
{
std::string str = lua_print_fmt(S);
if (color_ostream *out = Lua::GetOutput(S))
out->printerr("%s\n", str.c_str());
else
Core::printerr("%s\n", str.c_str());
}
static int lua_dfhack_printerr(lua_State *S)
{
std::string str = lua_print_fmt(S);
dfhack_printerr(S, str);
return 0;
}
static int traceback (lua_State *L) {
const char *msg = lua_tostring(L, 1);
if (msg)
luaL_traceback(L, L, msg, 1);
else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
lua_pushliteral(L, "(no error message)");
}
static int lua_dfhack_color(lua_State *S)
{
int cv = luaL_optint(S, 1, -1);
if (cv < -1 || cv > color_ostream::COLOR_MAX)
luaL_argerror(S, 1, "invalid color value");
color_ostream *out = Lua::GetOutput(S);
if (out)
out->color(color_ostream::color_value(cv));
return 0;
}
static int lua_dfhack_is_interactive(lua_State *S)
{
lua_pushboolean(S, get_console(S) != NULL);
return 1;
}
static void report_error(color_ostream &out, lua_State *L)
static int lua_dfhack_lineedit(lua_State *S)
{
const char *prompt = luaL_optstring(S, 1, ">> ");
const char *hfile = luaL_optstring(S, 2, NULL);
Console *pstream = get_console(S);
if (!pstream)
return 2;
DFHack::CommandHistory hist;
if (hfile)
hist.load(hfile);
std::string ret;
int rv = pstream->lineedit(prompt, ret, hist);
if (rv < 0)
{
lua_pushnil(S);
lua_pushstring(S, "input error");
return 2;
}
else
{
if (hfile)
hist.save(hfile);
lua_pushlstring(S, ret.data(), ret.size());
return 1;
}
}
static int DFHACK_EXCEPTION_META_TOKEN = 0;
static void error_tostring(lua_State *L)
{
lua_getglobal(L, "tostring");
lua_pushvalue(L, -2);
bool ok = lua_pcall(L, 1, 1, 0) == LUA_OK;
const char *msg = lua_tostring(L, -1);
if (!msg)
{
msg = "tostring didn't return a string";
ok = false;
}
if (!ok)
{
lua_pushfstring(L, "(invalid error: %s)", msg);
lua_remove(L, -2);
}
}
static void report_error(lua_State *L, color_ostream *out = NULL)
{
lua_dup(L);
error_tostring(L);
const char *msg = lua_tostring(L, -1);
if (msg)
out.printerr("%s\n", msg);
assert(msg);
if (out)
out->printerr("%s\n", msg);
else
out.printerr("In Lua::SafeCall: error message is not a string.\n", msg);
dfhack_printerr(L, msg);
lua_pop(L, 1);
}
static bool convert_to_exception(lua_State *L)
{
int base = lua_gettop(L);
bool force_unknown = false;
if (lua_istable(L, base) && lua_getmetatable(L, base))
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
bool is_exception = lua_rawequal(L, -1, -2);
lua_settop(L, base);
// If it is an exception, return as is
if (is_exception)
return false;
force_unknown = true;
}
if (!lua_istable(L, base) || force_unknown)
{
lua_newtable(L);
lua_swap(L);
if (lua_isstring(L, -1))
lua_setfield(L, base, "message");
else
{
error_tostring(L);
lua_setfield(L, base, "message");
lua_setfield(L, base, "object");
}
}
else
{
lua_getfield(L, base, "message");
if (!lua_isstring(L, -1))
{
error_tostring(L);
lua_setfield(L, base, "message");
}
lua_settop(L, base);
}
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
lua_setmetatable(L, base);
return true;
}
static int dfhack_onerror(lua_State *L)
{
luaL_checkany(L, 1);
lua_settop(L, 1);
bool changed = convert_to_exception(L);
if (!changed)
return 1;
luaL_traceback(L, L, NULL, 1);
lua_setfield(L, 1, "stacktrace");
return 1;
}
static int dfhack_exception_tostring(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
int base = lua_gettop(L);
lua_getfield(L, 1, "message");
if (!lua_isstring(L, -1))
{
lua_pop(L, 1);
lua_pushstring(L, "(error message is not a string)");
}
lua_pushstring(L, "\n");
lua_getfield(L, 1, "stacktrace");
if (!lua_isstring(L, -1))
lua_pop(L, 2);
lua_concat(L, lua_gettop(L) - base);
return 1;
}
static int finish_dfhack_safecall (lua_State *L, bool success)
{
if (!lua_checkstack(L, 2))
{
lua_settop(L, 0); /* create space for return values */
lua_pushboolean(L, 0);
lua_pushstring(L, "stack overflow in dfhack.safecall()");
success = false;
}
else
{
lua_pushboolean(L, success);
lua_replace(L, 1); /* put first result in first slot */
}
if (!success)
report_error(L);
return lua_gettop(L);
}
static int safecall_cont (lua_State *L)
{
int status = lua_getctx(L, NULL);
return finish_dfhack_safecall(L, (status == LUA_YIELD));
}
static int lua_dfhack_safecall (lua_State *L)
{
luaL_checkany(L, 1);
lua_pushcfunction(L, dfhack_onerror);
lua_insert(L, 1);
int status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0, safecall_cont);
return finish_dfhack_safecall(L, (status == LUA_OK));
}
bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres, bool perr)
{
int base = lua_gettop(L) - nargs;
@ -158,17 +375,20 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres
color_ostream *cur_out = Lua::GetOutput(L);
set_dfhack_output(L, &out);
lua_pushcfunction(L, traceback);
lua_pushcfunction(L, dfhack_onerror);
lua_insert(L, base);
bool ok = lua_pcall(L, nargs, nres, base) == LUA_OK;
if (!ok && perr)
{
report_error(L, &out);
lua_pop(L, 1);
}
lua_remove(L, base);
set_dfhack_output(L, cur_out);
if (!ok && perr)
report_error(out, L);
return ok;
}
@ -189,24 +409,51 @@ bool DFHack::Lua::Require(color_ostream &out, lua_State *state,
return true;
}
static bool load_with_env(color_ostream &out, lua_State *state, const std::string &code, int eidx)
bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr)
{
if (luaL_loadbuffer(state, code.data(), code.size(), "=(interactive)") != LUA_OK)
val_index = lua_absindex(state, val_index);
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
PushDFObject(state, type, target);
lua_pushvalue(state, val_index);
return Lua::SafeCall(out, state, 2, 0, perr);
}
bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
int nargs, int nres, bool perr,
const char *debug_tag, int env_idx)
{
if (!debug_tag)
debug_tag = code.c_str();
if (env_idx)
env_idx = lua_absindex(state, env_idx);
int base = lua_gettop(state);
// Parse the code
if (luaL_loadbuffer(state, code.data(), code.size(), debug_tag) != LUA_OK)
{
report_error(out, state);
if (perr)
{
report_error(state, &out);
lua_pop(state, 1);
}
return false;
}
// Replace _ENV
lua_pushvalue(state, eidx);
if (!lua_setupvalue(state, -2, 1))
if (env_idx)
{
out.printerr("No _ENV upvalue.\n");
return false;
lua_pushvalue(state, env_idx);
lua_setupvalue(state, -2, 1);
assert(lua_gettop(state) == base+1);
}
return true;
if (nargs > 0)
lua_insert(state, -1-nargs);
return Lua::SafeCall(out, state, nargs, nres, perr);
}
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
@ -240,6 +487,8 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
// Make a proxy global environment.
lua_newtable(state);
int base = lua_gettop(state);
lua_newtable(state);
if (env)
lua_pushvalue(state, env);
@ -249,7 +498,6 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
lua_setmetatable(state, -2);
// Main interactive loop
int base = lua_gettop(state);
int vcnt = 1;
string curline;
string prompt_str = "[" + string(prompt) + "]# ";
@ -272,9 +520,7 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
{
curline = "return " + curline.substr(1);
if (!load_with_env(out, state, curline, base))
continue;
if (!SafeCall(out, state, 0, LUA_MULTRET))
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
continue;
int numret = lua_gettop(state) - base;
@ -308,9 +554,7 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
}
else
{
if (!load_with_env(out, state, curline, base))
continue;
if (!SafeCall(out, state, 0, 0))
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
continue;
}
}
@ -323,9 +567,9 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
static int lua_dfhack_interpreter(lua_State *state)
{
color_ostream *pstream = Lua::GetOutput(state);
Console *pstream = get_console(state);
if (!pstream)
luaL_error(state, "Cannot use dfhack.interpreter() without output.");
return 2;
int argc = lua_gettop(state);
@ -339,8 +583,7 @@ static int lua_dfhack_interpreter(lua_State *state)
static int lua_dfhack_with_suspend(lua_State *L)
{
int ctx;
int rv = lua_getctx(L, &ctx);
int rv = lua_getctx(L, NULL);
// Non-resume entry point:
if (rv == LUA_OK)
@ -351,7 +594,7 @@ static int lua_dfhack_with_suspend(lua_State *L)
Core::getInstance().Suspend();
lua_pushcfunction(L, traceback);
lua_pushcfunction(L, dfhack_onerror);
lua_insert(L, 1);
rv = lua_pcallk(L, nargs-1, LUA_MULTRET, 1, 0, lua_dfhack_with_suspend);
@ -372,8 +615,12 @@ static const luaL_Reg dfhack_funcs[] = {
{ "print", lua_dfhack_print },
{ "println", lua_dfhack_println },
{ "printerr", lua_dfhack_printerr },
{ "traceback", traceback },
{ "color", lua_dfhack_color },
{ "is_interactive", lua_dfhack_is_interactive },
{ "lineedit", lua_dfhack_lineedit },
{ "interpreter", lua_dfhack_interpreter },
{ "safecall", lua_dfhack_safecall },
{ "onerror", dfhack_onerror },
{ "with_suspend", lua_dfhack_with_suspend },
{ NULL, NULL }
};
@ -459,6 +706,8 @@ static PersistentDataItem get_persistent(lua_State *state)
static int dfhack_persistent_get(lua_State *state)
{
CoreSuspender suspend;
auto ref = get_persistent(state);
return read_persistent(state, ref, !lua_istable(state, 1));
@ -466,6 +715,8 @@ static int dfhack_persistent_get(lua_State *state)
static int dfhack_persistent_delete(lua_State *state)
{
CoreSuspender suspend;
auto ref = get_persistent(state);
bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref);
@ -476,6 +727,8 @@ static int dfhack_persistent_delete(lua_State *state)
static int dfhack_persistent_get_all(lua_State *state)
{
CoreSuspender suspend;
const char *str = luaL_checkstring(state, 1);
bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false);
@ -501,6 +754,8 @@ static int dfhack_persistent_get_all(lua_State *state)
static int dfhack_persistent_save(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 2);
luaL_checktype(state, 1, LUA_TTABLE);
bool add = lua_toboolean(state, 2);
@ -597,8 +852,18 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_pushcfunction(state, lua_dfhack_println);
lua_setglobal(state, "print");
// Create and initialize the dfhack global
// Create the dfhack global
lua_newtable(state);
// Create the metatable for exceptions
lua_newtable(state);
lua_pushcfunction(state, dfhack_exception_tostring);
lua_setfield(state, -2, "__tostring");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
lua_setfield(state, -2, "exception");
// Initialize the dfhack global
luaL_setfuncs(state, dfhack_funcs, 0);
OpenPersistent(state);

@ -36,6 +36,7 @@ distribution.
#include "DataDefs.h"
#include "DataIdentity.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
#include "MiscUtils.h"
@ -402,6 +403,46 @@ static bool is_valid_metatable(lua_State *state, int objidx, int metaidx)
return ok;
}
bool Lua::IsDFNull(lua_State *state, int val_index)
{
if (lua_isnil(state, val_index))
return true;
if (lua_islightuserdata(state, val_index))
return lua_touserdata(state, val_index) == NULL;
return false;
}
Lua::ObjectClass Lua::IsDFObject(lua_State *state, int val_index)
{
if (lua_isnil(state, val_index))
return Lua::OBJ_NULL;
if (lua_islightuserdata(state, val_index))
return lua_touserdata(state, val_index) ? Lua::OBJ_VOIDPTR : OBJ_NULL;
Lua::ObjectClass cls;
if (lua_istable(state, val_index))
{
cls = Lua::OBJ_TYPE;
lua_pushvalue(state, val_index);
LookupInTable(state, &DFHACK_TYPEID_TABLE_TOKEN);
}
else if (lua_isuserdata(state, val_index))
{
if (!lua_getmetatable(state, val_index))
return Lua::OBJ_INVALID;
cls = Lua::OBJ_REF;
LookupInTable(state, &DFHACK_TYPETABLE_TOKEN);
}
else
return Lua::OBJ_INVALID;
bool ok = !lua_isnil(state, -1);
lua_pop(state, 1);
return ok ? cls : Lua::OBJ_INVALID;
}
/**
* Given a DF object reference or type, safely retrieve its identity pointer.
*/
@ -865,6 +906,52 @@ static int meta_enum_attr_index(lua_State *state)
return 1;
}
/**
* Metamethod: df.isvalid(obj[,allow_null])
*/
static int meta_isvalid(lua_State *state)
{
luaL_checkany(state, 1);
switch (Lua::IsDFObject(state, 1))
{
case Lua::OBJ_NULL:
lua_settop(state, 2);
if (lua_toboolean(state, 2))
lua_pushvalue(state, lua_upvalueindex(1));
else
lua_pushnil(state);
return 1;
case Lua::OBJ_TYPE:
lua_pushvalue(state, lua_upvalueindex(2));
return 1;
case Lua::OBJ_VOIDPTR:
lua_pushvalue(state, lua_upvalueindex(3));
return 1;
case Lua::OBJ_REF:
lua_pushvalue(state, lua_upvalueindex(4));
return 1;
case Lua::OBJ_INVALID:
default:
lua_pushnil(state);
return 1;
}
}
/**
* Metamethod: df.isnull(obj)
*/
static int meta_isnull(lua_State *state)
{
luaL_checkany(state, 1);
lua_pushboolean(state, Lua::IsDFNull(state, 1));
return 1;
}
static int meta_nodata(lua_State *state)
{
return 0;
@ -1221,9 +1308,15 @@ static void RenderType(lua_State *state, compound_identity *node)
break;
case IDTYPE_GLOBAL:
lua_pushstring(state, "global");
lua_setfield(state, ftable, "_kind");
{
RenderTypeChildren(state, node->getScopeChildren());
lua_pushlightuserdata(state, node);
lua_setfield(state, ftable, "_identity");
BuildTypeMetatable(state, node);
lua_dup(state);
@ -1244,6 +1337,9 @@ static void RenderType(lua_State *state, compound_identity *node)
RenderTypeChildren(state, node->getScopeChildren());
lua_pushlightuserdata(state, node);
lua_setfield(state, ftable, "_identity");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
lua_setfield(state, ftable, "sizeof");
@ -1341,6 +1437,16 @@ static int DoAttach(lua_State *state)
lua_pushlightuserdata(state, NULL);
lua_setglobal(state, "NULL");
lua_pushstring(state, "null");
lua_pushstring(state, "type");
lua_pushstring(state, "voidptr");
lua_pushstring(state, "ref");
lua_pushcclosure(state, meta_isvalid, 4);
lua_setfield(state, -2, "isvalid");
lua_pushcfunction(state, meta_isnull);
lua_setfield(state, -2, "isnull");
freeze_table(state, false, "df");
}

@ -445,6 +445,48 @@ static command_result ListEnums(color_ostream &stream,
#undef BITFIELD
}
static command_result ListJobSkills(color_ostream &stream, const EmptyMessage *, ListJobSkillsOut *out)
{
auto pf_skill = out->mutable_skill();
FOR_ENUM_ITEMS(job_skill, skill)
{
auto item = pf_skill->Add();
item->set_id(skill);
item->set_key(ENUM_KEY_STR(job_skill, skill));
item->set_caption(ENUM_ATTR_STR(job_skill, caption, skill));
item->set_caption_noun(ENUM_ATTR_STR(job_skill, caption_noun, skill));
item->set_profession(ENUM_ATTR(job_skill, profession, skill));
item->set_labor(ENUM_ATTR(job_skill, labor, skill));
item->set_type(ENUM_KEY_STR(job_skill_class, ENUM_ATTR(job_skill, type, skill)));
}
auto pf_profession = out->mutable_profession();
FOR_ENUM_ITEMS(profession, p)
{
auto item = pf_profession->Add();
item->set_id(p);
item->set_key(ENUM_KEY_STR(profession, p));
item->set_caption(ENUM_ATTR_STR(profession, caption, p));
item->set_military(ENUM_ATTR(profession, military, p));
item->set_can_assign_labor(ENUM_ATTR(profession, can_assign_labor, p));
item->set_parent(ENUM_ATTR(profession, parent, p));
}
auto pf_labor = out->mutable_labor();
FOR_ENUM_ITEMS(unit_labor, labor)
{
auto item = pf_labor->Add();
item->set_id(labor);
item->set_key(ENUM_KEY_STR(unit_labor, labor));
item->set_caption(ENUM_ATTR_STR(unit_labor, caption, labor));
}
return CR_OK;
}
static void listMaterial(ListMaterialsOut *out, int type, int index, const BasicMaterialInfoMask *mask)
{
MaterialInfo info(type, index);
@ -590,6 +632,7 @@ CoreService::CoreService() {
addFunction("GetWorldInfo", GetWorldInfo);
addFunction("ListEnums", ListEnums, SF_CALLED_ONCE | SF_DONT_SUSPEND);
addFunction("ListJobSkills", ListJobSkills, SF_CALLED_ONCE | SF_DONT_SUSPEND);
addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE);
addFunction("ListUnits", ListUnits);

@ -46,6 +46,29 @@ namespace DFHack { namespace Lua {
DFHACK_EXPORT bool Require(color_ostream &out, lua_State *state,
const std::string &module, bool setglobal = false);
/**
* Check if the object at the given index is NIL or NULL.
*/
DFHACK_EXPORT bool IsDFNull(lua_State *state, int val_index);
enum ObjectClass {
/** Not a DF wrapper object */
OBJ_INVALID = 0,
/** NIL or NULL */
OBJ_NULL,
/** A named type identity object */
OBJ_TYPE,
/** A void* reference, i.e. non-null lightuserdata */
OBJ_VOIDPTR,
/** A typed object reference */
OBJ_REF
};
/**
* Check if the object at the given index is a valid wrapper object.
*/
DFHACK_EXPORT ObjectClass IsDFObject(lua_State *state, int val_index);
/**
* Push the pointer onto the stack as a wrapped DF object of the given type.
*/
@ -56,6 +79,13 @@ namespace DFHack { namespace Lua {
*/
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
/**
* Assign the value at val_index to the target of given identity using df.assign().
* Return behavior is of SafeCall below.
*/
DFHACK_EXPORT bool AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr = true);
/**
* Push the pointer onto the stack as a wrapped DF object of a specific type.
*/
@ -72,12 +102,28 @@ namespace DFHack { namespace Lua {
return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
}
/**
* Assign the value at val_index to the target using df.assign().
*/
template<class T>
bool AssignDFObject(color_ostream &out, lua_State *state, T *target, int val_index, bool perr = true) {
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
}
/**
* Invoke lua function via pcall. Returns true if success.
* If an error is signalled, and perr is true, it is printed and popped from the stack.
*/
DFHACK_EXPORT bool SafeCall(color_ostream &out, lua_State *state, int nargs, int nres, bool perr = true);
/**
* Parse code from string with debug_tag and env_idx, then call it using SafeCall.
* In case of error, it is either left on the stack, or printed like SafeCall does.
*/
DFHACK_EXPORT bool SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
int nargs, int nres, bool perr = true,
const char *debug_tag = NULL, int env_idx = 0);
/**
* Returns the ostream passed to SafeCall.
*/

@ -1,6 +1,12 @@
-- Common startup file for all dfhack plugins with lua support
-- The global dfhack table is already created by C++ init code.
safecall = dfhack.safecall
function dfhack.pcall(f, ...)
return xpcall(f, dfhack.onerror, ...)
end
function mkmodule(module,env)
local pkg = package.loaded[module]
if pkg == nil then

@ -69,6 +69,36 @@ message BasicMaterialInfoMask {
optional bool reaction = 3 [default = false];
};
message JobSkillAttr {
required int32 id = 1;
required string key = 2;
optional string caption = 3;
optional string caption_noun = 4;
optional int32 profession = 5;
optional int32 labor = 6;
optional string type = 7;
};
message ProfessionAttr {
required int32 id = 1;
required string key = 2;
optional string caption = 3;
optional bool military = 4;
optional bool can_assign_labor = 5;
optional int32 parent = 6;
};
message UnitLaborAttr {
required int32 id = 1;
required string key = 2;
optional string caption = 3;
};
message NameInfo {
optional string first_name = 1;
optional string nickname = 2;

@ -51,6 +51,13 @@ message ListEnumsOut {
repeated EnumItemName profession = 11;
};
// RPC ListJobSkills : EmptyMessage -> ListJobSkillsOut
message ListJobSkillsOut {
repeated JobSkillAttr skill = 1;
repeated ProfessionAttr profession = 2;
repeated UnitLaborAttr labor = 3;
};
// RPC ListMaterials : ListMaterialsIn -> ListMaterialsOut
message ListMaterialsIn {
optional BasicMaterialInfoMask mask = 1;

@ -96,6 +96,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(tweak tweak.cpp)
DFHACK_PLUGIN(feature feature.cpp)
DFHACK_PLUGIN(lair lair.cpp)
DFHACK_PLUGIN(zone zone.cpp)
# not yet. busy with other crud again...
#DFHACK_PLUGIN(versionosd versionosd.cpp)
endif()

@ -8,6 +8,7 @@
#include "luamain.h"
#include "OutFile.h"
#include "functioncall.h"
#include "LuaTools.h"
namespace lua
{

@ -1,5 +1,53 @@
adv_tools=adv_tools or {}
adv_tools.menu=adv_tools.menu or MakeMenu()
adv_tools= {}
adv_tools.menu=MakeMenu()
--TODO make every tool generic (work for both modes)
function adv_tools.reincarnate(swap_soul) --only for adventurer i guess
if swap_soul==nil then
swap_soul=true
end
local adv=df.global.world.units.other[0][0]
if adv.flags1.dead==false then
error("You are not dead (yet)!")
end
local hist_fig=getNemesis(adv).figure
if hist_fig==nil then
error("No historical figure for adventurer...")
end
local events=df.global.world.history.events
local trg_hist_fig
for i=#events-1,0,-1 do -- reverse search because almost always it will be last entry
if df.history_event_hist_figure_diedst:is_instance(events[i]) then
--print("is instance:"..i)
if events[i].hfid==hist_fig.id then
--print("Is same id:"..i)
trg_hist_fig=events[i].slayer
if trg_hist_fig then
trg_hist_fig=df.historical_figure.find(trg_hist_fig)
end
break
end
end
end
if trg_hist_fig ==nil then
error("Slayer not found")
end
local trg_unit=trg_hist_fig.unit_id
if trg_unit==nil then
error("Unit id not found!")
end
local trg_unit_final=df.unit.find(trg_unit)
tools.change_adv(trg_unit_final)
if swap_soul then --actually add a soul...
t_soul=adv.status.current_soul
adv.status.current_soul=df.NULL
adv.status.souls:resize(0)
trg_unit_final.status.current_soul=t_soul
trg_unit_final.status.souls:insert(#trg_unit_final.status.souls,t_soul)
end
end
adv_tools.menu:add("Reincarnate",adv_tools.reincarnate)
function adv_tools.ressurect()
v2=engine.peek(vector:getval(indx),ptr_Creature.hurt1)

@ -0,0 +1,3 @@
if not(FILE) then
adv_tools.menu:display()
end

@ -254,11 +254,11 @@ function it_menu:display()
if r=='q' then return end
ans=tonumber(r)
if ans==nil or not(ans<=table.maxn(self.items) and ans>0) then
if ans==nil or not(ans<=#self.items and ans>0) then
print("incorrect choice")
end
until ans~=nil and (ans<=table.maxn(self.items) and ans>0)
until ans~=nil and (ans<=#self.items and ans>0)
self.items[ans][1]()
end
function MakeMenu()
@ -468,6 +468,9 @@ function ParseNames(path)
return ret
end
function getSelectedUnit()
if df.global.ui.main.mode~=23 then
return nil
end
local unit_indx=df.global.ui_selected_unit
if unit_indx<#df.global.world.units.other[0]-1 then
return df.global.world.units.other[0][unit_indx]
@ -493,10 +496,34 @@ function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
return vector[i] --return index
end
end
print("Creature not found!")
--print("Creature not found!")
return nil
end
function getCreatureAtPointer()
return getCreatureAtPos(getxyz())
end
function getCreature()
local unit=getSelectedUnit()
if unit==nil then
unit=getCreatureAtPointer()
end
--any other selection methods...
return unit
end
function getNemesisId(unit)
for k,v in pairs(unit.refs) do
if df.general_ref_is_nemesisst:is_instance(v) then
return v.nemesis_id
end
end
end
function getNemesis(unit)
local id=getNemesisId(unit)
if id then
return df.nemesis_record.find(id)
end
end
function Allocate(size)
local ptr=engine.getmod('General_Space')
if ptr==nil then
@ -509,14 +536,6 @@ function Allocate(size)
engine.poked(ptr,curptr)
return curptr-size+ptr
end
function initType(object,...)
local m=getmetatable(object)
if m~=nil and m.__setup~=nil then
m.__setup(object,...)
else
error("This object does not have __setup function")
end
end
dofile("dfusion/patterns.lua")
dofile("dfusion/patterns2.lua")
dofile("dfusion/itempatterns.lua")

@ -73,7 +73,7 @@ table.insert(plugins,{"friendship","Multi race fort enabler"})
table.insert(plugins,{"offsets","Find all offsets"})
table.insert(plugins,{"friendship_civ","Multi civ fort enabler"})
table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"})
table.insert(plugins,{"triggers","a function calling plug (discontinued...)"})
table.insert(plugins,{"migrants","multi race imigrations"})
@ -81,10 +81,19 @@ table.insert(plugins,{"migrants","multi race imigrations"})
--table.insert(plugins,{"onfunction","run lua on some df function"})
--table.insert(plugins,{"editor","edit internals of df",EditDF})
table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved})
table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"})
loadall(plugins)
dofile_silent("dfusion/initcustom.lua")
local args={...}
for k,v in pairs(args) do
local f,err=load(v)
if f then
f()
else
Console.printerr(err)
end
end
if not INIT then
mainmenu(plugins)
end

@ -1,7 +1,7 @@
offsets=offsets or {}
function offsets.find(startoffset,...)
local endadr=GetTextRegion()["end"];
--[=[if startoffset== 0 then
-- [=[
if startoffset== 0 then
local text=GetTextRegion()
--print("searching in:"..text.name)
startoffset=text.start
@ -14,7 +14,8 @@ function offsets.find(startoffset,...)
return 0
end
endadr=reg["end"]
end--]=]
end
--]=]
--print(string.format("Searching (%x->%x)",startoffset,endadr))
local h=hexsearch(startoffset,endadr,...)
local pos=h:find()

@ -8,7 +8,8 @@ if WINDOWS then --windows function defintions
onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}})
onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"})
onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=]
onfunction.AddFunction(4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx?
--onfunction.AddFunction(0x4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx? 0.34.07
onfunction.AddFunction(0x72aB6+offsets.base(),"Die",{creature="edi"}) --0.34.07
else --linux
--[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch...
onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=]

@ -1,8 +1,9 @@
mypos=engine.getmod("functions")
function DeathMsg(values)
local name
name=engine.peek(values[onfunction.hints["Die"].creature],ptt_dfstring)
print(name:getval().." died")
local u=engine.cast(df.unit,values[onfunction.hints["Die"].creature])
print(u.name.first_name.." died")
end
if mypos then
print("Onfunction already installed")

@ -1,20 +1,28 @@
tools={}
tools.menu=MakeMenu()
function tools.setrace()
function tools.setrace(name)
RaceTable=BuildNameTable()
print("Your current race is:"..GetRaceToken(df.global.ui.race_id))
print("Type new race's token name in full caps (q to quit):")
repeat
entry=getline()
if entry=="q" then
return
local id
if name == nil then
print("Type new race's token name in full caps (q to quit):")
repeat
entry=getline()
if entry=="q" then
return
end
id=RaceTable[entry]
until id~=nil
else
id=RaceTable[name]
if id==nil then
error("Name not found!")
end
id=RaceTable[entry]
until id~=nil
end
df.global.ui.race_id=id
end
tools.menu:add("Set current race",tools.setrace)
function tools.GiveSentience(names) --TODO make pattern...
function tools.GiveSentience(names)
RaceTable=RaceTable or BuildNameTable() --slow.If loaded don't load again
if names ==nil then
ids={}
@ -63,23 +71,7 @@ function tools.embark()
end
end
tools.menu:add("Embark anywhere",tools.embark)
function tools.getlegendsid(croff)
local vec=engine.peek(croff,ptr_Creature.legends)
if vec:size()==0 then
return 0
end
for i =0,vector:size()-1 do
--if engine.peekd(vec:getval(i))~=0 then
-- print(string.format("%x",engine.peekd(vec:getval(i))-offsets.base()))
--end
if(engine.peekd(vec:getval(i))==offsets.getEx("vtableLegends")) then --easy to get.. just copy from player's-base
return engine.peekd(vec:getval(i)+4)
end
end
return 0
end
function tools.getCreatureId(vector)
function tools.getCreatureId(vector) --redo it to getcreature by name/id or something
tnames={}
rnames={}
--[[print("vector1 size:"..vector:size())
@ -111,48 +103,70 @@ function tools.getCreatureId(vector)
end
return indx
end
function tools.change_adv()
myoff=offsets.getEx("AdvCreatureVec")
vector=engine.peek(myoff,ptr_vector)
indx=tools.getCreatureId(vector)
print("Swaping, press enter when done or 'q' to stay, 's' to stay with legends id change")
tval=vector:getval(0)
vector:setval(0,vector:getval(indx))
vector:setval(indx,tval)
r=getline()
if r=='q' then
return
function tools.change_adv(unit,nemesis)
if nemesis==nil then
nemesis=true --default value is nemesis switch too.
end
if r~='s' then
tval=vector:getval(0)
vector:setval(0,vector:getval(indx))
vector:setval(indx,tval)
if unit==nil then
unit=getCreatureAtPointer()
end
local lid=tools.getlegendsid(vector:getval(0))
if lid~=0 then
engine.poked(offsets.getEx("PlayerLegend"),lid)
else
print("Warning target does not have a valid legends id!")
if unit==nil then
error("Invalid unit!")
end
local other=df.global.world.units.other[0]
local unit_indx
for k,v in pairs(other) do
if v==unit then
unit_indx=k
break
end
end
if unit_indx==nil then
error("Unit not found in array?!") --should not happen
end
other[unit_indx]=other[0]
other[0]=unit
if nemesis then --basicly copied from advtools plugin...
local nem=getNemesis(unit)
local other_nem=getNemesis(other[unit_indx])
if other_nem then
other_nem.flags[0]=false
other_nem.flags[1]=true
end
if nem then
nem.flags[0]=true
nem.flags[2]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nem.id then
df.global.ui_advmode.player_id=k
end
end
else
error("Current unit does not have nemesis record, further working not guaranteed")
end
end
end
tools.menu:add("Change Adventurer",tools.change_adv)
function tools.MakeFollow()
myoff=offsets.getEx("AdvCreatureVec")
vector=engine.peek(myoff,ptr_vector)
indx=tools.getCreatureId(vector)
print(string.format("current creature:%x",vector:getval(indx)))
function tools.MakeFollow(unit,trgunit)
trgid=engine.peek(vector:getval(0)+ptr_Creature.ID.off,DWORD)
lfollow=engine.peek(vector:getval(indx)+ptr_Creature.followID.off,DWORD)
if lfollow ~=0xFFFFFFFF then
print("Already following, unfollow? y/N")
r=getline()
if r== "y" then
engine.poke(vector:getval(indx)+ptr_Creature.followID.off,DWORD,0)
end
else
engine.poke(vector:getval(indx)+ptr_Creature.followID.off,DWORD,trgid)
if unit == nil then
unit=getCreature()
end
if unit== nil then
error("Invalid creature")
end
if trgunit==nil then
trgunit=df.global.world.units.other[0][0]
end
unit.relations.group_leader_id=trgunit.id
local u_nem=getNemesis(unit)
local t_nem=getNemesis(trgunit)
if u_nem then
u_nem.group_leader_id=t_nem.id
end
if t_nem and u_nem then
t_nem.companions:insert(#t_nem.companions,u_nem.id)
end
end
tools.menu:add("Make creature follow",tools.MakeFollow)

@ -151,8 +151,6 @@ static size_t __stdcall PushValue(size_t ret,uint32_t eax,uint32_t ebx,uint32_t
#endif
{
lua::state st=lua::glua::Get();
st.getglobal("err");
int perr=st.gettop();
st.getglobal("OnFunction");
if(st.is<lua::nil>())
return 0;
@ -175,7 +173,7 @@ static size_t __stdcall PushValue(size_t ret,uint32_t eax,uint32_t ebx,uint32_t
st.setfield("ebp");
st.push(ret);
st.setfield("ret");
st.pcall(1,1,perr);
DFHack::Lua::SafeCall(DFHack::Core::getInstance().getConsole(),st,1,1);
return st.as<uint32_t>();
}
static int Get_PushValue(lua_State *L)
@ -210,6 +208,17 @@ static int Resume_Df(lua_State *L)
DFHack::Core::getInstance().Resume();
return 0;
}
static int Cast(lua_State *L)
{
lua::state st(L);
if(DFHack::Lua::IsDFObject(st,1)!=DFHack::Lua::OBJ_TYPE)
st.error("First argument must be df type!");
if(!st.is<lua::number>(2)) //todo maybe lightuserdata?
st.error("Second argument must be pointer as a number!");
st.getfield("_identity",1);
DFHack::Lua::PushDFObject(st,(DFHack::type_identity*)lua_touserdata(st,-1),(void*)st.as<int>(2));
return 1;
}
const luaL_Reg lua_misc_func[]=
{
{"alloc",lua_malloc},
@ -224,6 +233,7 @@ const luaL_Reg lua_misc_func[]=
{"calldf",Call_Df},
{"suspend",Suspend_Df},
{"resume",Resume_Df},
{"cast",Cast},
{NULL,NULL}
};
void lua::RegisterMisc(lua::state &st)

@ -1302,6 +1302,9 @@ command_result digl (color_ostream &out, vector <string> & parameters)
des_minus.bits.dig = tile_dig_designation::UpDownStair;
else
des_minus.bits.dig = tile_dig_designation::UpStair;
// undo mode: clear designation
if(undo)
des_minus.bits.dig = tile_dig_designation::No;
MCache->setDesignationAt(current-1,des_minus);
des.bits.dig = tile_dig_designation::DownStair;
@ -1314,6 +1317,9 @@ command_result digl (color_ostream &out, vector <string> & parameters)
des_plus.bits.dig = tile_dig_designation::UpDownStair;
else
des_plus.bits.dig = tile_dig_designation::DownStair;
// undo mode: clear designation
if(undo)
des_plus.bits.dig = tile_dig_designation::No;
MCache->setDesignationAt(current+1,des_plus);
if(des.bits.dig == tile_dig_designation::DownStair)

File diff suppressed because it is too large Load Diff