Conflicts:
	library/xml
develop
Japa 2014-05-11 09:34:05 +05:30
commit 43064f41e3
23 changed files with 1308 additions and 247 deletions

3
.gitignore vendored

@ -64,3 +64,6 @@ build/CPack*Config.cmake
# ctags file
tags
# Mac OS X .DS_Store files
.DS_Store

@ -344,92 +344,97 @@ ul.auto-toc {
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#df-data-structure-wrapper" id="id1">DF data 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>
<li><a class="reference internal" href="#df-data-structure-wrapper" id="id3">DF data structure wrapper</a><ul>
<li><a class="reference internal" href="#typed-object-references" id="id4">Typed object references</a><ul>
<li><a class="reference internal" href="#primitive-references" id="id5">Primitive references</a></li>
<li><a class="reference internal" href="#struct-references" id="id6">Struct references</a></li>
<li><a class="reference internal" href="#container-references" id="id7">Container references</a></li>
<li><a class="reference internal" href="#bitfield-references" id="id8">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>
<li><a class="reference internal" href="#named-types" id="id9">Named types</a></li>
<li><a class="reference internal" href="#global-functions" id="id10">Global functions</a></li>
<li><a class="reference internal" href="#recursive-table-assignment" id="id11">Recursive table assignment</a></li>
</ul>
</li>
<li><a class="reference internal" href="#dfhack-api" id="id10">DFHack API</a><ul>
<li><a class="reference internal" href="#native-utilities" id="id11">Native utilities</a><ul>
<li><a class="reference internal" href="#input-output" id="id12">Input &amp; Output</a></li>
<li><a class="reference internal" href="#exception-handling" id="id13">Exception handling</a></li>
<li><a class="reference internal" href="#miscellaneous" id="id14">Miscellaneous</a></li>
<li><a class="reference internal" href="#locking-and-finalization" id="id15">Locking and finalization</a></li>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id16">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#material-info-lookup" id="id17">Material info lookup</a></li>
<li><a class="reference internal" href="#random-number-generation" id="id18">Random number generation</a></li>
<li><a class="reference internal" href="#dfhack-api" id="id12">DFHack API</a><ul>
<li><a class="reference internal" href="#native-utilities" id="id13">Native utilities</a><ul>
<li><a class="reference internal" href="#input-output" id="id14">Input &amp; Output</a></li>
<li><a class="reference internal" href="#exception-handling" id="id15">Exception handling</a></li>
<li><a class="reference internal" href="#miscellaneous" id="id16">Miscellaneous</a></li>
<li><a class="reference internal" href="#locking-and-finalization" id="id17">Locking and finalization</a></li>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id18">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#material-info-lookup" id="id19">Material info lookup</a></li>
<li><a class="reference internal" href="#random-number-generation" id="id20">Random number generation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#c-function-wrappers" id="id19">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id20">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id21">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id22">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id23">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id24">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id25">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id26">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id27">Constructions module</a></li>
<li><a class="reference internal" href="#screen-api" id="id28">Screen API</a></li>
<li><a class="reference internal" href="#internal-api" id="id29">Internal API</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id21">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id22">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id23">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id24">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id25">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id26">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id27">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id28">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id29">Constructions module</a></li>
<li><a class="reference internal" href="#screen-api" id="id30">Screen API</a></li>
<li><a class="reference internal" href="#internal-api" id="id31">Internal API</a></li>
</ul>
</li>
<li><a class="reference internal" href="#core-interpreter-context" id="id30">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id31">Event type</a></li>
<li><a class="reference internal" href="#core-interpreter-context" id="id32">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id33">Event type</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#lua-modules" id="id32">Lua Modules</a><ul>
<li><a class="reference internal" href="#global-environment" id="id33">Global environment</a></li>
<li><a class="reference internal" href="#utils" id="id34">utils</a></li>
<li><a class="reference internal" href="#dumper" id="id35">dumper</a></li>
<li><a class="reference internal" href="#class" id="id36">class</a></li>
<li><a class="reference internal" href="#lua-modules" id="id34">Lua Modules</a><ul>
<li><a class="reference internal" href="#global-environment" id="id35">Global environment</a></li>
<li><a class="reference internal" href="#utils" id="id36">utils</a></li>
<li><a class="reference internal" href="#dumper" id="id37">dumper</a></li>
<li><a class="reference internal" href="#class" id="id38">class</a></li>
</ul>
</li>
<li><a class="reference internal" href="#in-game-ui-library" id="id37">In-game UI Library</a><ul>
<li><a class="reference internal" href="#gui" id="id38">gui</a><ul>
<li><a class="reference internal" href="#misc" id="id39">Misc</a></li>
<li><a class="reference internal" href="#viewrect-class" id="id40">ViewRect class</a></li>
<li><a class="reference internal" href="#painter-class" id="id41">Painter class</a></li>
<li><a class="reference internal" href="#view-class" id="id42">View class</a></li>
<li><a class="reference internal" href="#screen-class" id="id43">Screen class</a></li>
<li><a class="reference internal" href="#framedscreen-class" id="id44">FramedScreen class</a></li>
<li><a class="reference internal" href="#in-game-ui-library" id="id39">In-game UI Library</a><ul>
<li><a class="reference internal" href="#gui" id="id40">gui</a><ul>
<li><a class="reference internal" href="#misc" id="id41">Misc</a></li>
<li><a class="reference internal" href="#viewrect-class" id="id42">ViewRect class</a></li>
<li><a class="reference internal" href="#painter-class" id="id43">Painter class</a></li>
<li><a class="reference internal" href="#view-class" id="id44">View class</a></li>
<li><a class="reference internal" href="#screen-class" id="id45">Screen class</a></li>
<li><a class="reference internal" href="#framedscreen-class" id="id46">FramedScreen class</a></li>
</ul>
</li>
<li><a class="reference internal" href="#gui-widgets" id="id45">gui.widgets</a><ul>
<li><a class="reference internal" href="#widget-class" id="id46">Widget class</a></li>
<li><a class="reference internal" href="#panel-class" id="id47">Panel class</a></li>
<li><a class="reference internal" href="#pages-class" id="id48">Pages class</a></li>
<li><a class="reference internal" href="#editfield-class" id="id49">EditField class</a></li>
<li><a class="reference internal" href="#label-class" id="id50">Label class</a></li>
<li><a class="reference internal" href="#list-class" id="id51">List class</a></li>
<li><a class="reference internal" href="#filteredlist-class" id="id52">FilteredList class</a></li>
<li><a class="reference internal" href="#gui-widgets" id="id47">gui.widgets</a><ul>
<li><a class="reference internal" href="#widget-class" id="id48">Widget class</a></li>
<li><a class="reference internal" href="#panel-class" id="id49">Panel class</a></li>
<li><a class="reference internal" href="#pages-class" id="id50">Pages class</a></li>
<li><a class="reference internal" href="#editfield-class" id="id51">EditField class</a></li>
<li><a class="reference internal" href="#label-class" id="id52">Label class</a></li>
<li><a class="reference internal" href="#list-class" id="id53">List class</a></li>
<li><a class="reference internal" href="#filteredlist-class" id="id54">FilteredList class</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#plugins" id="id53">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id54">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id55">sort</a></li>
<li><a class="reference internal" href="#eventful" id="id56">Eventful</a><ul>
<li><a class="reference internal" href="#list-of-events" id="id57">List of events</a></li>
<li><a class="reference internal" href="#events-from-eventmanager" id="id58">Events from EventManager</a></li>
<li><a class="reference internal" href="#functions" id="id59">Functions</a></li>
<li><a class="reference internal" href="#examples" id="id60">Examples</a></li>
<li><a class="reference internal" href="#plugins" id="id55">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id56">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id57">sort</a></li>
<li><a class="reference internal" href="#eventful" id="id58">Eventful</a><ul>
<li><a class="reference internal" href="#list-of-events" id="id59">List of events</a></li>
<li><a class="reference internal" href="#events-from-eventmanager" id="id60">Events from EventManager</a></li>
<li><a class="reference internal" href="#functions" id="id61">Functions</a></li>
<li><a class="reference internal" href="#examples" id="id62">Examples</a></li>
</ul>
</li>
<li><a class="reference internal" href="#building-hacks" id="id63">Building-hacks</a><ul>
<li><a class="reference internal" href="#id1" id="id64">Functions</a></li>
<li><a class="reference internal" href="#id2" id="id65">Examples</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scripts" id="id61">Scripts</a><ul>
<li><a class="reference internal" href="#save-init-script" id="id62">Save init script</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scripts" id="id66">Scripts</a><ul>
<li><a class="reference internal" href="#save-init-script" id="id67">Save init script</a></li>
</ul>
</li>
</ul>
@ -449,10 +454,11 @@ native C++ commands, and invoked by plugins written in c++.</p>
It does not describe all of the utility functions
implemented by Lua files located in hack/lua/...</p>
<div class="section" id="df-data-structure-wrapper">
<h1><a class="toc-backref" href="#id1">DF data 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>
<h1><a class="toc-backref" href="#id3">DF data structure wrapper</a></h1>
<p>Data structures of the game are defined in XML files located in library/xml
(and online at <a class="reference external" href="http://github.com/DFHack/df-structures">http://github.com/DFHack/df-structures</a>), and automatically exported
to lua code as a tree of objects and functions under the <tt class="docutils literal">df</tt> global, which
also broadly maps to the <tt class="docutils literal">df</tt> namespace in the headers generated for 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
@ -485,7 +491,7 @@ both nested types and fields corresponding to global symbols.</p>
<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>
<h2><a class="toc-backref" href="#id4">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>
@ -534,7 +540,7 @@ 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>
<h3><a class="toc-backref" href="#id5">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,
@ -546,7 +552,7 @@ no bound checking is performed, since buffer length is not available.
Index 0 is equivalent to the <tt class="docutils literal">value</tt> field.</p>
</div>
<div class="section" id="struct-references">
<h3><a class="toc-backref" href="#id4">Struct references</a></h3>
<h3><a class="toc-backref" href="#id6">Struct references</a></h3>
<p>Struct references are used for class and struct objects.</p>
<p>They implement the following features:</p>
<ul>
@ -580,7 +586,7 @@ shadowing rules.</p>
</ul>
</div>
<div class="section" id="container-references">
<h3><a class="toc-backref" href="#id5">Container references</a></h3>
<h3><a class="toc-backref" href="#id7">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
@ -624,7 +630,7 @@ use <tt class="docutils literal">#ref</tt>, or just <tt class="docutils literal"
</ul>
</div>
<div class="section" id="bitfield-references">
<h3><a class="toc-backref" href="#id6">Bitfield references</a></h3>
<h3><a class="toc-backref" href="#id8">Bitfield references</a></h3>
<p>Bitfields behave like special fixed-size containers.
Consider them to be something in between structs and
fixed-size vectors.</p>
@ -640,7 +646,7 @@ them, e.g. <tt class="docutils literal">matinfo:matches{metal=true}</tt>.</p>
</div>
</div>
<div class="section" id="named-types">
<h2><a class="toc-backref" href="#id7">Named types</a></h2>
<h2><a class="toc-backref" href="#id9">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>
@ -678,7 +684,7 @@ xml have a <tt class="docutils literal">type.find(key)</tt> function that wraps
method provided in C++.</p>
</div>
<div class="section" id="global-functions">
<h2><a class="toc-backref" href="#id8">Global functions</a></h2>
<h2><a class="toc-backref" href="#id10">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>
@ -723,7 +729,7 @@ a lightuserdata, or a number.</p>
</ul>
</div>
<div class="section" id="recursive-table-assignment">
<h2><a class="toc-backref" href="#id9">Recursive table assignment</a></h2>
<h2><a class="toc-backref" href="#id11">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">
@ -805,12 +811,12 @@ cleanup.</p>
</div>
</div>
<div class="section" id="dfhack-api">
<h1><a class="toc-backref" href="#id10">DFHack API</a></h1>
<h1><a class="toc-backref" href="#id12">DFHack API</a></h1>
<p>DFHack utility functions are placed in the <tt class="docutils literal">dfhack</tt> global tree.</p>
<div class="section" id="native-utilities">
<h2><a class="toc-backref" href="#id11">Native utilities</a></h2>
<h2><a class="toc-backref" href="#id13">Native utilities</a></h2>
<div class="section" id="input-output">
<h3><a class="toc-backref" href="#id12">Input &amp; Output</a></h3>
<h3><a class="toc-backref" href="#id14">Input &amp; Output</a></h3>
<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,
@ -846,7 +852,7 @@ string, global environment and command-line history file.</p>
</ul>
</div>
<div class="section" id="exception-handling">
<h3><a class="toc-backref" href="#id13">Exception handling</a></h3>
<h3><a class="toc-backref" href="#id15">Exception handling</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.error(msg[,level[,verbose]])</span></tt></p>
<p>Throws a dfhack exception object with location and stack trace.
@ -902,7 +908,7 @@ following properties:</p>
</ul>
</div>
<div class="section" id="miscellaneous">
<h3><a class="toc-backref" href="#id14">Miscellaneous</a></h3>
<h3><a class="toc-backref" href="#id16">Miscellaneous</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.VERSION</tt></p>
<p>DFHack version string constant.</p>
@ -915,7 +921,7 @@ both from the curry call and the closure call itself. I.e.
</ul>
</div>
<div class="section" id="locking-and-finalization">
<h3><a class="toc-backref" href="#id15">Locking and finalization</a></h3>
<h3><a class="toc-backref" href="#id17">Locking and finalization</a></h3>
<ul>
<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.
@ -948,7 +954,7 @@ Implemented using <tt class="docutils literal"><span class="pre">call_with_final
</ul>
</div>
<div class="section" id="persistent-configuration-storage">
<h3><a class="toc-backref" href="#id16">Persistent configuration storage</a></h3>
<h3><a class="toc-backref" href="#id18">Persistent configuration storage</a></h3>
<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
@ -998,7 +1004,7 @@ as an all-zero mask.</p>
the persistent entry will <strong>NOT</strong> delete the associated masks.</p>
</div>
<div class="section" id="material-info-lookup">
<h3><a class="toc-backref" href="#id17">Material info lookup</a></h3>
<h3><a class="toc-backref" href="#id19">Material info lookup</a></h3>
<p>A material info record has fields:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p>
@ -1042,7 +1048,7 @@ Accept dfhack_material_category auto-assign table.</p>
</ul>
</div>
<div class="section" id="random-number-generation">
<h3><a class="toc-backref" href="#id18">Random number generation</a></h3>
<h3><a class="toc-backref" href="#id20">Random number generation</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.random.new([seed[,perturb_count]])</span></tt></p>
<p>Creates a new random number generator object. Without any
@ -1086,7 +1092,7 @@ Dimension may be 1, 2 or 3 (default).</p>
</div>
</div>
<div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id19">C++ function wrappers</a></h2>
<h2><a class="toc-backref" href="#id21">C++ function wrappers</a></h2>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.
One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments
@ -1127,7 +1133,7 @@ can be omitted.</p>
</li>
</ul>
<div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id20">Gui module</a></h3>
<h3><a class="toc-backref" href="#id22">Gui module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getCurViewscreen([skip_dismissed])</span></tt></p>
<p>Returns the topmost viewscreen. If <tt class="docutils literal">skip_dismissed</tt> is <em>true</em>,
@ -1197,7 +1203,7 @@ operations accordingly. The units are used to call <tt class="docutils literal">
</ul>
</div>
<div class="section" id="job-module">
<h3><a class="toc-backref" href="#id21">Job module</a></h3>
<h3><a class="toc-backref" href="#id23">Job module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p>
@ -1252,10 +1258,13 @@ the flags in the job item.</p>
<li><p class="first"><tt class="docutils literal">dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)</tt></p>
<p>Likewise, if replacing material.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getName(job)</tt></p>
<p>Returns the job's description, as seen in the Units and Jobs screens.</p>
</li>
</ul>
</div>
<div class="section" id="units-module">
<h3><a class="toc-backref" href="#id22">Units module</a></h3>
<h3><a class="toc-backref" href="#id24">Units module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p>
<p>Returns true <em>x,y,z</em> of the unit, or <em>nil</em> if invalid; may be not equal to unit.pos if caged.</p>
@ -1359,7 +1368,7 @@ or raws. The <tt class="docutils literal">ignore_noble</tt> boolean disables the
</ul>
</div>
<div class="section" id="items-module">
<h3><a class="toc-backref" href="#id23">Items module</a></h3>
<h3><a class="toc-backref" href="#id25">Items module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p>
<p>Returns true <em>x,y,z</em> of the item, or <em>nil</em> if invalid; may be not equal to item.pos if in inventory.</p>
@ -1420,10 +1429,16 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.items.getSubtypeDef(item_type, subtype)</tt></p>
<p>Returns the raw definition for the given item type and subtype, or <em>nil</em> if invalid.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)</tt></p>
<p>Calculates the base value for an item of the specified type and material.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getValue(item)</tt></p>
<p>Calculates the Basic Value of an item, as seen in the View Item screen.</p>
</li>
</ul>
</div>
<div class="section" id="maps-module">
<h3><a class="toc-backref" href="#id24">Maps module</a></h3>
<h3><a class="toc-backref" href="#id26">Maps module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getSize()</tt></p>
<p>Returns map size in blocks: <em>x, y, z</em></p>
@ -1492,7 +1507,7 @@ burrows, or the presence of invaders.</p>
</ul>
</div>
<div class="section" id="burrows-module">
<h3><a class="toc-backref" href="#id25">Burrows module</a></h3>
<h3><a class="toc-backref" href="#id27">Burrows module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.findByName(name)</tt></p>
<p>Returns the burrow pointer or <em>nil</em>.</p>
@ -1527,7 +1542,7 @@ burrows, or the presence of invaders.</p>
</ul>
</div>
<div class="section" id="buildings-module">
<h3><a class="toc-backref" href="#id26">Buildings module</a></h3>
<h3><a class="toc-backref" href="#id28">Buildings module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getGeneralRef(building, type)</tt></p>
<p>Searches for a general_ref with the given type.</p>
@ -1677,7 +1692,7 @@ can be determined this way, <tt class="docutils literal">constructBuilding</tt>
</ul>
</div>
<div class="section" id="constructions-module">
<h3><a class="toc-backref" href="#id27">Constructions module</a></h3>
<h3><a class="toc-backref" href="#id29">Constructions module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateNew(pos,type,item_type,mat_index)</tt></p>
<p>Designates a new construction at given position. If there already is
@ -1693,7 +1708,7 @@ Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none fo
</ul>
</div>
<div class="section" id="screen-api">
<h3><a class="toc-backref" href="#id28">Screen API</a></h3>
<h3><a class="toc-backref" href="#id30">Screen API</a></h3>
<p>The screen module implements support for drawing to the tiled screen of the game.
Note that drawing only has any effect when done from callbacks, so it can only
be feasibly used in the core context.</p>
@ -1881,7 +1896,7 @@ options; if multiple interpretations exist, the table will contain multiple keys
</ul>
</div>
<div class="section" id="internal-api">
<h3><a class="toc-backref" href="#id29">Internal API</a></h3>
<h3><a class="toc-backref" href="#id31">Internal API</a></h3>
<p>These functions are intended for the use by dfhack developers,
and are only documented here for completeness:</p>
<ul>
@ -1950,7 +1965,7 @@ Returns: <em>file_names</em> or empty table if not found.</p>
</div>
</div>
<div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id30">Core interpreter context</a></h2>
<h2><a class="toc-backref" href="#id32">Core interpreter context</a></h2>
<p>While plugins can create any number of interpreter instances,
there is one special context managed by dfhack core. It is the
only context that can receive events from DF and plugins.</p>
@ -1981,7 +1996,7 @@ Using <tt class="docutils literal">timeout_active(id,nil)</tt> cancels the timer
</li>
</ul>
<div class="section" id="event-type">
<h3><a class="toc-backref" href="#id31">Event type</a></h3>
<h3><a class="toc-backref" href="#id33">Event type</a></h3>
<p>An event is a native object transparently wrapping a lua table,
and implementing a __call metamethod. When it is invoked, it loops
through the table with next and calls all contained values.
@ -2013,7 +2028,7 @@ order using <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div>
</div>
<div class="section" id="lua-modules">
<h1><a class="toc-backref" href="#id32">Lua Modules</a></h1>
<h1><a class="toc-backref" href="#id34">Lua Modules</a></h1>
<p>DFHack sets up the lua interpreter so that the built-in <tt class="docutils literal">require</tt>
function can be used to load shared lua code from hack/lua/.
The <tt class="docutils literal">dfhack</tt> namespace reference itself may be obtained via
@ -2042,7 +2057,7 @@ in this document.</p>
</li>
</ul>
<div class="section" id="global-environment">
<h2><a class="toc-backref" href="#id33">Global environment</a></h2>
<h2><a class="toc-backref" href="#id35">Global environment</a></h2>
<p>A number of variables and functions are provided in the base global
environment by the mandatory init file dfhack.lua:</p>
<ul>
@ -2105,7 +2120,7 @@ Returns <em>nil</em> if any of obj or indices is <em>nil</em>, or a numeric inde
</ul>
</div>
<div class="section" id="utils">
<h2><a class="toc-backref" href="#id34">utils</a></h2>
<h2><a class="toc-backref" href="#id36">utils</a></h2>
<ul>
<li><p class="first"><tt class="docutils literal">utils.compare(a,b)</tt></p>
<p>Comparator function; returns <em>-1</em> if a&lt;b, <em>1</em> if a&gt;b, <em>0</em> otherwise.</p>
@ -2254,7 +2269,7 @@ throws an error.</p>
</ul>
</div>
<div class="section" id="dumper">
<h2><a class="toc-backref" href="#id35">dumper</a></h2>
<h2><a class="toc-backref" href="#id37">dumper</a></h2>
<p>A third-party lua table dumper module from
<a class="reference external" href="http://lua-users.org/wiki/DataDumper">http://lua-users.org/wiki/DataDumper</a>. Defines one
function:</p>
@ -2267,7 +2282,7 @@ the other arguments see the original documentation link above.</p>
</ul>
</div>
<div class="section" id="class">
<h2><a class="toc-backref" href="#id36">class</a></h2>
<h2><a class="toc-backref" href="#id38">class</a></h2>
<p>Implements a trivial single-inheritance class system.</p>
<ul>
<li><p class="first"><tt class="docutils literal">Foo = defclass(Foo[, ParentClass])</tt></p>
@ -2358,7 +2373,7 @@ library itself uses them for constructors.</p>
</div>
</div>
<div class="section" id="in-game-ui-library">
<h1><a class="toc-backref" href="#id37">In-game UI Library</a></h1>
<h1><a class="toc-backref" href="#id39">In-game UI Library</a></h1>
<p>A number of lua modules with names starting with <tt class="docutils literal">gui</tt> are dedicated
to wrapping the natives of the <tt class="docutils literal">dfhack.screen</tt> module in a way that
is easy to use. This allows relatively easily and naturally creating
@ -2367,12 +2382,12 @@ dialogs that integrate in the main game UI window.</p>
things ranging from the basic <tt class="docutils literal">Painter</tt>, <tt class="docutils literal">View</tt> and <tt class="docutils literal">Screen</tt>
classes, to fully functional predefined dialogs.</p>
<div class="section" id="gui">
<h2><a class="toc-backref" href="#id38">gui</a></h2>
<h2><a class="toc-backref" href="#id40">gui</a></h2>
<p>This module defines the most important classes and functions for
implementing interfaces. This documents those of them that are
considered stable.</p>
<div class="section" id="misc">
<h3><a class="toc-backref" href="#id39">Misc</a></h3>
<h3><a class="toc-backref" href="#id41">Misc</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">USE_GRAPHICS</tt></p>
<p>Contains the value of <tt class="docutils literal">dfhack.screen.inGraphicsMode()</tt>, which cannot be
@ -2411,7 +2426,7 @@ msec. This is intended for rendering blinking interface objects.</p>
</ul>
</div>
<div class="section" id="viewrect-class">
<h3><a class="toc-backref" href="#id40">ViewRect class</a></h3>
<h3><a class="toc-backref" href="#id42">ViewRect class</a></h3>
<p>This class represents an on-screen rectangle with an associated independent
clip area rectangle. It is the base of the <tt class="docutils literal">Painter</tt> class, and is used by
<tt class="docutils literal">Views</tt> to track their client area.</p>
@ -2459,7 +2474,7 @@ it with the clip area of the original object.</p>
</ul>
</div>
<div class="section" id="painter-class">
<h3><a class="toc-backref" href="#id41">Painter class</a></h3>
<h3><a class="toc-backref" href="#id43">Painter class</a></h3>
<p>The painting natives in <tt class="docutils literal">dfhack.screen</tt> apply to the whole screen, are
completely stateless and don't implement clipping.</p>
<p>The Painter class inherits from ViewRect to provide clipping and local
@ -2530,7 +2545,7 @@ painter:pen(foo):seek(x,y):char(1):advance(1):string('bar')...
</pre>
</div>
<div class="section" id="view-class">
<h3><a class="toc-backref" href="#id42">View class</a></h3>
<h3><a class="toc-backref" href="#id44">View class</a></h3>
<p>This class is the common abstract base of both the stand-alone screens
and common widgets to be used inside them. It defines the basic layout,
rendering and event handling framework.</p>
@ -2654,7 +2669,7 @@ Returns <em>true</em> if any of the subviews handled the event.</p>
</ul>
</div>
<div class="section" id="screen-class">
<h3><a class="toc-backref" href="#id43">Screen class</a></h3>
<h3><a class="toc-backref" href="#id45">Screen class</a></h3>
<p>This is a View subclass intended for use as a stand-alone dialog or screen.
It adds the following methods:</p>
<ul>
@ -2708,7 +2723,7 @@ the screen is removed by any means here.</p>
</ul>
</div>
<div class="section" id="framedscreen-class">
<h3><a class="toc-backref" href="#id44">FramedScreen class</a></h3>
<h3><a class="toc-backref" href="#id46">FramedScreen class</a></h3>
<p>A Screen subclass that paints a visible frame around its body.
Most dialogs should inherit from this class.</p>
<p>A framed screen has the following attributes:</p>
@ -2746,10 +2761,10 @@ Most dialogs should inherit from this class.</p>
</div>
</div>
<div class="section" id="gui-widgets">
<h2><a class="toc-backref" href="#id45">gui.widgets</a></h2>
<h2><a class="toc-backref" href="#id47">gui.widgets</a></h2>
<p>This module implements some basic widgets based on the View infrastructure.</p>
<div class="section" id="widget-class">
<h3><a class="toc-backref" href="#id46">Widget class</a></h3>
<h3><a class="toc-backref" href="#id48">Widget class</a></h3>
<p>Base of all the widgets. Inherits from View and has the following attributes:</p>
<ul>
<li><p class="first"><tt class="docutils literal">frame = <span class="pre">{...}</span></tt></p>
@ -2815,7 +2830,7 @@ inset, or a table with the following fields:</p>
</ul>
</div>
<div class="section" id="panel-class">
<h3><a class="toc-backref" href="#id47">Panel class</a></h3>
<h3><a class="toc-backref" href="#id49">Panel class</a></h3>
<p>Inherits from Widget, and intended for grouping a number of subviews.</p>
<p>Has attributes:</p>
<ul>
@ -2828,7 +2843,7 @@ inset, or a table with the following fields:</p>
</ul>
</div>
<div class="section" id="pages-class">
<h3><a class="toc-backref" href="#id48">Pages class</a></h3>
<h3><a class="toc-backref" href="#id50">Pages class</a></h3>
<p>Subclass of Panel; keeps exactly one child visible.</p>
<ul>
<li><p class="first"><tt class="docutils literal">Pages{ <span class="pre">...,</span> selected = ... }</tt></p>
@ -2844,7 +2859,7 @@ It is permitted to use the subview object, or its <tt class="docutils literal">v
</ul>
</div>
<div class="section" id="editfield-class">
<h3><a class="toc-backref" href="#id49">EditField class</a></h3>
<h3><a class="toc-backref" href="#id51">EditField class</a></h3>
<p>Subclass of Widget; implements a simple edit field.</p>
<p>Attributes:</p>
<table class="docutils field-list" frame="void" rules="none">
@ -2866,7 +2881,7 @@ If it returns false, the character is ignored.</td>
</table>
</div>
<div class="section" id="label-class">
<h3><a class="toc-backref" href="#id50">Label class</a></h3>
<h3><a class="toc-backref" href="#id52">Label class</a></h3>
<p>This Widget subclass implements flowing semi-static text.</p>
<p>It has the following attributes:</p>
<table class="docutils field-list" frame="void" rules="none">
@ -2962,7 +2977,7 @@ this may be extended with mouse click support.</p>
</ul>
</div>
<div class="section" id="list-class">
<h3><a class="toc-backref" href="#id51">List class</a></h3>
<h3><a class="toc-backref" href="#id53">List class</a></h3>
<p>The List widget implements a simple list with paging.</p>
<p>It has the following attributes:</p>
<table class="docutils field-list" frame="void" rules="none">
@ -3048,7 +3063,7 @@ with the following fields:</p>
</ul>
</div>
<div class="section" id="filteredlist-class">
<h3><a class="toc-backref" href="#id52">FilteredList class</a></h3>
<h3><a class="toc-backref" href="#id54">FilteredList class</a></h3>
<p>This widget combines List, EditField and Label into a combo-box like
construction that allows filtering the list by subwords of its items.</p>
<p>In addition to passing through all attributes supported by List, it
@ -3101,14 +3116,14 @@ index <tt class="docutils literal">pos</tt> in the <em>unfiltered</em> list if p
</div>
</div>
<div class="section" id="plugins">
<h1><a class="toc-backref" href="#id53">Plugins</a></h1>
<h1><a class="toc-backref" href="#id55">Plugins</a></h1>
<p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p>
<div class="section" id="burrows">
<h2><a class="toc-backref" href="#id54">burrows</a></h2>
<h2><a class="toc-backref" href="#id56">burrows</a></h2>
<p>Implements extended burrow manipulations.</p>
<p>Events:</p>
<ul>
@ -3146,16 +3161,16 @@ set is the same as used by the command line.</p>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div>
<div class="section" id="sort">
<h2><a class="toc-backref" href="#id55">sort</a></h2>
<h2><a class="toc-backref" href="#id57">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p>
</div>
<div class="section" id="eventful">
<h2><a class="toc-backref" href="#id56">Eventful</a></h2>
<h2><a class="toc-backref" href="#id58">Eventful</a></h2>
<p>This plugin exports some events to lua thus allowing to run lua functions
on DF world events.</p>
<div class="section" id="list-of-events">
<h3><a class="toc-backref" href="#id57">List of events</a></h3>
<h3><a class="toc-backref" href="#id59">List of events</a></h3>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">onReactionComplete(reaction,unit,input_items,input_reagents,output_items,call_native)</tt></p>
<p>Auto activates if detects reactions starting with <tt class="docutils literal">LUA_HOOK_</tt>. Is called when reaction finishes.</p>
@ -3185,7 +3200,7 @@ tweaking (e.g. adding custom reactions)</p>
</ol>
</div>
<div class="section" id="events-from-eventmanager">
<h3><a class="toc-backref" href="#id58">Events from EventManager</a></h3>
<h3><a class="toc-backref" href="#id60">Events from EventManager</a></h3>
<p>These events are straight from EventManager module. Each of them first needs to be enabled. See functions for more info. If you register a listener before the game is loaded, be aware that no events will be triggered immediately after loading, so you might need to add another event listener for when the game first loads in some cases.</p>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">onBuildingCreatedDestroyed(building_id)</tt></p>
@ -3218,7 +3233,7 @@ tweaking (e.g. adding custom reactions)</p>
</ol>
</div>
<div class="section" id="functions">
<h3><a class="toc-backref" href="#id59">Functions</a></h3>
<h3><a class="toc-backref" href="#id61">Functions</a></h3>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">registerReaction(reaction_name,callback)</tt></p>
<p>Simplified way of using onReactionComplete; the callback is function (same params as event).</p>
@ -3232,10 +3247,13 @@ tweaking (e.g. adding custom reactions)</p>
<li><p class="first"><tt class="docutils literal">enableEvent(evType,frequency)</tt></p>
<p>Enable event checking for EventManager events. For event types use <tt class="docutils literal">eventType</tt> table. Note that different types of events require different frequencies to be effective. The frequency is how many ticks EventManager will wait before checking if that type of event has happened. If multiple scripts or plugins use the same event type, the smallest frequency is the one that is used, so you might get events triggered more often than the frequency you use here.</p>
</li>
<li><p class="first"><tt class="docutils literal">registerSidebar(shop_name,callback)</tt></p>
<p>Enable callback when sidebar for <tt class="docutils literal">shop_name</tt> is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib.</p>
</li>
</ol>
</div>
<div class="section" id="examples">
<h3><a class="toc-backref" href="#id60">Examples</a></h3>
<h3><a class="toc-backref" href="#id62">Examples</a></h3>
<p>Spawn dragon breath on each item attempt to contaminate wound:</p>
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
@ -3247,13 +3265,13 @@ end
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native)
b.registerReaction(&quot;LUA_HOOK_LAY_BOMB&quot;,function(reaction,unit,in_items,in_reag,out_items,call_native)
local pos=copyall(unit.pos)
-- spawn dragonbreath after 100 ticks
dfhack.timeout(100,&quot;ticks&quot;,function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end)
--do not call real item creation code
call_native.value=false
end
end)
</pre>
<p>Grenade example:</p>
<pre class="literal-block">
@ -3270,9 +3288,57 @@ b.addReactionToShop(&quot;TAN_A_HIDE&quot;,&quot;LEATHERWORKS&quot;)
</pre>
</div>
</div>
<div class="section" id="building-hacks">
<h2><a class="toc-backref" href="#id63">Building-hacks</a></h2>
<p>This plugin overwrites some methods in workshop df class so that mechanical workshops are possible. Although
plugin export a function it's recommended to use lua decorated function.</p>
<div class="section" id="id1">
<h3><a class="toc-backref" href="#id64">Functions</a></h3>
<dl class="docutils">
<dt><tt class="docutils literal">registerBuilding(table)</tt> where table must contain name, as a workshop raw name, the rest are optional:</dt>
<dd><ol class="first last arabic simple">
<li>name -- custom workshop id e.g. <tt class="docutils literal">SOAPMAKER</tt></li>
<li>fix_impassible -- if true make impassible tiles impassible to liquids too</li>
<li>consume -- how much machine power is needed to work. Disables reactions if not supplied enough</li>
<li>produce -- how much machine power is produced. Use discouraged as there is no way to change this at runtime</li>
<li>gears -- a table or <tt class="docutils literal"><span class="pre">{x=?,y=?}</span></tt> of connection points for machines</li>
<li>action -- a table of number (how much ticks to skip) and a function which gets called on shop update</li>
<li>animate -- a table of frames which can be a table of:<ol class="loweralpha">
<li>tables of 4 numbers <tt class="docutils literal">{tile,fore,back,bright}</tt> OR</li>
<li>empty table (tile not modified) OR</li>
<li><tt class="docutils literal"><span class="pre">{x=&lt;number&gt;</span> <span class="pre">y=&lt;number&gt;</span> + 4 numbers like in first case}</tt>, this generates full frame useful for animations that change little (1-2 tiles)</li>
</ol>
</li>
</ol>
</dd>
<dt>Animate table also might contain:</dt>
<dd><ol class="first last arabic simple">
<li>frameLenght -- how many ticks does one frame take OR</li>
<li>isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning)</li>
</ol>
</dd>
</dl>
</div>
<div class="section" id="id2">
<h3><a class="toc-backref" href="#id65">Examples</a></h3>
<p>Simple mechanical workshop:</p>
<pre class="literal-block">
require('plugins.building-hacks').registerBuilding{name=&quot;BONE_GRINDER&quot;,
consume=15,
gears={x=0,y=0}, --connection point
animate={
isMechanical=true, --animate the same connection point as vanilla gear
frames={
{{x=0,y=0,42,7,0,0}}, --first frame, 1 changed tile
{{x=0,y=0,15,7,0,0}} -- second frame, same
}
}
</pre>
</div>
</div>
</div>
<div class="section" id="scripts">
<h1><a class="toc-backref" href="#id61">Scripts</a></h1>
<h1><a class="toc-backref" href="#id66">Scripts</a></h1>
<p>Any files with the .lua extension placed into hack/scripts/*
are automatically used by the DFHack core as commands. The
matching command name consists of the name of the file sans
@ -3303,7 +3369,7 @@ The <tt class="docutils literal">name</tt> argument should be the name stem, as
</ul>
<p>Note that this function lets errors propagate to the caller.</p>
<div class="section" id="save-init-script">
<h2><a class="toc-backref" href="#id62">Save init script</a></h2>
<h2><a class="toc-backref" href="#id67">Save init script</a></h2>
<p>If a save directory contains a file called <tt class="docutils literal">raw/init.lua</tt>, it is
automatically loaded and executed every time the save is loaded.
The same applies to any files called <tt class="docutils literal"><span class="pre">raw/init.d/*.lua</span></tt>. Every

@ -25,9 +25,10 @@ implemented by Lua files located in hack/lua/...
DF data 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++.
Data structures of the game are defined in XML files located in library/xml
(and online at http://github.com/DFHack/df-structures), and automatically exported
to lua code as a tree of objects and functions under the ``df`` global, which
also broadly maps to the ``df`` namespace in the headers generated for C++.
**WARNING**: The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
@ -979,6 +980,10 @@ Job module
Likewise, if replacing material.
* ``dfhack.job.getName(job)``
Returns the job's description, as seen in the Units and Jobs screens.
Units module
------------
@ -1187,6 +1192,14 @@ Items module
Returns the raw definition for the given item type and subtype, or *nil* if invalid.
* ``dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)``
Calculates the base value for an item of the specified type and material.
* ``dfhack.items.getValue(item)``
Calculates the Basic Value of an item, as seen in the View Item screen.
Maps module
-----------
@ -3167,6 +3180,10 @@ Functions
Enable event checking for EventManager events. For event types use ``eventType`` table. Note that different types of events require different frequencies to be effective. The frequency is how many ticks EventManager will wait before checking if that type of event has happened. If multiple scripts or plugins use the same event type, the smallest frequency is the one that is used, so you might get events triggered more often than the frequency you use here.
5. ``registerSidebar(shop_name,callback)``
Enable callback when sidebar for ``shop_name`` is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib.
Examples
--------
Spawn dragon breath on each item attempt to contaminate wound::
@ -3180,13 +3197,13 @@ Reaction complete example::
b=require "plugins.eventful"
b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native)
b.registerReaction("LUA_HOOK_LAY_BOMB",function(reaction,unit,in_items,in_reag,out_items,call_native)
local pos=copyall(unit.pos)
-- spawn dragonbreath after 100 ticks
dfhack.timeout(100,"ticks",function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end)
--do not call real item creation code
call_native.value=false
end
end)
Grenade example::
@ -3201,6 +3218,48 @@ Integrated tannery::
b=require "plugins.eventful"
b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS")
Building-hacks
==============
This plugin overwrites some methods in workshop df class so that mechanical workshops are possible. Although
plugin export a function it's recommended to use lua decorated function.
Functions
---------
``registerBuilding(table)`` where table must contain name, as a workshop raw name, the rest are optional:
1. name -- custom workshop id e.g. ``SOAPMAKER``
2. fix_impassible -- if true make impassible tiles impassible to liquids too
3. consume -- how much machine power is needed to work. Disables reactions if not supplied enough
4. produce -- how much machine power is produced. Use discouraged as there is no way to change this at runtime
5. gears -- a table or ``{x=?,y=?}`` of connection points for machines
6. action -- a table of number (how much ticks to skip) and a function which gets called on shop update
7. animate -- a table of frames which can be a table of:
a. tables of 4 numbers ``{tile,fore,back,bright}`` OR
b. empty table (tile not modified) OR
c. ``{x=<number> y=<number> + 4 numbers like in first case}``, this generates full frame useful for animations that change little (1-2 tiles)
Animate table also might contain:
1. frameLenght -- how many ticks does one frame take OR
2. isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning)
Examples
--------
Simple mechanical workshop::
require('plugins.building-hacks').registerBuilding{name="BONE_GRINDER",
consume=15,
gears={x=0,y=0}, --connection point
animate={
isMechanical=true, --animate the same connection point as vanilla gear
frames={
{{x=0,y=0,42,7,0,0}}, --first frame, 1 changed tile
{{x=0,y=0,15,7,0,0}} -- second frame, same
}
}
=======
Scripts
=======

@ -5,11 +5,14 @@ DFHack future
- Lua API for listing files in directory. Needed for mod-manager.
- Lua API for creating unit combat reports and writing to gamelog.
- support for multiple raw/init.d/*.lua init scripts in one save.
- eventful now has a more friendly way of making custom sidebars
- new plugin: building-hacks. Allows to add custom functionality and/or animations to buildings.
New scripts:
- gui/mod-manager: allows installing/uninstalling mods into df from df/mods directory.
- gui/clone-uniform: duplicates the currently selected uniform in the military screen.
- fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it)
- undump-buildings: removes dump designation from materials used in buildings.
New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands

@ -543,48 +543,49 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#embark" id="id146">embark</a></li>
<li><a class="reference internal" href="#lever" id="id147">lever</a></li>
<li><a class="reference internal" href="#stripcaged" id="id148">stripcaged</a></li>
<li><a class="reference internal" href="#create-items" id="id149">create-items</a></li>
<li><a class="reference internal" href="#locate-ore" id="id150">locate-ore</a></li>
<li><a class="reference internal" href="#soundsense-season" id="id151">soundsense-season</a></li>
<li><a class="reference internal" href="#multicmd" id="id152">multicmd</a></li>
<li><a class="reference internal" href="#undump-buildings" id="id149">undump-buildings</a></li>
<li><a class="reference internal" href="#create-items" id="id150">create-items</a></li>
<li><a class="reference internal" href="#locate-ore" id="id151">locate-ore</a></li>
<li><a class="reference internal" href="#soundsense-season" id="id152">soundsense-season</a></li>
<li><a class="reference internal" href="#multicmd" id="id153">multicmd</a></li>
</ul>
</li>
<li><a class="reference internal" href="#in-game-interface-tools" id="id153">In-game interface tools</a><ul>
<li><a class="reference internal" href="#dwarf-manipulator" id="id154">Dwarf Manipulator</a></li>
<li><a class="reference internal" href="#search" id="id155">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id156">AutoMaterial</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id157">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id158">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-rename" id="id159">gui/rename</a></li>
<li><a class="reference internal" href="#gui-room-list" id="id160">gui/room-list</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id161">gui/choose-weapons</a></li>
<li><a class="reference internal" href="#gui-clone-uniform" id="id162">gui/clone-uniform</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id163">gui/guide-path</a></li>
<li><a class="reference internal" href="#gui-workshop-job" id="id164">gui/workshop-job</a></li>
<li><a class="reference internal" href="#gui-workflow" id="id165">gui/workflow</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id166">gui/assign-rack</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id167">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-companion-order" id="id168">gui/companion-order</a></li>
<li><a class="reference internal" href="#gui-gm-editor" id="id169">gui/gm-editor</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id170">gui/mod-manager</a></li>
<li><a class="reference internal" href="#in-game-interface-tools" id="id154">In-game interface tools</a><ul>
<li><a class="reference internal" href="#dwarf-manipulator" id="id155">Dwarf Manipulator</a></li>
<li><a class="reference internal" href="#search" id="id156">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id157">AutoMaterial</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id158">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id159">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-rename" id="id160">gui/rename</a></li>
<li><a class="reference internal" href="#gui-room-list" id="id161">gui/room-list</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id162">gui/choose-weapons</a></li>
<li><a class="reference internal" href="#gui-clone-uniform" id="id163">gui/clone-uniform</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id164">gui/guide-path</a></li>
<li><a class="reference internal" href="#gui-workshop-job" id="id165">gui/workshop-job</a></li>
<li><a class="reference internal" href="#gui-workflow" id="id166">gui/workflow</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id167">gui/assign-rack</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id168">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-companion-order" id="id169">gui/companion-order</a></li>
<li><a class="reference internal" href="#gui-gm-editor" id="id170">gui/gm-editor</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id171">gui/mod-manager</a></li>
</ul>
</li>
<li><a class="reference internal" href="#behavior-mods" id="id171">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id172">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id173">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id174">Configuration UI</a></li>
<li><a class="reference internal" href="#behavior-mods" id="id172">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id173">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id174">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id175">Configuration UI</a></li>
</ul>
</li>
<li><a class="reference internal" href="#power-meter" id="id175">Power Meter</a></li>
<li><a class="reference internal" href="#steam-engine" id="id176">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id177">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id178">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id179">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id180">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id181">Save files</a></li>
<li><a class="reference internal" href="#power-meter" id="id176">Power Meter</a></li>
<li><a class="reference internal" href="#steam-engine" id="id177">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id178">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id179">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id180">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id181">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id182">Save files</a></li>
</ul>
</li>
<li><a class="reference internal" href="#add-spatter" id="id182">Add Spatter</a></li>
<li><a class="reference internal" href="#add-spatter" id="id183">Add Spatter</a></li>
</ul>
</li>
</ul>
@ -3171,8 +3172,12 @@ alternatively pass cage IDs as arguments:</p>
stripcaged weapons 25321 34228
</pre>
</div>
<div class="section" id="undump-buildings">
<h2><a class="toc-backref" href="#id149">undump-buildings</a></h2>
<p>Undesignates building base materials for dumping.</p>
</div>
<div class="section" id="create-items">
<h2><a class="toc-backref" href="#id149">create-items</a></h2>
<h2><a class="toc-backref" href="#id150">create-items</a></h2>
<p>Spawn arbitrary items under the cursor.</p>
<p>The first argument gives the item category, the second gives the material,
and the optionnal third gives the number of items to create (defaults to 20).</p>
@ -3194,7 +3199,7 @@ create-items bar adamantine
</pre>
</div>
<div class="section" id="locate-ore">
<h2><a class="toc-backref" href="#id150">locate-ore</a></h2>
<h2><a class="toc-backref" href="#id151">locate-ore</a></h2>
<p>Scan the map for metal ores.</p>
<p>Finds and designate for digging one tile of a specific metal ore.
Only works for native metal ores, does not handle reaction stuff (eg STEEL).</p>
@ -3207,7 +3212,7 @@ locate-ore iron</dd>
</dl>
</div>
<div class="section" id="soundsense-season">
<h2><a class="toc-backref" href="#id151">soundsense-season</a></h2>
<h2><a class="toc-backref" href="#id152">soundsense-season</a></h2>
<p>It is a well known issue that Soundsense cannot detect the correct
current season when a savegame is loaded and has to play random
season music until a season switch occurs.</p>
@ -3216,7 +3221,7 @@ to gamelog.txt on every map load to fix this. For best results
call the script from <tt class="docutils literal">dfhack.init</tt>.</p>
</div>
<div class="section" id="multicmd">
<h2><a class="toc-backref" href="#id152">multicmd</a></h2>
<h2><a class="toc-backref" href="#id153">multicmd</a></h2>
<p>Run multiple dfhack commands. The argument is split around the
character ; and all parts are run sequencially as independent
dfhack commands. Useful for hotkeys.</p>
@ -3227,7 +3232,7 @@ dfhack commands. Useful for hotkeys.</p>
</div>
</div>
<div class="section" id="in-game-interface-tools">
<h1><a class="toc-backref" href="#id153">In-game interface tools</a></h1>
<h1><a class="toc-backref" href="#id154">In-game interface tools</a></h1>
<p>These tools work by displaying dialogs or overlays in the game window, and
are mostly implemented by lua scripts.</p>
<div class="note">
@ -3242,7 +3247,7 @@ guideline because it arguably just fixes small usability bugs in the game UI.</p
you must enable the plugins which provide them.</p>
</div>
<div class="section" id="dwarf-manipulator">
<h2><a class="toc-backref" href="#id154">Dwarf Manipulator</a></h2>
<h2><a class="toc-backref" href="#id155">Dwarf Manipulator</a></h2>
<p>Implemented by the 'manipulator' plugin.</p>
<p>To activate, open the unit screen and press 'l'.</p>
<img alt="images/manipulator.png" src="images/manipulator.png" />
@ -3281,7 +3286,7 @@ cursor onto that cell instead of toggling it.</li>
directly to the main dwarf mode screen.</p>
</div>
<div class="section" id="search">
<h2><a class="toc-backref" href="#id155">Search</a></h2>
<h2><a class="toc-backref" href="#id156">Search</a></h2>
<p>Implemented by the 'search' plugin.</p>
<p>The search plugin adds search to the Stocks, Animals, Trading, Stockpile,
Noble (assignment candidates), Military (position candidates), Burrows
@ -3312,7 +3317,7 @@ only fat or tallow by forbidding fats, then searching for fat/tallow, and
using Permit Fats again while the list is filtered.</p>
</div>
<div class="section" id="automaterial">
<h2><a class="toc-backref" href="#id156">AutoMaterial</a></h2>
<h2><a class="toc-backref" href="#id157">AutoMaterial</a></h2>
<p>Implemented by the 'automaterial' plugin.</p>
<p>This makes building constructions (walls, floors, fortifications, etc) a little bit
easier by saving you from having to trawl through long lists of materials each time
@ -3340,7 +3345,7 @@ materials, it returns you back to this screen. If you use this along with severa
enabled materials, you should be able to place complex constructions more conveniently.</p>
</div>
<div class="section" id="gui-liquids">
<h2><a class="toc-backref" href="#id157">gui/liquids</a></h2>
<h2><a class="toc-backref" href="#id158">gui/liquids</a></h2>
<p>To use, bind to a key (the example config uses Alt-L) and activate in the 'k' mode.</p>
<img alt="images/liquids.png" src="images/liquids.png" />
<p>This script is a gui front-end to the liquids plugin and works similar to it,
@ -3360,7 +3365,7 @@ rivers power water wheels even when full and technically not flowing.</p>
<p>After setting up the desired operations using the described keys, use <tt class="docutils literal">Enter</tt> to apply them.</p>
</div>
<div class="section" id="gui-mechanisms">
<h2><a class="toc-backref" href="#id158">gui/mechanisms</a></h2>
<h2><a class="toc-backref" href="#id159">gui/mechanisms</a></h2>
<p>To use, bind to a key (the example config uses Ctrl-M) and activate in the 'q' mode.</p>
<img alt="images/mechanisms.png" src="images/mechanisms.png" />
<p>Lists mechanisms connected to the building, and their links. Navigating the list centers
@ -3370,7 +3375,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter
re-entering the mechanisms ui.</p>
</div>
<div class="section" id="gui-rename">
<h2><a class="toc-backref" href="#id159">gui/rename</a></h2>
<h2><a class="toc-backref" href="#id160">gui/rename</a></h2>
<p>Backed by the rename plugin, this script allows entering the desired name
via a simple dialog in the game ui.</p>
<ul>
@ -3393,7 +3398,7 @@ their species string.</p>
unit profession change to Ctrl-Shift-T.</p>
</div>
<div class="section" id="gui-room-list">
<h2><a class="toc-backref" href="#id160">gui/room-list</a></h2>
<h2><a class="toc-backref" href="#id161">gui/room-list</a></h2>
<p>To use, bind to a key (the example config uses Alt-R) and activate in the 'q' mode,
either immediately or after opening the assign owner page.</p>
<img alt="images/room-list.png" src="images/room-list.png" />
@ -3401,7 +3406,7 @@ either immediately or after opening the assign owner page.</p>
list, and allows unassigning them.</p>
</div>
<div class="section" id="gui-choose-weapons">
<h2><a class="toc-backref" href="#id161">gui/choose-weapons</a></h2>
<h2><a class="toc-backref" href="#id162">gui/choose-weapons</a></h2>
<p>Bind to a key (the example config uses Ctrl-W), and activate in the Equip-&gt;View/Customize
page of the military screen.</p>
<p>Depending on the cursor location, it rewrites all 'individual choice weapon' entries
@ -3412,14 +3417,14 @@ only that entry, and does it even if it is not 'individual choice'.</p>
and may lead to inappropriate weapons being selected.</p>
</div>
<div class="section" id="gui-clone-uniform">
<h2><a class="toc-backref" href="#id162">gui/clone-uniform</a></h2>
<h2><a class="toc-backref" href="#id163">gui/clone-uniform</a></h2>
<p>Bind to a key (the example config uses Ctrl-C), and activate in the Uniforms
page of the military screen with the cursor in the leftmost list.</p>
<p>When invoked, the script duplicates the currently selected uniform template,
and selects the newly created copy.</p>
</div>
<div class="section" id="gui-guide-path">
<h2><a class="toc-backref" href="#id163">gui/guide-path</a></h2>
<h2><a class="toc-backref" href="#id164">gui/guide-path</a></h2>
<p>Bind to a key (the example config uses Alt-P), and activate in the Hauling menu with
the cursor over a Guide order.</p>
<img alt="images/guide-path.png" src="images/guide-path.png" />
@ -3427,7 +3432,7 @@ the cursor over a Guide order.</p>
computes it when the order is executed for the first time.</p>
</div>
<div class="section" id="gui-workshop-job">
<h2><a class="toc-backref" href="#id164">gui/workshop-job</a></h2>
<h2><a class="toc-backref" href="#id165">gui/workshop-job</a></h2>
<p>Bind to a key (the example config uses Alt-A), and activate with a job selected in
a workshop in the 'q' mode.</p>
<img alt="images/workshop-job.png" src="images/workshop-job.png" />
@ -3463,7 +3468,7 @@ and then try to change the input item type, now it won't let you select <em>plan
you have to unset the material first.</p>
</div>
<div class="section" id="gui-workflow">
<h2><a class="toc-backref" href="#id165">gui/workflow</a></h2>
<h2><a class="toc-backref" href="#id166">gui/workflow</a></h2>
<p>Bind to a key (the example config uses Alt-W), and activate with a job selected
in a workshop in the 'q' mode.</p>
<img alt="images/workflow.png" src="images/workflow.png" />
@ -3510,7 +3515,7 @@ the current stock value. The bright green dashed line is the target
limit (maximum) and the dark green line is that minus the gap (minimum).</p>
</div>
<div class="section" id="gui-assign-rack">
<h2><a class="toc-backref" href="#id166">gui/assign-rack</a></h2>
<h2><a class="toc-backref" href="#id167">gui/assign-rack</a></h2>
<p>Bind to a key (the example config uses P), and activate when viewing a weapon
rack in the 'q' mode.</p>
<img alt="images/assign-rack.png" src="images/assign-rack.png" />
@ -3534,7 +3539,7 @@ the intended user. In order to aid in the choice, it shows the number
of currently assigned racks for every valid squad.</p>
</div>
<div class="section" id="gui-advfort">
<h2><a class="toc-backref" href="#id167">gui/advfort</a></h2>
<h2><a class="toc-backref" href="#id168">gui/advfort</a></h2>
<p>This script allows to perform jobs in adventure mode. For more complete help
press '?' while script is running. It's most confortable to use this as a
keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments:</p>
@ -3553,7 +3558,7 @@ implies -a</li>
</div>
</div>
<div class="section" id="gui-companion-order">
<h2><a class="toc-backref" href="#id168">gui/companion-order</a></h2>
<h2><a class="toc-backref" href="#id169">gui/companion-order</a></h2>
<p>A script to issue orders for companions. Select companions with lower case chars, issue orders with upper
case. Must be in look or talk mode to issue command on tile.</p>
<img alt="images/companion-order.png" src="images/companion-order.png" />
@ -3569,7 +3574,7 @@ case. Must be in look or talk mode to issue command on tile.</p>
</ul>
</div>
<div class="section" id="gui-gm-editor">
<h2><a class="toc-backref" href="#id169">gui/gm-editor</a></h2>
<h2><a class="toc-backref" href="#id170">gui/gm-editor</a></h2>
<p>There are three ways to open this editor:</p>
<ul class="simple">
<li>using gui/gm-editor command/keybinding - opens editor on what is selected
@ -3584,14 +3589,14 @@ the same as version above.</li>
in-game help.</p>
</div>
<div class="section" id="gui-mod-manager">
<h2><a class="toc-backref" href="#id170">gui/mod-manager</a></h2>
<h2><a class="toc-backref" href="#id171">gui/mod-manager</a></h2>
<p>A way to simply install and remove small mods. It looks for specially formated mods in
df subfolder 'mods'. Mods are not included, for example mods see: <a class="reference external" href="https://github.com/warmist/df-mini-mods">github mini mod repository</a></p>
<img alt="images/mod-manager.png" src="images/mod-manager.png" />
</div>
</div>
<div class="section" id="behavior-mods">
<h1><a class="toc-backref" href="#id171">Behavior Mods</a></h1>
<h1><a class="toc-backref" href="#id172">Behavior Mods</a></h1>
<p>These plugins, when activated via configuration UI or by detecting certain
structures in RAWs, modify the game engine behavior concerning the target
objects to add features not otherwise present.</p>
@ -3602,20 +3607,20 @@ technical challenge, and do not represent any long-term plans to produce more
similar modifications of the game.</p>
</div>
<div class="section" id="siege-engine">
<h2><a class="toc-backref" href="#id172">Siege Engine</a></h2>
<h2><a class="toc-backref" href="#id173">Siege Engine</a></h2>
<p>The siege-engine plugin enables siege engines to be linked to stockpiles, and
aimed at an arbitrary rectangular area across Z levels, instead of the original
four directions. Also, catapults can be ordered to load arbitrary objects, not
just stones.</p>
<div class="section" id="rationale">
<h3><a class="toc-backref" href="#id173">Rationale</a></h3>
<h3><a class="toc-backref" href="#id174">Rationale</a></h3>
<p>Siege engines are a very interesting feature, but sadly almost useless in the current state
because they haven't been updated since 2D and can only aim in four directions. This is an
attempt to bring them more up to date until Toady has time to work on it. Actual improvements,
e.g. like making siegers bring their own, are something only Toady can do.</p>
</div>
<div class="section" id="configuration-ui">
<h3><a class="toc-backref" href="#id174">Configuration UI</a></h3>
<h3><a class="toc-backref" href="#id175">Configuration UI</a></h3>
<p>The configuration front-end to the plugin is implemented by the gui/siege-engine
script. Bind it to a key (the example config uses Alt-A) and activate after selecting
a siege engine in 'q' mode.</p>
@ -3638,7 +3643,7 @@ menu.</p>
</div>
</div>
<div class="section" id="power-meter">
<h2><a class="toc-backref" href="#id175">Power Meter</a></h2>
<h2><a class="toc-backref" href="#id176">Power Meter</a></h2>
<p>The power-meter plugin implements a modified pressure plate that detects power being
supplied to gear boxes built in the four adjacent N/S/W/E tiles.</p>
<p>The configuration front-end is implemented by the gui/power-meter script. Bind it to a
@ -3649,11 +3654,11 @@ in the build menu.</p>
configuration page, but configures parameters relevant to the modded power meter building.</p>
</div>
<div class="section" id="steam-engine">
<h2><a class="toc-backref" href="#id176">Steam Engine</a></h2>
<h2><a class="toc-backref" href="#id177">Steam Engine</a></h2>
<p>The steam-engine plugin detects custom workshops with STEAM_ENGINE in
their token, and turns them into real steam engines.</p>
<div class="section" id="id1">
<h3><a class="toc-backref" href="#id177">Rationale</a></h3>
<h3><a class="toc-backref" href="#id178">Rationale</a></h3>
<p>The vanilla game contains only water wheels and windmills as sources of
power, but windmills give relatively little power, and water wheels require
flowing water, which must either be a real river and thus immovable and
@ -3664,7 +3669,7 @@ it can be done just by combining existing features of the game engine
in a new way with some glue code and a bit of custom logic.</p>
</div>
<div class="section" id="construction">
<h3><a class="toc-backref" href="#id178">Construction</a></h3>
<h3><a class="toc-backref" href="#id179">Construction</a></h3>
<p>The workshop needs water as its input, which it takes via a
passable floor tile below it, like usual magma workshops do.
The magma version also needs magma.</p>
@ -3688,7 +3693,7 @@ short axles that can be built later than both of the engines.</p>
</div>
</div>
<div class="section" id="operation">
<h3><a class="toc-backref" href="#id179">Operation</a></h3>
<h3><a class="toc-backref" href="#id180">Operation</a></h3>
<p>In order to operate the engine, queue the Stoke Boiler job (optionally
on repeat). A furnace operator will come, possibly bringing a bar of fuel,
and perform it. As a result, a &quot;boiling water&quot; item will appear
@ -3719,7 +3724,7 @@ decrease it by further 4%, and also decrease the whole steam
use rate by 10%.</p>
</div>
<div class="section" id="explosions">
<h3><a class="toc-backref" href="#id180">Explosions</a></h3>
<h3><a class="toc-backref" href="#id181">Explosions</a></h3>
<p>The engine must be constructed using barrel, pipe and piston
from fire-safe, or in the magma version magma-safe metals.</p>
<p>During operation weak parts get gradually worn out, and
@ -3728,7 +3733,7 @@ toppled during operation by a building destroyer, or a
tantruming dwarf.</p>
</div>
<div class="section" id="save-files">
<h3><a class="toc-backref" href="#id181">Save files</a></h3>
<h3><a class="toc-backref" href="#id182">Save files</a></h3>
<p>It should be safe to load and view engine-using fortresses
from a DF version without DFHack installed, except that in such
case the engines won't work. However actually making modifications
@ -3739,7 +3744,7 @@ being generated.</p>
</div>
</div>
<div class="section" id="add-spatter">
<h2><a class="toc-backref" href="#id182">Add Spatter</a></h2>
<h2><a class="toc-backref" href="#id183">Add Spatter</a></h2>
<p>This plugin makes reactions with names starting with <tt class="docutils literal">SPATTER_ADD_</tt>
produce contaminants on the items instead of improvements. The produced
contaminants are immune to being washed away by water or destroyed by

@ -2335,6 +2335,10 @@ alternatively pass cage IDs as arguments::
stripcaged weapons 25321 34228
undump-buildings
================
Undesignates building base materials for dumping.
create-items
============
Spawn arbitrary items under the cursor.

@ -1337,6 +1337,7 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,checkDesignationsNow),
WRAPM(Job,isSuitableItem),
WRAPM(Job,isSuitableMaterial),
WRAPM(Job,getName),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
@ -1475,6 +1476,8 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, isCasteMaterial),
WRAPM(Items, getSubtypeCount),
WRAPM(Items, getSubtypeDef),
WRAPM(Items, getItemBaseValue),
WRAPM(Items, getValue),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToBuilding, items_moveToBuilding),

@ -0,0 +1 @@
friend struct df::interfacest;

@ -173,5 +173,11 @@ DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat
/// Detaches the items from its current location and turns it into a projectile
DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item *item);
/// Gets value of base-quality item with specified type and material
DFHACK_EXPORT int getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype);
/// Gets the value of a specific item, ignoring civ values and trade agreements
DFHACK_EXPORT int getValue(df::item *item);
}
}

@ -81,6 +81,7 @@ namespace DFHack
DFHACK_EXPORT bool isSuitableItem(df::job_item *item, df::item_type itype, int isubtype);
DFHACK_EXPORT bool isSuitableMaterial(df::job_item *item, int mat_type, int mat_index);
DFHACK_EXPORT std::string getName(df::job *job);
}
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);

@ -30,6 +30,7 @@
using namespace std;
using namespace DFHack;
using namespace EventManager;
using namespace df::enums;
/*
* TODO:
@ -322,12 +323,8 @@ static void manageJobInitiatedEvent(color_ostream& out) {
//helper function for manageJobCompletedEvent
static int32_t getWorkerID(df::job* job) {
for ( size_t a = 0; a < job->general_refs.size(); a++ ) {
if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER )
continue;
return ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id;
}
return -1;
auto ref = findRef(job->general_refs, general_ref_type::UNIT_WORKER);
return ref ? ref->getID() : -1;
}
/*

@ -534,7 +534,7 @@ bool Items::setOwner(df::item *item, df::unit *unit)
{
df::general_ref *ref = item->general_refs[i];
if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
if (ref->getType() != general_ref_type::UNIT_ITEMOWNER)
continue;
if (auto cur = ref->getUnit())
@ -997,16 +997,22 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item)
if (!ref)
return NULL;
auto proj = df::allocate<df::proj_itemst>();
if (!proj) {
delete ref;
return NULL;
}
if (!detachItem(mc, item))
{
delete ref;
delete proj;
return NULL;
}
item->pos = pos;
item->flags.bits.in_job = true;
auto proj = new df::proj_itemst();
proj->link = new df::proj_list_link();
proj->link->item = proj;
proj->id = (*proj_next_id)++;
@ -1022,3 +1028,303 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item)
return proj;
}
int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype)
{
int value = 0;
switch (item_type)
{
case item_type::BAR:
case item_type::SMALLGEM:
case item_type::BLOCKS:
case item_type::SKIN_TANNED:
value = 5;
break;
case item_type::ROUGH:
case item_type::BOULDER:
case item_type::WOOD:
value = 3;
break;
case item_type::DOOR:
case item_type::FLOODGATE:
case item_type::BED:
case item_type::CHAIR:
case item_type::CHAIN:
case item_type::FLASK:
case item_type::GOBLET:
case item_type::INSTRUMENT:
case item_type::TOY:
case item_type::CAGE:
case item_type::BARREL:
case item_type::BUCKET:
case item_type::ANIMALTRAP:
case item_type::TABLE:
case item_type::COFFIN:
case item_type::BOX:
case item_type::BIN:
case item_type::ARMORSTAND:
case item_type::WEAPONRACK:
case item_type::CABINET:
case item_type::FIGURINE:
case item_type::AMULET:
case item_type::SCEPTER:
case item_type::CROWN:
case item_type::RING:
case item_type::EARRING:
case item_type::BRACELET:
case item_type::GEM:
case item_type::ANVIL:
case item_type::TOTEM:
case item_type::BACKPACK:
case item_type::QUIVER:
case item_type::BALLISTAARROWHEAD:
case item_type::PIPE_SECTION:
case item_type::HATCH_COVER:
case item_type::GRATE:
case item_type::QUERN:
case item_type::MILLSTONE:
case item_type::SPLINT:
case item_type::CRUTCH:
case item_type::SLAB:
case item_type::BOOK:
value = 10;
break;
case item_type::WINDOW:
case item_type::STATUE:
value = 25;
break;
case item_type::CORPSE:
case item_type::CORPSEPIECE:
case item_type::REMAINS:
return 0;
case item_type::WEAPON:
if (size_t(item_subtype) < world->raws.itemdefs.weapons.size())
value = world->raws.itemdefs.weapons[item_subtype]->value;
else
value = 10;
break;
case item_type::ARMOR:
if (size_t(item_subtype) < world->raws.itemdefs.armor.size())
value = world->raws.itemdefs.armor[item_subtype]->value;
else
value = 10;
break;
case item_type::SHOES:
if (size_t(item_subtype) < world->raws.itemdefs.shoes.size())
value = world->raws.itemdefs.shoes[item_subtype]->value;
else
value = 5;
break;
case item_type::SHIELD:
if (size_t(item_subtype) < world->raws.itemdefs.shields.size())
value = world->raws.itemdefs.shields[item_subtype]->value;
else
value = 10;
break;
case item_type::HELM:
if (size_t(item_subtype) < world->raws.itemdefs.helms.size())
value = world->raws.itemdefs.helms[item_subtype]->value;
else
value = 10;
break;
case item_type::GLOVES:
if (size_t(item_subtype) < world->raws.itemdefs.gloves.size())
value = world->raws.itemdefs.gloves[item_subtype]->value;
else
value = 5;
break;
case item_type::AMMO:
if (size_t(item_subtype) < world->raws.itemdefs.ammo.size())
value = world->raws.itemdefs.ammo[item_subtype]->value;
else
value = 1;
break;
case item_type::MEAT:
case item_type::PLANT:
case item_type::LEAVES:
case item_type::CHEESE:
value = 2;
break;
case item_type::FISH:
case item_type::FISH_RAW:
case item_type::EGG:
value = 2;
if (size_t(mat_type) < world->raws.creatures.all.size())
{
auto creature = world->raws.creatures.all[mat_type];
if (size_t(mat_subtype) < creature->caste.size())
{
auto caste = creature->caste[mat_subtype];
mat_type = caste->misc.bone_mat;
mat_subtype = caste->misc.bone_matidx;
}
}
break;
case item_type::VERMIN:
value = 0;
if (size_t(mat_type) < world->raws.creatures.all.size())
{
auto creature = world->raws.creatures.all[mat_type];
if (size_t(mat_subtype) < creature->caste.size())
value = creature->caste[mat_subtype]->misc.petvalue;
}
value /= 2;
if (!value)
return 1;
return value;
case item_type::PET:
if (size_t(mat_type) < world->raws.creatures.all.size())
{
auto creature = world->raws.creatures.all[mat_type];
if (size_t(mat_subtype) < creature->caste.size())
return creature->caste[mat_subtype]->misc.petvalue;
}
return 0;
case item_type::SEEDS:
case item_type::DRINK:
case item_type::POWDER_MISC:
case item_type::LIQUID_MISC:
case item_type::COIN:
case item_type::GLOB:
case item_type::ORTHOPEDIC_CAST:
value = 1;
break;
case item_type::THREAD:
value = 6;
break;
case item_type::CLOTH:
value = 7;
break;
case item_type::PANTS:
if (size_t(item_subtype) < world->raws.itemdefs.pants.size())
value = world->raws.itemdefs.pants[item_subtype]->value;
else
value = 10;
break;
case item_type::CATAPULTPARTS:
case item_type::BALLISTAPARTS:
case item_type::TRAPPARTS:
value = 30;
break;
case item_type::SIEGEAMMO:
case item_type::TRACTION_BENCH:
value = 20;
break;
case item_type::TRAPCOMP:
if (size_t(item_subtype) < world->raws.itemdefs.trapcomps.size())
value = world->raws.itemdefs.trapcomps[item_subtype]->value;
else
value = 10;
break;
case item_type::FOOD:
return 10;
// case item_type::ROCK:
default:
return 0;
case item_type::TOOL:
if (size_t(item_subtype) < world->raws.itemdefs.tools.size())
value = world->raws.itemdefs.tools[item_subtype]->value;
else
value = 10;
break;
}
MaterialInfo mat;
if (mat.decode(mat_type, mat_subtype))
value *= mat.material->material_value;
return value;
}
int Items::getValue(df::item *item)
{
CHECK_NULL_POINTER(item);
int16_t item_type = item->getType();
int16_t item_subtype = item->getSubtype();
int16_t mat_type = item->getMaterial();
int32_t mat_subtype = item->getMaterialIndex();
// Get base value for item type, subtype, and material
int value = getItemBaseValue(item_type, item_subtype, mat_type, mat_subtype);
// Ignore entity value modifications
// Improve value based on quality
int quality = item->getQuality();
value *= (quality + 1);
if (quality == 5)
value *= 2;
// Add improvement values
int impValue = item->getThreadDyeValue(NULL) + item->getImprovementsValue(NULL);
if (item_type == item_type::AMMO) // Ammo improvements are worth less
impValue /= 30;
value += impValue;
// Degrade value due to wear
switch (item->getWear())
{
case 1:
value = value * 3 / 4;
break;
case 2:
value = value / 2;
break;
case 3:
value = value / 4;
break;
}
// Ignore value bonuses from magic, since that never actually happens
// Artifacts have 10x value
if (item->flags.bits.artifact_mood)
value *= 10;
// Boost value from stack size
value *= item->getStackSize();
// ...but not for coins
if (item_type == item_type::COIN)
{
value /= 500;
if (!value)
value = 1;
}
// Handle vermin swarms
if (item_type == item_type::VERMIN || item_type == item_type::PET)
{
int divisor = 1;
auto creature = vector_get(world->raws.creatures.all, mat_type);
if (creature && size_t(mat_subtype) < creature->caste.size())
divisor = creature->caste[mat_subtype]->misc.petvalue_divisor;
if (divisor > 1)
value /= divisor;
}
return value;
}

@ -53,6 +53,7 @@ using namespace std;
#include "df/general_ref.h"
#include "df/general_ref_unit_workerst.h"
#include "df/general_ref_building_holderst.h"
#include "df/interface_button_building_new_jobst.h"
using namespace DFHack;
using namespace df::enums;
@ -92,7 +93,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepEverything)
pnew->job_items[a] = new df::job_item(*pnew->job_items[a]);
for ( size_t a = 0; a < job->general_refs.size(); a++ )
if ( keepEverything || job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER )
if ( keepEverything || job->general_refs[a]->getType() != general_ref_type::UNIT_WORKER )
pnew->general_refs.push_back(job->general_refs[a]->clone());
return pnew;
@ -264,28 +265,18 @@ df::building *DFHack::Job::getHolder(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->general_refs.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]);
if (ref)
return ref->getBuilding();
}
auto ref = getGeneralRef(job, general_ref_type::BUILDING_HOLDER);
return NULL;
return ref ? ref->getBuilding() : NULL;
}
df::unit *DFHack::Job::getWorker(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->general_refs.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
if (ref)
return ref->getUnit();
}
auto ref = getGeneralRef(job, general_ref_type::UNIT_WORKER);
return NULL;
return ref ? ref->getUnit() : NULL;
}
void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int cooldown)
@ -325,8 +316,8 @@ bool DFHack::Job::removeWorker(df::job *job, int cooldown)
for (size_t i = 0; i < job->general_refs.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
if (!ref)
df::general_ref *ref = job->general_refs[i];
if (ref->getType() != general_ref_type::UNIT_WORKER)
continue;
auto worker = ref->getUnit();
@ -476,3 +467,25 @@ bool Job::isSuitableMaterial(df::job_item *item, int mat_type, int mat_index)
return minfo.isValid() && iinfo.matches(*item, &minfo);
}
std::string Job::getName(df::job *job)
{
CHECK_NULL_POINTER(job);
std::string desc;
auto button = df::allocate<df::interface_button_building_new_jobst>();
button->reaction_name = job->reaction_name;
button->hist_figure_id = job->hist_figure_id;
button->job_type = job->job_type;
button->item_type = job->item_type;
button->item_subtype = job->item_subtype;
button->mat_type = job->mat_type;
button->mat_index = job->mat_index;
button->item_category = job->item_category;
button->material_category = job->material_category;
button->getLabel(&desc);
delete button;
return desc;
}

@ -584,8 +584,8 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
if ( dx == 0 && dy == 0 ) {
//check for forbidden hatches and floors and such
df::enums::tile_building_occ::tile_building_occ upOcc = index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building;
if ( upOcc == df::enums::tile_building_occ::Impassable || upOcc == df::enums::tile_building_occ::Obstacle || upOcc == df::enums::tile_building_occ::Floored )
df::tile_building_occ upOcc = index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building;
if ( upOcc == tile_building_occ::Impassable || upOcc == tile_building_occ::Obstacle || upOcc == tile_building_occ::Floored )
return false;
if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == shape1 )
@ -617,7 +617,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
return false; //unusable ramp
//there has to be an unforbidden hatch above the ramp
if ( index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building != df::enums::tile_building_occ::Dynamic )
if ( index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic )
return false;
//note that forbidden hatches have Floored occupancy. unforbidden ones have dynamic occupancy
df::building* building = Buildings::findAtTile(pos2);
@ -625,7 +625,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
out << __FILE__ << ", line " << __LINE__ << ": couldn't find hatch.\n";
return false;
}
if ( building->getType() != df::enums::building_type::Hatch ) {
if ( building->getType() != building_type::Hatch ) {
return false;
}
return true;
@ -661,8 +661,8 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
if ( !blockUp )
return false;
df::enums::tile_building_occ::tile_building_occ occupancy = index_tile<df::tile_occupancy>(blockUp->occupancy,up).bits.building;
if ( occupancy == df::enums::tile_building_occ::Obstacle || occupancy == df::enums::tile_building_occ::Floored || occupancy == df::enums::tile_building_occ::Impassable )
df::tile_building_occ occupancy = index_tile<df::tile_occupancy>(blockUp->occupancy,up).bits.building;
if ( occupancy == tile_building_occ::Obstacle || occupancy == tile_building_occ::Floored || occupancy == tile_building_occ::Impassable )
return false;
return true;
}

@ -1 +1 @@
Subproject commit 42736fe494e7edeb884caa45a8669996a0eafb11
Subproject commit 499507eac5bdd3abc70e204497248627327cf8de

@ -164,6 +164,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(3dveins 3dveins.cpp)
DFHACK_PLUGIN(strangemood strangemood.cpp)
DFHACK_PLUGIN(command-prompt command-prompt.cpp)
DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
endif()
# this is the skeleton plugin. If you want to make your own, make a copy and then change it

@ -0,0 +1,389 @@
//most of the code is shamelessly stolen from steam-engine.cpp
#include "Core.h"
#include "Error.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include "LuaTools.h"
#include <VTableInterpose.h>
#include "MiscUtils.h"
#include "df/building_doorst.h"
#include "df/building_workshopst.h"
#include "df/machine.h"
#include "df/machine_tile_set.h"
#include "df/power_info.h"
#include "df/world.h"
#include "df/buildings_other_id.h"
#include "df/coord.h"
#include "df/tile_building_occ.h"
#include "df/building_drawbuffer.h"
#include <map>
using namespace DFHack;
using namespace df::enums;
using df::global::world;
DFHACK_PLUGIN("building-hacks");
struct graphic_tile //could do just 31x31 and be done, but it's nicer to have flexible imho.
{
int16_t tile; //originally uint8_t but we need to indicate non-animated tiles
int8_t fore;
int8_t back;
int8_t bright;
};
struct workshop_hack_data
{
int32_t myType;
bool impassible_fix;
//machine stuff
df::machine_tile_set connections;
df::power_info powerInfo;
//animation
std::vector<std::vector<graphic_tile> > frames;
bool machine_timing; //6 frames used in vanilla
int frame_skip; // e.g. 2 means have to ticks between frames
//updateCallback:
int skip_updates;
};
typedef std::map<int32_t,workshop_hack_data> workshops_data_t;
workshops_data_t hacked_workshops;
static void handle_update_action(color_ostream &out,df::building_workshopst*){};
DEFINE_LUA_EVENT_1(onUpdateAction,handle_update_action,df::building_workshopst*);
DFHACK_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onUpdateAction),
DFHACK_LUA_END
};
struct work_hook : df::building_workshopst{
typedef df::building_workshopst interpose_base;
workshop_hack_data* find_def()
{
if (type == workshop_type::Custom)
{
auto it=hacked_workshops.find(this->getCustomType());
if(it!=hacked_workshops.end())
return &(it->second);
}
return NULL;
}
inline bool is_fully_built()
{
return getBuildStage() >= getMaxBuildStage();
}
DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,())
{
if(auto def = find_def())
{
if(def->impassible_fix)
return tile_building_occ::Impassable;
}
return INTERPOSE_NEXT(getImpassableOccupancy)();
}
DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info))
{
if (auto def = find_def())
{
info->produced = def->powerInfo.produced;
info->consumed = def->powerInfo.consumed;
return;
}
INTERPOSE_NEXT(getPowerInfo)(info);
}
DEFINE_VMETHOD_INTERPOSE(df::machine_info*, getMachineInfo, ())
{
if (find_def())
return &machine;
return INTERPOSE_NEXT(getMachineInfo)();
}
DEFINE_VMETHOD_INTERPOSE(bool, isPowerSource, ())
{
workshop_hack_data* def=find_def();
if (def && def->powerInfo.produced>0)
return true;
return INTERPOSE_NEXT(isPowerSource)();
}
DEFINE_VMETHOD_INTERPOSE(void, categorize, (bool free))
{
if (find_def())
{
auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE];
insert_into_vector(vec, &df::building::id, (df::building*)this);
}
INTERPOSE_NEXT(categorize)(free);
}
DEFINE_VMETHOD_INTERPOSE(void, uncategorize, ())
{
if (find_def())
{
auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE];
erase_from_vector(vec, &df::building::id, id);
}
INTERPOSE_NEXT(uncategorize)();
}
DEFINE_VMETHOD_INTERPOSE(bool, canConnectToMachine, (df::machine_tile_set *info))
{
if (auto def = find_def())
{
int real_cx = centerx, real_cy = centery;
bool ok = false;
for (size_t i = 0; i < def->connections.tiles.size(); i++)
{
// the original function connects to the center tile
centerx = x1 + def->connections.tiles[i].x;
centery = y1 + def->connections.tiles[i].y;
if (!INTERPOSE_NEXT(canConnectToMachine)(info))
continue;
ok = true;
break;
}
centerx = real_cx; centery = real_cy;
return ok;
}
else
return INTERPOSE_NEXT(canConnectToMachine)(info);
}
DEFINE_VMETHOD_INTERPOSE(bool, isUnpowered, ())
{
if (auto def = find_def())
{
if(def->powerInfo.consumed==0)
return false;
if(machine.machine_id==-1)
return true;
df::machine* target_machine=df::machine::find(machine.machine_id);
if(target_machine && target_machine->flags.bits.active)
return false;
return true;
}
return INTERPOSE_NEXT(isUnpowered)();
}
DEFINE_VMETHOD_INTERPOSE(void, updateAction, ())
{
if(auto def = find_def())
{
if(def->skip_updates!=0 && is_fully_built())
{
df::world* world = df::global::world;
if(world->frame_counter % def->skip_updates == 0)
{
CoreSuspendClaimer suspend;
color_ostream_proxy out(Core::getInstance().getConsole());
onUpdateAction(out,this);
}
}
}
INTERPOSE_NEXT(updateAction)();
}
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk))
{
INTERPOSE_NEXT(drawBuilding)(db, unk);
if (auto def = find_def())
{
if (!is_fully_built() || def->frames.size()==0)
return;
int frame=0;
if(!def->machine_timing)
{
int frame_mod=def->frames.size()* def->frame_skip;
df::world* world = df::global::world;
frame=(world->frame_counter % frame_mod)/def->frame_skip;
}
else
{
if(machine.machine_id!=-1)
{
df::machine* target_machine=df::machine::find(machine.machine_id);
if(target_machine)
{
frame=target_machine->visual_phase % def->frames.size();
}
}
}
int w=db->x2-db->x1+1;
std::vector<graphic_tile> &cur_frame=def->frames[frame];
for(int i=0;i<cur_frame.size();i++)
{
if(cur_frame[i].tile>=0)
{
int tx=i % w;
int ty=i / w;
db->tile[tx][ty]=cur_frame[i].tile;
db->back[tx][ty]=cur_frame[i].back;
db->bright[tx][ty]=cur_frame[i].bright;
db->fore[tx][ty]=cur_frame[i].fore;
}
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getImpassableOccupancy);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getPowerInfo);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getMachineInfo);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isPowerSource);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, categorize);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, uncategorize);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canConnectToMachine);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isUnpowered);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction);
IMPLEMENT_VMETHOD_INTERPOSE(work_hook, drawBuilding);
void clear_mapping()
{
hacked_workshops.clear();
}
static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos)
{
luaL_checktype(L,stack_pos,LUA_TTABLE);
lua_pushvalue(L,stack_pos);
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
luaL_checktype(L,-1,LUA_TTABLE);
lua_pushnil(L);
std::vector<graphic_tile> frame;
while (lua_next(L, -2) != 0) {
graphic_tile t;
lua_pushnumber(L,1);
lua_gettable(L,-2);
if(lua_isnil(L,-1))
{
t.tile=-1;
lua_pop(L,1);
}
else
{
t.tile=lua_tonumber(L,-1);
lua_pop(L,1);
lua_pushnumber(L,2);
lua_gettable(L,-2);
t.fore=lua_tonumber(L,-1);
lua_pop(L,1);
lua_pushnumber(L,3);
lua_gettable(L,-2);
t.back=lua_tonumber(L,-1);
lua_pop(L,1);
lua_pushnumber(L,4);
lua_gettable(L,-2);
t.bright=lua_tonumber(L,-1);
lua_pop(L,1);
}
frame.push_back(t);
lua_pop(L,1);
}
lua_pop(L,1);
def.frames.push_back(frame);
}
lua_pop(L,1);
return ;
}
//arguments: custom type,impassible fix (bool), consumed power, produced power, list of connection points, update skip(0/nil to disable)
// table of frames,frame to tick ratio (-1 for machine control)
static int addBuilding(lua_State* L)
{
workshop_hack_data newDefinition;
newDefinition.myType=luaL_checkint(L,1);
newDefinition.impassible_fix=luaL_checkint(L,2);
newDefinition.powerInfo.consumed=luaL_checkint(L,3);
newDefinition.powerInfo.produced=luaL_checkint(L,4);
//table of machine connection points
luaL_checktype(L,5,LUA_TTABLE);
lua_pushvalue(L,5);
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
lua_getfield(L,-1,"x");
int x=lua_tonumber(L,-1);
lua_pop(L,1);
lua_getfield(L,-1,"y");
int y=lua_tonumber(L,-1);
lua_pop(L,1);
newDefinition.connections.can_connect.push_back(-1);//TODO add this too...
newDefinition.connections.tiles.push_back(df::coord(x,y,0));
lua_pop(L,1);
}
lua_pop(L,1);
//updates
newDefinition.skip_updates=luaL_optinteger(L,6,0);
//animation
if(!lua_isnil(L,7))
{
loadFrames(L,newDefinition,7);
newDefinition.frame_skip=luaL_optinteger(L,8,-1);
if(newDefinition.frame_skip==0)
newDefinition.frame_skip=1;
if(newDefinition.frame_skip<0)
newDefinition.machine_timing=true;
else
newDefinition.machine_timing=false;
}
hacked_workshops[newDefinition.myType]=newDefinition;
return 0;
}
DFHACK_PLUGIN_LUA_COMMANDS{
DFHACK_LUA_COMMAND(addBuilding),
DFHACK_LUA_END
};
static void enable_hooks(bool enable)
{
INTERPOSE_HOOK(work_hook,getImpassableOccupancy).apply(enable);
//machine part
INTERPOSE_HOOK(work_hook,getPowerInfo).apply(enable);
INTERPOSE_HOOK(work_hook,getMachineInfo).apply(enable);
INTERPOSE_HOOK(work_hook,isPowerSource).apply(enable);
INTERPOSE_HOOK(work_hook,categorize).apply(enable);
INTERPOSE_HOOK(work_hook,uncategorize).apply(enable);
INTERPOSE_HOOK(work_hook,canConnectToMachine).apply(enable);
INTERPOSE_HOOK(work_hook,isUnpowered).apply(enable);
//update n render
INTERPOSE_HOOK(work_hook,updateAction).apply(enable);
INTERPOSE_HOOK(work_hook,drawBuilding).apply(enable);
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_WORLD_LOADED:
enable_hooks(true);
break;
case SC_WORLD_UNLOADED:
enable_hooks(false);
clear_mapping();
break;
default:
break;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
enable_hooks(true);
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
plugin_onstatechange(out,SC_WORLD_UNLOADED);
return CR_OK;
}

@ -88,10 +88,10 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df:
df::job* job = new df::job;
job->job_type = df::enums::job_type::DestroyBuilding;
//job->flags.bits.special = 1;
df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst;
df::general_ref_building_holderst* buildingRef = df::allocate<df::general_ref_building_holderst>();
buildingRef->building_id = building->id;
job->general_refs.push_back(buildingRef);
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
df::general_ref_unit_workerst* workerRef = df::allocate<df::general_ref_unit_workerst>();
workerRef->unit_id = firstInvader->id;
job->general_refs.push_back(workerRef);
getRidOfOldJob(firstInvader);
@ -118,7 +118,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df:
if ( construction2 ) {
df::job* job = new df::job;
job->job_type = df::enums::job_type::RemoveConstruction;
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
df::general_ref_unit_workerst* workerRef = df::allocate<df::general_ref_unit_workerst>();
workerRef->unit_id = firstInvader->id;
job->general_refs.push_back(workerRef);
job->pos = pt2;
@ -189,7 +189,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df:
job->pos = workHere;
firstInvader->path.dest = goHere;
location = goHere;
df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst;
df::general_ref_unit_workerst* ref = df::allocate<df::general_ref_unit_workerst>();
ref->unit_id = firstInvader->id;
job->general_refs.push_back(ref);
firstInvader->job.hunt_target = NULL;

@ -0,0 +1,109 @@
local _ENV = mkmodule('plugins.building-hacks')
--[[
from native:
addBuilding(custom type,impassible fix (bool), consumed power, produced power, list of connection points,
update skip(0/nil to disable),table of frames,frame to tick ratio (-1 for machine control))
from here:
registerBuilding{
name -- custom workshop id e.g. SOAPMAKER << required!
fix_impassible -- make impassible tiles impassible to liquids too
consume -- how much machine power is needed to work
produce -- how much machine power is produced
gears -- a table or {x=?,y=?} of connection points for machines
action -- a table of number (how much ticks to skip) and a function which gets called on shop update
animate -- a table of
frames -- a table of
tables of 4 numbers (tile,fore,back,bright) OR
empty table (tile not modified) OR
{x=<number> y=<number> + 4 numbers like in first case} -- this generates full frame even, usefull for animations that change little (1-2 tiles)
frameLenght -- how many ticks does one frame take OR
isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning)
}
]]
_registeredStuff={}
local function unregall(state)
if state==SC_WORLD_UNLOADED then
onUpdateAction._library=nil
dfhack.onStateChange.building_hacks= nil
_registeredStuff={}
end
end
local function onUpdateLocal(workshop)
local f=_registeredStuff[workshop:getCustomType()]
if f then
f(workshop)
end
end
local function findCustomWorkshop(name)
local raws=df.global.world.raws.buildings.all
for k,v in ipairs(raws) do
if v.code==name then
return v
end
end
end
local function registerUpdateAction(shopId,callback)
_registeredStuff[shopId]=callback
onUpdateAction._library=onUpdateLocal
dfhack.onStateChange.building_hacks=unregall
end
local function generateFrame(tiles,w,h)
local mTiles={}
for k,v in ipairs(tiles) do
mTiles[v.x]=mTiles[v.x] or {}
mTiles[v.x][v.y]=v
end
local ret={}
for ty=0,h-1 do
for tx=0,w-1 do
if mTiles[tx] and mTiles[tx][ty] then
table.insert(ret,mTiles[tx][ty]) -- leaves x and y in but who cares
else
table.insert(ret,{})
end
end
end
return ret
end
local function processFrames(shop_def,frames)
local w,h=shop_def.dim_x,shop_def.dim_y
for frame_id,frame in ipairs(frames) do
if frame[1].x~=nil then
frames[frame_id]=generateFrame(frame,w,h)
end
end
return frames
end
function registerBuilding(args)
local shop_def=findCustomWorkshop(args.name)
local shop_id=shop_def.id
local fix_impassible
if args.fix_impassible then
fix_impassible=1
else
fix_impassible=0
end
local consume=args.consume or 0
local produce=args.produce or 0
local gears=args.gears or {}
local action=args.action --could be nil
local updateSkip=0
if action~=nil then
updateSkip=action[1]
registerUpdateAction(shop_id,action[2])
end
local animate=args.animate
local frameLength=1
local frames
if animate~=nil then
frameLength=animate.frameLength
if animate.isMechanical then
frameLength=-1
end
frames=processFrames(shop_def,animate.frames)
end
addBuilding(shop_id,fix_impassible,consume,produce,gears,updateSkip,frames,frameLength)
end
return _ENV

@ -25,13 +25,13 @@ local function getShopName(btype,bsubtype,bcustom)
if typenames_shop[bsubtype]~=nil then
return typenames_shop[bsubtype]
else
return nil --todo add custom (not very useful)
return df.building_def_workshopst.find(bcustom).code
end
elseif btype==df.building_type.Furnace then
if typenames_furnace[bsubtype]~=nil then
return typenames_furnace[bsubtype]
else
return nil --todo add custom (not very useful)
return df.building_def_furnacest.find(bcustom).code
end
end
end
@ -77,9 +77,19 @@ local function onPostSidebar(workshop)
wjob.choices_visible:insert("#",new_button)
end
end
if _registeredStuff.customSidebar and _registeredStuff.customSidebar[shop_id] then
_registeredStuff.customSidebar[shop_id](workshop)
end
end
end
local function customSidebarsCallback(workshop)
local shop_id=getShopName(workshop:getType(),workshop:getSubtype(),workshop:getCustomType())
if shop_id then
if _registeredStuff.customSidebar and _registeredStuff.customSidebar[shop_id] then
_registeredStuff.customSidebar[shop_id](workshop)
end
end
end
function registerReaction(reaction_name,callback)
_registeredStuff.reactionCallbacks=_registeredStuff.reactionCallbacks or {}
_registeredStuff.reactionCallbacks[reaction_name]=callback
@ -87,6 +97,13 @@ function registerReaction(reaction_name,callback)
dfhack.onStateChange.eventful=unregall
end
function registerSidebar(shop_name,callback)
_registeredStuff.customSidebar=_registeredStuff.customSidebar or {}
_registeredStuff.customSidebar[shop_name]=callback
onWorkshopFillSidebarMenu._library=customSidebarsCallback
dfhack.onStateChange.eventful=unregall
end
function removeNative(shop_name,name)
_registeredStuff.shopNonNative=_registeredStuff.shopNonNative or {}
local shops=_registeredStuff.shopNonNative

@ -928,7 +928,7 @@ function usetool:init(args)
wid.Label{
view_id="mainLabel",
frame = {xalign=0,yalign=0},
text={{key=keybinds.prevJob.key},{gap=1,text=dfhack.curry(usetool.getModeName,self)},{gap=1,key=keybinds.nextJob.key},
text={{key=keybinds.prevJob.key},{gap=1,text=self:callback("getModeName")},{gap=1,key=keybinds.nextJob.key},
}
},

@ -2,6 +2,7 @@
local gui = require 'gui'
local dialog = require 'gui.dialogs'
local widgets =require 'gui.widgets'
local guiScript = require 'gui.script'
local args={...}
local keybindings={
@ -10,7 +11,9 @@ local keybindings={
lua_set={key="CUSTOM_ALT_S",desc="Set by using a lua function"},
insert={key="CUSTOM_ALT_I",desc="Insert a new value to the vector"},
delete={key="CUSTOM_ALT_D",desc="Delete selected entry"},
reinterpret={key="CUSTOM_ALT_R",desc="Open selected entry as something else"},
help={key="HELP",desc="Show this help"},
NOT_USED={key="SEC_SELECT",desc="Choose an enum value from a list"}, --not a binding...
}
function getTargetFromScreens()
local my_trg
@ -75,6 +78,7 @@ function GmEditorUi:init(args)
local helpPage=widgets.Panel{
subviews={widgets.Label{text=helptext,frame = {l=1,t=1,yalign=0}}}}
local mainList=widgets.List{view_id="list_main",choices={},frame = {l=1,t=3,yalign=0},on_submit=self:callback("editSelected"),
on_submit2=self:callback("editSelectedEnum"),
text_pen=dfhack.pen.parse{fg=COLOR_DARKGRAY,bg=0},cursor_pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}
local mainPage=widgets.Panel{
subviews={
@ -155,6 +159,37 @@ end
function GmEditorUi:currentTarget()
return self.stack[#self.stack]
end
function GmEditorUi:editSelectedEnum(index,choice)
local trg=self:currentTarget()
local trg_key=trg.keys[index]
if trg.target._field==nil then qerror("not an enum") end
local enum=trg.target:_field(trg_key)._type
if enum._kind=="enum-type" then
local list={}
for i=enum._first_item, enum._last_item do
table.insert(list,{text=tostring(enum[i]),value=i})
end
guiScript.start(function()
local ret,idx,choice=guiScript.showListPrompt("Choose item:",nil,3,list)
if ret then
trg.target[trg_key]=choice.value
self:updateTarget(true)
end
end)
else
qerror("not an enum")
end
end
function GmEditorUi:openReinterpret(key)
local trg=self:currentTarget()
dialog.showInputPrompt(tostring(trg_key),"Enter new type:",COLOR_WHITE,
"",function(choice)
local ntype=df[tp]
self:pushTarget(df.reinterpret_cast(ntype,trg.target[key]))
end)
end
function GmEditorUi:editSelected(index,choice)
local trg=self:currentTarget()
local trg_key=trg.keys[index]
@ -206,7 +241,6 @@ function GmEditorUi:set(key,input)
self:updateTarget(true)
end
function GmEditorUi:onInput(keys)
if keys.LEAVESCREEN then
if self.subviews.pages:getSelected()==2 then
self.subviews.pages:setSelected(1)
@ -218,21 +252,35 @@ function GmEditorUi:onInput(keys)
local _,stoff=df.sizeof(trg.target)
local size,off=df.sizeof(trg.target:_field(self:getSelectedKey()))
dialog.showMessage("Offset",string.format("Size hex=%x,%x dec=%d,%d\nRelative hex=%x dec=%d",size,off,size,off,off-stoff,off-stoff),COLOR_WHITE)
--elseif keys.CUSTOM_ALT_F then --filter?
elseif keys[keybindings.find.key] then
self:find()
elseif keys[keybindings.lua_set.key] then
self:set(self:getSelectedKey())
--elseif keys.CUSTOM_I then
-- self:insertSimple()
elseif keys[keybindings.insert.key] then --insert
self:insertNew()
elseif keys[keybindings.delete.key] then --delete
self:deleteSelected(self:getSelectedKey())
elseif keys[keybindings.reinterpret.key] then
self:openReinterpret(self:getSelectedKey())
end
self.super.onInput(self,keys)
end
function getStringValue(trg,field)
local obj=trg.target
local text=tostring(obj[field])
pcall(function()
if obj._field ~= nil then
local enum=obj:_field(field)._type
if enum._kind=="enum-type" then
text=text.."("..tostring(enum[obj[field]])..")"
end
end
end)
return text
end
function GmEditorUi:updateTarget(preserve_pos,reindex)
local trg=self:currentTarget()
if reindex then
@ -244,7 +292,7 @@ function GmEditorUi:updateTarget(preserve_pos,reindex)
self.subviews.lbl_current_item:itemById('name').text=tostring(trg.target)
local t={}
for k,v in pairs(trg.keys) do
table.insert(t,{text={{text=string.format("%-25s",tostring(v))},{gap=1,text=tostring(trg.target[v]),}}})
table.insert(t,{text={{text=string.format("%-25s",tostring(v))},{gap=1,text=getStringValue(trg,v)}}})
end
local last_pos
if preserve_pos then
@ -292,6 +340,8 @@ if #args~=0 then
show_editor(t)
end
dialog.showInputPrompt("Gm Editor", "Object to edit:", COLOR_GRAY, "",thunk)
elseif args[1]=="free" then
show_editor(df.reinterpret_cast(df[args[2]],args[3]))
else
local t=load("return "..args[1])()
show_editor(t)

@ -0,0 +1,28 @@
-- Undesignates building base materials for dumping.
function undump_buildings()
local buildings = df.global.world.buildings.all
local undumped = 0
for i = 0, #buildings - 1 do
local building = buildings[i]
-- Zones and stockpiles don't have the contained_items field.
if df.building_actual:is_instance(building) then
local items = building.contained_items
for j = 0, #items - 1 do
local contained = items[j]
if contained.use_mode == 2 and contained.item.flags.dump then
-- print(building, contained.item)
undumped = undumped + 1
contained.item.flags.dump = false
end
end
end
end
if undumped > 0 then
local s = "s"
if undumped == 1 then s = "" end
print("Undumped "..undumped.." item"..s..".")
end
end
undump_buildings()