Merge remote-tracking branch 'origin/develop' into mousequery-z-level-fix

Conflicts:
	NEWS
develop
lethosor 2014-11-08 14:36:20 -05:00
commit 04ceb64bad
52 changed files with 1982 additions and 1176 deletions

@ -58,7 +58,7 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif()
# set up versioning.
set(DF_VERSION "0.40.13")
set(DF_VERSION "0.40.14")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")

32
NEWS

@ -1,12 +1,42 @@
DFHack future
DFHack Future
Fixes:
- mousequery: Fixed behavior when selecting a tile on the lowest z-level
Internals:
- EventManager: deals with frame_counter getting reset properly now.
- modtools/item-trigger: fixed equip/unequip bug and corrected minor documentation error
DFHack 0.40.14-r1
Internals:
- The DFHack console can now be disabled by setting the DFHACK_DISABLE_CONSOLE
environment variable: "DFHACK_DISABLE_CONSOLE=1 ./dfhack"
Fixes:
- Stopped duplicate load/unload events when unloading a world
- Stopped "-e" from being echoed when DFHack quits on Linux
- automelt now uses a faster method to locate items
- autotrade: "Mark all" no longer double-marks bin contents
- drainaquifer.rb: replaced with a faster less buggy drain-aquifer.lua
- embark-tools no longer conflicts with keys on the notes screen
- fastdwarf: Fixed problems with combat/attacks
- forum-dwarves should work now
- manipulator now uses a stable sort, allowing sorting by multiple categories
- rendermax updated to work with 0.40
New plugins:
- trackstop: Shows track stop friction and dump direction in its 'q' menu
New tweaks:
- farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus
- import-priority-category: Allows changing the priority of all goods in a
category when discussing an import agreement with the liaison
- manager-quantity: Removes the limit of 30 jobs per manager order
- civ-view-agreement: Fixes overlapping text on the "view agreement" screen
- nestbox-color: Fixes the color of built nestboxes
DFHack 0.40.13-r1
Internals:
- unified spatter structs
- added ruby df.print_color(color, string) method for dfhack console
Fixes:
- no more -e after terminating
- fixed superdwarf
DFHack 0.40.12-r1
Fixes:

@ -3,13 +3,13 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.8.1: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.11: http://docutils.sourceforge.net/" />
<title>DFHack Readme</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7056 2011-06-17 10:50:48Z milde $
:Id: $Id: html4css1.css 7614 2013-02-21 15:55:51Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
@ -77,7 +77,7 @@ div.tip p.admonition-title {
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
@ -249,10 +249,19 @@ pre.address {
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math {
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
@ -304,6 +313,21 @@ table.docutils th.field-name, table.docinfo th.docinfo-name {
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
@ -501,7 +525,7 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#binpatch" id="id132">binpatch</a></li>
<li><a class="reference internal" href="#create-items" id="id133">create-items</a></li>
<li><a class="reference internal" href="#digfort" id="id134">digfort</a></li>
<li><a class="reference internal" href="#drainaquifer" id="id135">drainaquifer</a></li>
<li><a class="reference internal" href="#drain-aquifer" id="id135">drain-aquifer</a></li>
<li><a class="reference internal" href="#deathcause" id="id136">deathcause</a></li>
<li><a class="reference internal" href="#dfstatus" id="id137">dfstatus</a></li>
<li><a class="reference internal" href="#embark" id="id138">embark</a></li>
@ -520,48 +544,50 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#source" id="id151">source</a></li>
<li><a class="reference internal" href="#superdwarf" id="id152">superdwarf</a></li>
<li><a class="reference internal" href="#stripcaged" id="id153">stripcaged</a></li>
<li><a class="reference internal" href="#undump-buildings" id="id154">undump-buildings</a></li>
<li><a class="reference internal" href="#teleport" id="id154">teleport</a></li>
<li><a class="reference internal" href="#undump-buildings" id="id155">undump-buildings</a></li>
</ul>
</li>
<li><a class="reference internal" href="#modtools" id="id155">modtools</a></li>
<li><a class="reference internal" href="#in-game-interface-tools" id="id156">In-game interface tools</a><ul>
<li><a class="reference internal" href="#dwarf-manipulator" id="id157">Dwarf Manipulator</a></li>
<li><a class="reference internal" href="#search" id="id158">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id159">AutoMaterial</a></li>
<li><a class="reference internal" href="#stockpile-automation" id="idautomelt">Stockpile Automation</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id160">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id161">gui/assign-rack</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-companion-order" id="id164">gui/companion-order</a></li>
<li><a class="reference internal" href="#gui-gm-editor" id="id165">gui/gm-editor</a></li>
<li><a class="reference internal" href="#hotkeys" id="idhotkeys">Hotkeys</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id166">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id167">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id168">gui/mod-manager</a></li>
<li><a class="reference internal" href="#gui-rename" id="id169">gui/rename</a></li>
<li><a class="reference internal" href="#gui-room-list" id="id170">gui/room-list</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id171">gui/guide-path</a></li>
<li><a class="reference internal" href="#gui-workflow" id="id172">gui/workflow</a></li>
<li><a class="reference internal" href="#gui-workshop-job" id="id173">gui/workshop-job</a></li>
<li><a class="reference internal" href="#modtools" id="id156">modtools</a></li>
<li><a class="reference internal" href="#in-game-interface-tools" id="id157">In-game interface tools</a><ul>
<li><a class="reference internal" href="#dwarf-manipulator" id="id158">Dwarf Manipulator</a></li>
<li><a class="reference internal" href="#search" id="id159">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id160">AutoMaterial</a></li>
<li><a class="reference internal" href="#stockpile-automation" id="id161">Stockpile Automation</a></li>
<li><a class="reference internal" href="#track-stop-menu" id="id162">Track Stop Menu</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id163">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id164">gui/assign-rack</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id165">gui/choose-weapons</a></li>
<li><a class="reference internal" href="#gui-clone-uniform" id="id166">gui/clone-uniform</a></li>
<li><a class="reference internal" href="#gui-companion-order" id="id167">gui/companion-order</a></li>
<li><a class="reference internal" href="#gui-gm-editor" id="id168">gui/gm-editor</a></li>
<li><a class="reference internal" href="#hotkeys" id="id169">Hotkeys</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id170">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id171">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id172">gui/mod-manager</a></li>
<li><a class="reference internal" href="#gui-rename" id="id173">gui/rename</a></li>
<li><a class="reference internal" href="#gui-room-list" id="id174">gui/room-list</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id175">gui/guide-path</a></li>
<li><a class="reference internal" href="#gui-workflow" id="id176">gui/workflow</a></li>
<li><a class="reference internal" href="#gui-workshop-job" id="id177">gui/workshop-job</a></li>
</ul>
</li>
<li><a class="reference internal" href="#behavior-mods" id="id174">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id175">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id176">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id177">Configuration UI</a></li>
<li><a class="reference internal" href="#behavior-mods" id="id178">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id179">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id180">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id181">Configuration UI</a></li>
</ul>
</li>
<li><a class="reference internal" href="#power-meter" id="id178">Power Meter</a></li>
<li><a class="reference internal" href="#steam-engine" id="id179">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id180">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id181">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id182">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id183">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id184">Save files</a></li>
<li><a class="reference internal" href="#power-meter" id="id182">Power Meter</a></li>
<li><a class="reference internal" href="#steam-engine" id="id183">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id184">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id185">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id186">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id187">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id188">Save files</a></li>
</ul>
</li>
<li><a class="reference internal" href="#add-spatter" id="id185">Add Spatter</a></li>
<li><a class="reference internal" href="#add-spatter" id="id189">Add Spatter</a></li>
</ul>
</li>
</ul>
@ -1930,14 +1956,6 @@ they are scuttled.</td>
<tr class="field"><th class="field-name">stable-cursor:</th><td class="field-body"><p class="first">Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.</p>
</td>
</tr>
<tr class="field"><th class="field-name">patrol-duty:</th><td class="field-body"><p class="first">Makes Train orders not count as patrol duty to stop unhappy thoughts.
Does NOT fix the problem when soldiers go off-duty (i.e. civilian).</p>
</td>
</tr>
<tr class="field"><th class="field-name">stable-temp:</th><td class="field-body"><p class="first">Fixes performance bug 6012 by squashing jitter in temperature updates.
In very item-heavy forts with big stockpiles this can improve FPS by 50-100%</p>
</td>
</tr>
<tr class="field"><th class="field-name">fast-heat:</th><td class="field-body"><p class="first">Further improves temperature update performance by ensuring that 1 degree
of item temperature is crossed in no more than specified number of frames
when updating from the environment temperature. This reduces the time it
@ -1975,7 +1993,24 @@ With this tweak, items made from cloth and leather will gain a level of wear eve
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Prevents adamantine clothing from wearing out while being worn (bug 6481).</p>
</td>
</tr>
<tr class="field"><th class="field-name">confirm-embark:</th><td class="field-body"><p class="first last">Adds a prompt before embarking (on the &quot;prepare carefully&quot; screen).</p>
<tr class="field"><th class="field-name" colspan="2">farm-plot-select:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Adds &quot;Select all&quot; and &quot;Deselect all&quot; options to farm plot menus</p>
</td>
</tr>
<tr class="field"><th class="field-name" colspan="2">import-priority-category:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Allows changing the priority of all goods in a
category when discussing an import agreement with the liaison</p>
</td>
</tr>
<tr class="field"><th class="field-name" colspan="2">manager-quantity:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Removes the limit of 30 jobs per manager order</p>
</td>
</tr>
<tr class="field"><th class="field-name" colspan="2">civ-view-agreement:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Fixes overlapping text on the &quot;view agreement&quot; screen</p>
</td>
</tr>
<tr class="field"><th class="field-name">nestbox-color:</th><td class="field-body"><p class="first last">Fixes the color of built nestboxes</p>
</td>
</tr>
</tbody>
@ -2107,7 +2142,7 @@ with visualizers.</p>
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">maps:</th><td class="field-body">Exports all fifteen detailed maps</td>
<tr class="field"><th class="field-name">maps:</th><td class="field-body">Exports all seventeen detailed maps</td>
</tr>
<tr class="field"><th class="field-name">all:</th><td class="field-body">first exports the world/gen info, then the XML, then all detailed maps</td>
</tr>
@ -2310,9 +2345,38 @@ workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
<h2><a class="toc-backref" href="#id109">Fortress activity management</a></h2>
<div class="section" id="seedwatch">
<h3><a class="toc-backref" href="#id110">seedwatch</a></h3>
<p>Tool for turning cooking of seeds and plants on/off depending on how much you
have of them.</p>
<p>See 'seedwatch help' for detailed description.</p>
<p>Watches the numbers of seeds available and enables/disables seed and plant cooking.</p>
<p>Each plant type can be assigned a limit. If their number falls below that limit,
the plants and seeds of that type will be excluded from cookery.
If the number rises above the limit + 20, then cooking will be allowed.</p>
<p>The plugin needs a fortress to be loaded and will deactivate automatically otherwise.
You have to reactivate with 'seedwatch start' after you load the game.</p>
<p>Options:</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">all:</th><td class="field-body">Adds all plants from the abbreviation list to the watch list.</td>
</tr>
<tr class="field"><th class="field-name">start:</th><td class="field-body">Start watching.</td>
</tr>
<tr class="field"><th class="field-name">stop:</th><td class="field-body">Stop watching.</td>
</tr>
<tr class="field"><th class="field-name">info:</th><td class="field-body">Display whether seedwatch is watching, and the watch list.</td>
</tr>
<tr class="field"><th class="field-name">clear:</th><td class="field-body">Clears the watch list.</td>
</tr>
</tbody>
</table>
<p>Examples:</p>
<dl class="docutils">
<dt><tt class="docutils literal">seedwatch MUSHROOM_HELMET_PLUMP 30</tt></dt>
<dd>add <tt class="docutils literal">MUSHROOM_HELMET_PLUMP</tt> to the watch list, limit = 30</dd>
<dt><tt class="docutils literal">seedwatch MUSHROOM_HELMET_PLUMP</tt></dt>
<dd>removes <tt class="docutils literal">MUSHROOM_HELMET_PLUMP</tt> from the watch list.</dd>
<dt><tt class="docutils literal">seedwatch all 30</tt></dt>
<dd>adds all plants from the abbreviation list to the watch list, the limit being 30.</dd>
</dl>
</div>
<div class="section" id="zone">
<h3><a class="toc-backref" href="#id111">zone</a></h3>
@ -2873,8 +2937,8 @@ as an offset for the pattern: instead of starting at the cursor, it will start
<p>The script takes the plan filename, starting from the root df folder (where
Dwarf Fortress.exe is found).</p>
</div>
<div class="section" id="drainaquifer">
<h2><a class="toc-backref" href="#id135">drainaquifer</a></h2>
<div class="section" id="drain-aquifer">
<h2><a class="toc-backref" href="#id135">drain-aquifer</a></h2>
<p>Remove all 'aquifer' tag from the map blocks. Irreversible.</p>
</div>
<div class="section" id="deathcause">
@ -3089,13 +3153,25 @@ alternatively pass cage IDs as arguments:</p>
stripcaged weapons 25321 34228
</pre>
</div>
<div class="section" id="teleport">
<h2><a class="toc-backref" href="#id154">teleport</a></h2>
<p>Teleports a unit to given coordinates.</p>
<p>Examples:</p>
<pre class="literal-block">
teleport showunitid - prints unitid beneath cursor
teleport showpos - prints coordinates beneath cursor
teleport unit 1234 x 56 y 115 z 26 - teleports unit 1234 to 56,115,26
</pre>
<p>One or both of <tt class="docutils literal">unit</tt> and <tt class="docutils literal">x</tt>/<tt class="docutils literal">y</tt>/<tt class="docutils literal">z</tt> coordinate positions must be
specified. If one is omitted, the unit or position beneath the cursor is used.</p>
</div>
<div class="section" id="undump-buildings">
<h2><a class="toc-backref" href="#id154">undump-buildings</a></h2>
<h2><a class="toc-backref" href="#id155">undump-buildings</a></h2>
<p>Undesignates building base materials for dumping.</p>
</div>
</div>
<div class="section" id="modtools">
<h1><a class="toc-backref" href="#id155">modtools</a></h1>
<h1><a class="toc-backref" href="#id156">modtools</a></h1>
<p>These scripts are mostly useful for raw modders and scripters. They all have standard arguments: arguments are of the form <tt class="docutils literal">tool <span class="pre">-argName1</span> argVal1 <span class="pre">-argName2</span> argVal2</tt>. This is equivalent to <tt class="docutils literal">tool <span class="pre">-argName2</span> argVal2 <span class="pre">-argName1</span> argVal1</tt>. It is not necessary to provide a value to an argument name: <tt class="docutils literal">tool <span class="pre">-argName3</span></tt> is fine. Supplying the same argument name multiple times will result in an error. Argument names are preceded with a dash. The <tt class="docutils literal"><span class="pre">-help</span></tt> argument will print a descriptive usage string describing the nature of the arguments. For multiple word argument values, brackets must be used: <tt class="docutils literal">tool <span class="pre">-argName4</span> [ sadf1 sadf2 sadf3 ]</tt>. In order to allow passing literal braces as part of the argument, backslashes are used: <tt class="docutils literal">tool <span class="pre">-argName4</span> [ \] asdf \foo ]</tt> sets <tt class="docutils literal">argName4</tt> to <tt class="docutils literal">\] asdf foo</tt>. The <tt class="docutils literal"><span class="pre">*-trigger</span></tt> scripts have a similar policy with backslashes.</p>
<ul>
<li><p class="first">add-syndrome</p>
@ -3152,7 +3228,7 @@ stripcaged weapons 25321 34228
</ul>
</div>
<div class="section" id="in-game-interface-tools">
<h1><a class="toc-backref" href="#id156">In-game interface tools</a></h1>
<h1><a class="toc-backref" href="#id157">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">
@ -3167,7 +3243,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="#id157">Dwarf Manipulator</a></h2>
<h2><a class="toc-backref" href="#id158">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" />
@ -3208,7 +3284,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="#id158">Search</a></h2>
<h2><a class="toc-backref" href="#id159">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
@ -3239,7 +3315,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="#id159">AutoMaterial</a></h2>
<h2><a class="toc-backref" href="#id160">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
@ -3267,7 +3343,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="stockpile-automation">
<h2><a class="toc-backref" href="#idautomelt">Stockpile Automation</a></h2>
<h2><a class="toc-backref" href="#id161">Stockpile Automation</a></h2>
<p>Enable the automelt plugin in your dfhack.init with:</p>
<pre class="literal-block">
enable automelt
@ -3275,8 +3351,22 @@ enable automelt
<p>When querying a stockpile an option will appear to toggle automelt for this stockpile.
Any items placed in this stockpile will be designated to be melted.</p>
</div>
<div class="section" id="track-stop-menu">
<h2><a class="toc-backref" href="#id162">Track Stop Menu</a></h2>
<p>The <cite>q</cite> menu of track stops is completely blank by default. To enable one:</p>
<pre class="literal-block">
enable trackstop
</pre>
<p>This allows you to view and/or change the track stop's friction and dump direction settings.
It re-uses the keybindings from the track stop building interface:</p>
<ul class="simple">
<li>BUILDING_TRACK_STOP_FRICTION_UP</li>
<li>BUILDING_TRACK_STOP_FRICTION_DOWN</li>
<li>BUILDING_TRACK_STOP_DUMP</li>
</ul>
</div>
<div class="section" id="gui-advfort">
<h2><a class="toc-backref" href="#id160">gui/advfort</a></h2>
<h2><a class="toc-backref" href="#id163">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>
@ -3295,7 +3385,7 @@ implies -a</li>
</div>
</div>
<div class="section" id="gui-assign-rack">
<h2><a class="toc-backref" href="#id161">gui/assign-rack</a></h2>
<h2><a class="toc-backref" href="#id164">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" />
@ -3319,7 +3409,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-choose-weapons">
<h2><a class="toc-backref" href="#id162">gui/choose-weapons</a></h2>
<h2><a class="toc-backref" href="#id165">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
@ -3330,14 +3420,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="#id163">gui/clone-uniform</a></h2>
<h2><a class="toc-backref" href="#id166">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-companion-order">
<h2><a class="toc-backref" href="#id164">gui/companion-order</a></h2>
<h2><a class="toc-backref" href="#id167">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" />
@ -3353,7 +3443,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="#id165">gui/gm-editor</a></h2>
<h2><a class="toc-backref" href="#id168">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
@ -3368,7 +3458,7 @@ the same as version above.</li>
in-game help.</p>
</div>
<div class="section" id="hotkeys">
<h2><a class="toc-backref" href="#idhotkeys">Hotkeys</a></h2>
<h2><a class="toc-backref" href="#id169">Hotkeys</a></h2>
<p>Opens an in-game screen showing DFHack keybindings that are valid in the current mode.</p>
<img alt="images/hotkeys.png" src="images/hotkeys.png" />
<p>Type <tt class="docutils literal">hotkeys</tt> into the DFHack console to open the screen, or bind the command to a
@ -3378,7 +3468,7 @@ keybinding add Ctrl-F1 hotkeys
</pre>
</div>
<div class="section" id="gui-liquids">
<h2><a class="toc-backref" href="#id166">gui/liquids</a></h2>
<h2><a class="toc-backref" href="#id170">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,
@ -3398,7 +3488,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="#id167">gui/mechanisms</a></h2>
<h2><a class="toc-backref" href="#id171">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
@ -3408,13 +3498,13 @@ 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-mod-manager">
<h2><a class="toc-backref" href="#id168">gui/mod-manager</a></h2>
<h2><a class="toc-backref" href="#id172">gui/mod-manager</a></h2>
<p>A way to simply install and remove small mods. It looks for specially formatted 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 class="section" id="gui-rename">
<h2><a class="toc-backref" href="#id169">gui/rename</a></h2>
<h2><a class="toc-backref" href="#id173">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>
@ -3437,7 +3527,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="#id170">gui/room-list</a></h2>
<h2><a class="toc-backref" href="#id174">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" />
@ -3445,7 +3535,7 @@ either immediately or after opening the assign owner page.</p>
list, and allows unassigning them.</p>
</div>
<div class="section" id="gui-guide-path">
<h2><a class="toc-backref" href="#id171">gui/guide-path</a></h2>
<h2><a class="toc-backref" href="#id175">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" />
@ -3453,7 +3543,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-workflow">
<h2><a class="toc-backref" href="#id172">gui/workflow</a></h2>
<h2><a class="toc-backref" href="#id176">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" />
@ -3500,7 +3590,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-workshop-job">
<h2><a class="toc-backref" href="#id173">gui/workshop-job</a></h2>
<h2><a class="toc-backref" href="#id177">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" />
@ -3537,7 +3627,7 @@ you have to unset the material first.</p>
</div>
</div>
<div class="section" id="behavior-mods">
<h1><a class="toc-backref" href="#id174">Behavior Mods</a></h1>
<h1><a class="toc-backref" href="#id178">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>
@ -3548,20 +3638,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="#id175">Siege Engine</a></h2>
<h2><a class="toc-backref" href="#id179">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="#id176">Rationale</a></h3>
<h3><a class="toc-backref" href="#id180">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="#id177">Configuration UI</a></h3>
<h3><a class="toc-backref" href="#id181">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>
@ -3584,7 +3674,7 @@ menu.</p>
</div>
</div>
<div class="section" id="power-meter">
<h2><a class="toc-backref" href="#id178">Power Meter</a></h2>
<h2><a class="toc-backref" href="#id182">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
@ -3595,11 +3685,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="#id179">Steam Engine</a></h2>
<h2><a class="toc-backref" href="#id183">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="#id180">Rationale</a></h3>
<h3><a class="toc-backref" href="#id184">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
@ -3610,7 +3700,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="#id181">Construction</a></h3>
<h3><a class="toc-backref" href="#id185">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>
@ -3634,7 +3724,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="#id182">Operation</a></h3>
<h3><a class="toc-backref" href="#id186">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
@ -3665,7 +3755,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="#id183">Explosions</a></h3>
<h3><a class="toc-backref" href="#id187">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
@ -3674,7 +3764,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="#id184">Save files</a></h3>
<h3><a class="toc-backref" href="#id188">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
@ -3685,7 +3775,7 @@ being generated.</p>
</div>
</div>
<div class="section" id="add-spatter">
<h2><a class="toc-backref" href="#id185">Add Spatter</a></h2>
<h2><a class="toc-backref" href="#id189">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

@ -1258,11 +1258,6 @@ One-shot subcommands:
Subcommands that persist until disabled or DF quit:
:stable-cursor: Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.
:patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts.
Does NOT fix the problem when soldiers go off-duty (i.e. civilian).
:stable-temp: Fixes performance bug 6012 by squashing jitter in temperature updates.
In very item-heavy forts with big stockpiles this can improve FPS by 50-100%
:fast-heat: Further improves temperature update performance by ensuring that 1 degree
of item temperature is crossed in no more than specified number of frames
when updating from the environment temperature. This reduces the time it
@ -1286,7 +1281,12 @@ Subcommands that persist until disabled or DF quit:
:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (bug 6481).
:confirm-embark: Adds a prompt before embarking (on the "prepare carefully" screen).
:farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus
:import-priority-category: Allows changing the priority of all goods in a
category when discussing an import agreement with the liaison
:manager-quantity: Removes the limit of 30 jobs per manager order
:civ-view-agreement: Fixes overlapping text on the "view agreement" screen
:nestbox-color: Fixes the color of built nestboxes
fix-armory
----------
@ -1626,10 +1626,31 @@ Fortress activity management
seedwatch
---------
Tool for turning cooking of seeds and plants on/off depending on how much you
have of them.
Watches the numbers of seeds available and enables/disables seed and plant cooking.
Each plant type can be assigned a limit. If their number falls below that limit,
the plants and seeds of that type will be excluded from cookery.
If the number rises above the limit + 20, then cooking will be allowed.
The plugin needs a fortress to be loaded and will deactivate automatically otherwise.
You have to reactivate with 'seedwatch start' after you load the game.
Options:
:all: Adds all plants from the abbreviation list to the watch list.
:start: Start watching.
:stop: Stop watching.
:info: Display whether seedwatch is watching, and the watch list.
:clear: Clears the watch list.
Examples:
See 'seedwatch help' for detailed description.
``seedwatch MUSHROOM_HELMET_PLUMP 30``
add ``MUSHROOM_HELMET_PLUMP`` to the watch list, limit = 30
``seedwatch MUSHROOM_HELMET_PLUMP``
removes ``MUSHROOM_HELMET_PLUMP`` from the watch list.
``seedwatch all 30``
adds all plants from the abbreviation list to the watch list, the limit being 30.
zone
----
@ -2149,8 +2170,8 @@ as an offset for the pattern: instead of starting at the cursor, it will start
The script takes the plan filename, starting from the root df folder (where
Dwarf Fortress.exe is found).
drainaquifer
============
drain-aquifer
=============
Remove all 'aquifer' tag from the map blocks. Irreversible.
deathcause
@ -2655,6 +2676,19 @@ Enable the automelt plugin in your dfhack.init with::
When querying a stockpile an option will appear to toggle automelt for this stockpile.
Any items placed in this stockpile will be designated to be melted.
Track Stop Menu
===============
The `q` menu of track stops is completely blank by default. To enable one::
enable trackstop
This allows you to view and/or change the track stop's friction and dump direction settings.
It re-uses the keybindings from the track stop building interface:
* BUILDING_TRACK_STOP_FRICTION_UP
* BUILDING_TRACK_STOP_FRICTION_DOWN
* BUILDING_TRACK_STOP_DUMP
gui/advfort
===========

@ -139,9 +139,6 @@ keybinding add P@dwarfmode/QueryBuilding/Some/Weaponrack gui/assign-rack
# stabilize the cursor of dwarfmode when switching menus
tweak stable-cursor
# stop military from considering training as 'patrol duty'
tweak patrol-duty
# stop stacked liquid/bar/thread/cloth items from lasting forever
# if used in reactions that use only a fraction of the dimension.
# might be fixed by DF
@ -169,6 +166,15 @@ tweak craft-age-wear
# stop adamantine clothing from wearing out (bug 6481)
#tweak adamantine-cloth-wear
# Add "Select all" and "Deselect all" options to farm plot menus
tweak farm-plot-select
# Add Shift-Left/Right controls to import agreement screen
tweak import-priority-category
# Misc. UI tweaks
tweak civ-view-agreement
###########################
# Globally acting plugins #
###########################
@ -183,7 +189,7 @@ enable search
enable automaterial
# Other interface improvement tools
enable dwarfmonitor mousequery automelt autotrade buildingplan resume zone
enable dwarfmonitor mousequery automelt autotrade buildingplan resume trackstop zone stocks autochop
# allow the fortress bookkeeper to queue jobs through the manager
stockflow enable

@ -61,6 +61,7 @@ using namespace DFHack;
#include "df/world_data.h"
#include "df/interfacest.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_game_cleanerst.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include <df/graphic.h>
@ -1027,12 +1028,15 @@ bool Core::Init()
cerr << "Initializing Console.\n";
// init the console.
bool is_text_mode = false;
if(init && init->display.flag.is_set(init_display_flags::TEXT))
bool is_text_mode = (init && init->display.flag.is_set(init_display_flags::TEXT));
if (is_text_mode || getenv("DFHACK_DISABLE_CONSOLE"))
{
is_text_mode = true;
con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n";
if (!is_text_mode)
{
cout << "Console disabled.\n";
}
}
else if(con.init(false))
cerr << "Console is running.\n";
@ -1280,6 +1284,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
}
bool is_load_save =
strict_virtual_cast<df::viewscreen_game_cleanerst>(screen) ||
strict_virtual_cast<df::viewscreen_loadgamest>(screen) ||
strict_virtual_cast<df::viewscreen_savegamest>(screen);

@ -67,10 +67,7 @@ void *type_identity::allocate() {
bool type_identity::copy(void *tgt, const void *src) {
if (can_allocate() && tgt && src)
{
do_copy(tgt, src);
return true;
}
return do_copy(tgt, src);
else
return false;
}

@ -39,6 +39,14 @@ namespace df {
stl_bit_vector_identity identity_traits<std::vector<bool> >::identity;
bit_array_identity identity_traits<BitArray<int> >::identity;
static void *fstream_allocator_fn(void *out, const void *in) {
if (out) { /* *(T*)out = *(const T*)in;*/ return NULL; }
else if (in) { delete (std::fstream*)in; return (std::fstream*)in; }
else return new std::fstream();
}
opaque_identity identity_traits<std::fstream>::identity(
sizeof(std::fstream), fstream_allocator_fn, "fstream");
buffer_container_identity buffer_container_identity::base_instance;
#undef NUMBER_IDENTITY_TRAITS

@ -1256,8 +1256,11 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type)
// Index the fields
lua_newtable(state);
if (type->type() != IDTYPE_OPAQUE)
{
EnableMetaField(state, base+2, "value", type);
AssociateId(state, base+3, 1, "value");
}
// Add the iteration metamethods
PushStructMethod(state, base+1, base+3, meta_struct_next);

@ -61,7 +61,8 @@ namespace DFHack
IDTYPE_STRUCT,
IDTYPE_CLASS,
IDTYPE_BUFFER,
IDTYPE_STL_PTR_VECTOR
IDTYPE_STL_PTR_VECTOR,
IDTYPE_OPAQUE
};
typedef void *(*TAllocateFn)(void*,const void*);
@ -78,7 +79,7 @@ namespace DFHack
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:
@ -116,7 +117,7 @@ namespace DFHack
virtual bool can_allocate() { return (allocator != NULL); }
virtual void *do_allocate() { return allocator(NULL,NULL); }
virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); }
virtual bool do_copy(void *tgt, const void *src) { return allocator(tgt,src) == tgt; }
virtual bool do_destroy(void *obj) { return allocator(NULL,obj) == obj; }
public:
virtual bool isPrimitive() { return false; }
@ -166,7 +167,7 @@ namespace DFHack
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:
@ -199,7 +200,7 @@ namespace DFHack
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate();
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:

@ -65,6 +65,17 @@ namespace DFHack
virtual identity_type type() { return IDTYPE_PRIMITIVE; }
};
class DFHACK_EXPORT opaque_identity : public constructed_identity {
std::string name;
public:
opaque_identity(size_t size, TAllocateFn alloc, const std::string &name)
: constructed_identity(size, alloc), name(name) {};
virtual std::string getFullName() { return name; }
virtual identity_type type() { return IDTYPE_OPAQUE; }
};
class DFHACK_EXPORT pointer_identity : public primitive_identity {
type_identity *target;
@ -170,6 +181,7 @@ namespace df
{
using DFHack::function_identity_base;
using DFHack::primitive_identity;
using DFHack::opaque_identity;
using DFHack::pointer_identity;
using DFHack::container_identity;
using DFHack::ptr_container_identity;
@ -488,6 +500,11 @@ namespace df
static stl_string_identity *get() { return &identity; }
};
template<> struct DFHACK_EXPORT identity_traits<std::fstream> {
static opaque_identity identity;
static opaque_identity *get() { return &identity; }
};
template<> struct DFHACK_EXPORT identity_traits<char*> {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }

@ -0,0 +1,12 @@
file_compressorst& operator=(const file_compressorst &in) {
compressed = in.compressed;
/* fstream cannot be assigned */
in_buffer = in.in_buffer;
in_buffersize = in.in_buffersize;
in_buffer_amount_loaded = in.in_buffer_amount_loaded;
in_buffer_position = in.in_buffer_position;
out_buffer = in.out_buffer;
out_buffersize = in.out_buffersize;
out_buffer_amount_written = in.out_buffer_amount_written;
return *this;
}

@ -119,7 +119,7 @@ function isValidTarget(unit,syndrome)
end
for caste,creature in ipairs(syndrome.syn_affected_creature) do
local affectedCreature = creature.value
local affectedCaste = syndrome.syn_affectedCaste[caste].value
local affectedCaste = syndrome.syn_affected_caste[caste].value
if affectedCreature == unitRaceName and (affectedCaste == unitCasteName or affectedCaste == "ALL") then
affected = true
end

@ -320,7 +320,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
}
else eventFrequency = 1;
if ( tick - eventLastTick[a] < eventFrequency )
if ( tick >= eventLastTick[a] && tick - eventLastTick[a] < eventFrequency )
continue;
eventManager[a](out);

@ -154,7 +154,7 @@ void Units::CopyCreature(df::unit * source, t_unit & furball)
// profession
furball.profession = source->profession;
// happiness
furball.happiness = source->status.happiness;
furball.happiness = 100;//source->status.happiness;
// physical attributes
memcpy(&furball.strength, source->body.physical_attrs, sizeof(source->body.physical_attrs));

@ -1 +1 @@
Subproject commit 93606b34b78a94b201704adb5c9ae28af304cdb1
Subproject commit d19695846518a3cb62f35e70d49a5e9064e10af1

@ -110,7 +110,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(digFlood digFlood.cpp)
add_subdirectory(diggingInvaders)
DFHACK_PLUGIN(drybuckets drybuckets.cpp)
DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
#DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
@ -132,7 +132,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua)
DFHACK_PLUGIN(manipulator manipulator.cpp)
DFHACK_PLUGIN(mode mode.cpp)
DFHACK_PLUGIN(misery misery.cpp)
#DFHACK_PLUGIN(misery misery.cpp)
DFHACK_PLUGIN(mousequery mousequery.cpp)
DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
DFHACK_PLUGIN(plants plants.cpp)
@ -156,9 +156,10 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(strangemood strangemood.cpp)
DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
DFHACK_PLUGIN(trackstop trackstop.cpp)
# DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(tubefill tubefill.cpp)
DFHACK_PLUGIN(tweak tweak.cpp)
add_subdirectory(tweak)
DFHACK_PLUGIN(weather weather.cpp)
DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(workNow workNow.cpp)

@ -819,7 +819,7 @@ static void assign_labor(unit_labor::unit_labor labor,
// bias by happiness
value += dwarfs[dwarf]->status.happiness;
//value += dwarfs[dwarf]->status.happiness;
values[dwarf] = value;

@ -7,6 +7,7 @@
#include "df/building_def.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/building_stockpilest.h"
#include "modules/Buildings.h"
#include "modules/Items.h"
#include "df/ui.h"
#include "modules/Maps.h"
@ -23,10 +24,36 @@ DFHACK_PLUGIN("automelt");
static const string PERSISTENCE_KEY = "automelt/stockpiles";
static void mark_all_in_stockpiles(vector<PersistentStockpileInfo> &stockpiles)
static int mark_item(df::item *item, df::item_flags bad_flags, int32_t stockpile_id)
{
if (item->flags.whole & bad_flags.whole)
return 0;
if (item->isAssignedToThisStockpile(stockpile_id)) {
size_t marked_count = 0;
std::vector<df::item*> contents;
Items::getContainedItems(item, &contents);
for (auto child = contents.begin(); child != contents.end(); child++)
{
std::vector<df::item*> &items = world->items.other[items_other_id::IN_PLAY];
marked_count += mark_item(*child, bad_flags, stockpile_id);
}
return marked_count;
}
if (!can_melt(item))
return 0;
if (is_set_to_melt(item))
return 0;
insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item);
item->flags.bits.melt = true;
return 1;
}
static void mark_all_in_stockpiles(vector<PersistentStockpileInfo> &stockpiles)
{
// Precompute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
@ -39,27 +66,16 @@ static void mark_all_in_stockpiles(vector<PersistentStockpileInfo> &stockpiles)
#undef F
size_t marked_count = 0;
for (size_t i = 0; i < items.size(); i++)
{
df::item *item = items[i];
if (item->flags.whole & bad_flags.whole)
continue;
if (!can_melt(item))
continue;
if (is_set_to_melt(item))
continue;
auto &melting_items = world->items.other[items_other_id::ANY_MELT_DESIGNATED];
for (auto it = stockpiles.begin(); it != stockpiles.end(); it++)
{
if (!it->inStockpile(item))
if (!it->isValid())
continue;
++marked_count;
insert_into_vector(melting_items, &df::item::id, item);
item->flags.bits.melt = true;
auto spid = it->getId();
Buildings::StockpileIterator stored;
for (stored.begin(it->getStockpile()); !stored.done(); ++stored)
{
marked_count += mark_item(*stored, bad_flags, spid);
}
}

@ -455,7 +455,9 @@ struct tradeview_hook : public df::viewscreen_tradegoodsst
{
for (int i = 0; i < trader_selected.size(); i++)
{
trader_selected[i] = 1;
// Only mark containers, not their contents.
// Granted, this behaves poorly with the search plugin...
trader_selected[i] = !trader_items[i]->flags.bits.in_inventory;
}
}
else if (input->count(interface_key::CUSTOM_U))
@ -469,7 +471,8 @@ struct tradeview_hook : public df::viewscreen_tradegoodsst
{
for (int i = 0; i < broker_selected.size(); i++)
{
broker_selected[i] = 1;
// Only mark containers, not their contents.
broker_selected[i] = !broker_items[i]->flags.bits.in_inventory;
}
}
else if (input->count(interface_key::CUSTOM_SHIFT_U))

@ -72,7 +72,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
" digFlood digAll0\n"
" disable digAll mode\n"
"\n"
"Note that while order matters, multiple commands can be sequenced in one line. It is recommended to alter your dfhack.init file so that you won't have to type in every mineral type you want to dig every time you start the game. Material names are case sensitive.\n"
"Note that while order matters, multiple commands can be sequenced in one line. It is recommended to alter your save-specific regionX/raw/onLoad.init or global onLoadWorld.init file so that you won't have to type in every mineral type you want to dig every time you start the game. Material names are case sensitive.\n"
));
return CR_OK;
}

@ -11,98 +11,13 @@
#include <VTableInterpose.h>
#include "ColorText.h"
#include "uicommon.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/interface_key.h"
using namespace DFHack;
struct EmbarkTool
{
std::string id;
std::string name;
std::string desc;
bool enabled;
df::interface_key toggle_key;
};
EmbarkTool embark_tools[] = {
{"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map",
false, df::interface_key::CUSTOM_A},
{"nano", "Nano embark", "Allows the embark size to be decreased below 2x2",
false, df::interface_key::CUSTOM_N},
{"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site",
false, df::interface_key::CUSTOM_S},
{"sticky", "Stable position", "Maintains the selected local area while navigating the world map",
false, df::interface_key::CUSTOM_P},
};
#define NUM_TOOLS int(sizeof(embark_tools) / sizeof(EmbarkTool))
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
void OutputString (int8_t color, int &x, int y, const std::string &text);
bool tool_exists (std::string tool_name);
bool tool_enabled (std::string tool_name);
bool tool_enable (std::string tool_name, bool enable_state);
void tool_update (std::string tool_name);
class embark_tools_settings : public dfhack_viewscreen
{
public:
embark_tools_settings () { };
~embark_tools_settings () { };
void help () { };
std::string getFocusString () { return "embark-tools/options"; };
void render ()
{
parent->render();
int x;
auto dim = Screen::getWindowSize();
int width = 50,
height = 4 + 1 + NUM_TOOLS, // Padding + lower row
min_x = (dim.x - width) / 2,
max_x = (dim.x + width) / 2,
min_y = (dim.y - height) / 2,
max_y = min_y + height;
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::SELECT));
OutputString(COLOR_WHITE, x, max_y - 2, "/");
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
OutputString(COLOR_WHITE, x, max_y - 2, ": Done");
for (int i = 0, y = min_y + 2; i < NUM_TOOLS; i++, y++)
{
EmbarkTool t = embark_tools[i];
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t.toggle_key));
OutputString(COLOR_WHITE, x, y, ": " + t.name + (t.enabled ? ": Enabled" : ": Disabled"));
}
};
void feed (std::set<df::interface_key> * input)
{
if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
{
Screen::dismiss(this);
return;
}
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
for (int i = 0; i < NUM_TOOLS; i++)
{
if (embark_tools[i].toggle_key == key)
{
embark_tools[i].enabled = !embark_tools[i].enabled;
}
}
}
};
};
/*
* Logic
*/
#define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++)
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
{
@ -159,88 +74,81 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
update_embark_sidebar(screen);
}
std::string sand_indicator = "";
bool sand_dirty = true; // Flag set when update is needed
void sand_update (df::viewscreen_choose_start_sitest * screen)
{
CoreSuspendClaimer suspend;
buffered_color_ostream out;
Core::getInstance().runCommand(out, "prospect");
auto fragments = out.fragments();
sand_indicator = "";
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
std::string fragment = iter->second;
if (fragment.find("SAND_") != std::string::npos)
{
sand_indicator = "Sand";
break;
}
}
sand_dirty = false;
}
typedef df::viewscreen_choose_start_sitest start_sitest;
typedef std::set<df::interface_key> ikey_set;
int sticky_pos[] = {0, 0, 3, 3};
bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen)
class EmbarkTool
{
sticky_pos[0] = screen->location.embark_pos_min.x;
sticky_pos[1] = screen->location.embark_pos_max.x;
sticky_pos[2] = screen->location.embark_pos_min.y;
sticky_pos[3] = screen->location.embark_pos_max.y;
}
void sticky_apply (df::viewscreen_choose_start_sitest * screen)
{
if (screen->finder.finder_state != -1)
{
// Site finder is active - don't override default local position
return;
}
screen->location.embark_pos_min.x = sticky_pos[0];
screen->location.embark_pos_max.x = sticky_pos[1];
screen->location.embark_pos_min.y = sticky_pos[2];
screen->location.embark_pos_max.y = sticky_pos[3];
update_embark_sidebar(screen);
}
protected:
bool enabled;
public:
EmbarkTool()
:enabled(false)
{ }
virtual bool getEnabled() { return enabled; }
virtual void setEnabled(bool state) { enabled = state; }
virtual void toggleEnabled() { setEnabled(!enabled); }
virtual std::string getId() = 0;
virtual std::string getName() = 0;
virtual std::string getDesc() = 0;
virtual df::interface_key getToggleKey() = 0;
virtual void before_render(start_sitest* screen) { };
virtual void after_render(start_sitest* screen) { };
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
virtual void after_feed(start_sitest* screen, ikey_set* input) { };
};
std::vector<EmbarkTool*> tools;
/*
* Viewscreen hooks
*/
void OutputString (int8_t color, int &x, int y, const std::string &text)
class SampleTool : public EmbarkTool
{
Screen::paintString(Screen::Pen(' ', color, 0), x, y, text);
x += text.length();
}
virtual std::string getId() { return "id"; }
virtual std::string getName() { return "Name"; }
virtual std::string getDesc() { return "Description"; }
virtual df::interface_key getToggleKey() { return df::interface_key::KEY; }
virtual void before_render(start_sitest* screen) { }
virtual void after_render(start_sitest* screen) { }
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
virtual void after_feed(start_sitest* screen, ikey_set* input) { };
};
struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
typedef df::viewscreen_choose_start_sitest interpose_base;
*/
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
{
bool prevent_default = false;
if (tool_enabled("anywhere"))
class EmbarkAnywhere : public EmbarkTool
{
for (auto iter = input->begin(); iter != input->end(); iter++)
public:
virtual std::string getId() { return "anywhere"; }
virtual std::string getName() { return "Embark anywhere"; }
virtual std::string getDesc() { return "Allows embarking anywhere on the world map"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_A; }
virtual void after_render(start_sitest* screen)
{
df::interface_key key = *iter;
if (key == df::interface_key::SETUP_EMBARK)
auto dim = Screen::getWindowSize();
int x = 20, y = dim.y - 2;
if (screen->page >= 0 && screen->page <= 4)
{
prevent_default = true;
this->in_embark_normal = 1;
}
OutputString(COLOR_WHITE, x, y, ": Embark!");
}
}
if (input->count(df::interface_key::CUSTOM_S))
virtual void before_feed(start_sitest* screen, ikey_set *input, bool &cancel)
{
Screen::show(new embark_tools_settings);
return;
if (input->count(df::interface_key::SETUP_EMBARK))
{
cancel = true;
screen->in_embark_normal = 1;
}
};
};
if (tool_enabled("nano"))
class NanoEmbark : public EmbarkTool
{
public:
virtual std::string getId() { return "nano"; }
virtual std::string getName() { return "Nano embark"; }
virtual std::string getDesc() { return "Allows the embark size to be decreased below 2x2"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_N; }
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel)
{
for (auto iter = input->begin(); iter != input->end(); iter++)
{
@ -266,14 +174,117 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
}
if (is_resize)
{
prevent_default = true;
resize_embark(this, dx, dy);
cancel = true;
resize_embark(screen, dx, dy);
return;
}
}
};
};
class SandIndicator : public EmbarkTool
{
protected:
bool dirty;
std::string indicator;
void update_indicator()
{
CoreSuspendClaimer suspend;
buffered_color_ostream out;
Core::getInstance().runCommand(out, "prospect");
auto fragments = out.fragments();
indicator = "";
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
std::string fragment = iter->second;
if (fragment.find("SAND_") != std::string::npos)
{
indicator = "Sand";
break;
}
}
dirty = false;
}
public:
SandIndicator()
:EmbarkTool(),
dirty(true),
indicator("")
{ }
virtual void setEnabled(bool state)
{
EmbarkTool::setEnabled(state);
dirty = true;
}
virtual std::string getId() { return "sand"; }
virtual std::string getName() { return "Sand indicator"; }
virtual std::string getDesc() { return "Displays an indicator when sand is present on the given embark site"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_S; }
virtual void after_render(start_sitest* screen)
{
if (dirty)
update_indicator();
auto dim = Screen::getWindowSize();
int x = dim.x - 28,
y = 13;
if (screen->page == 0)
{
OutputString(COLOR_YELLOW, x, y, indicator);
}
}
virtual void after_feed(start_sitest* screen, ikey_set* input)
{
dirty = true;
};
};
if (tool_enabled("sticky"))
class StablePosition : public EmbarkTool
{
protected:
int prev_position[4];
bool moved_position;
void save_position(start_sitest* screen)
{
prev_position[0] = screen->location.embark_pos_min.x;
prev_position[1] = screen->location.embark_pos_max.x;
prev_position[2] = screen->location.embark_pos_min.y;
prev_position[3] = screen->location.embark_pos_max.y;
}
void restore_position(start_sitest* screen)
{
if (screen->finder.finder_state != -1)
{
// Site finder is active - don't override default local position
return;
}
screen->location.embark_pos_min.x = prev_position[0];
screen->location.embark_pos_max.x = prev_position[1];
screen->location.embark_pos_min.y = prev_position[2];
screen->location.embark_pos_max.y = prev_position[3];
update_embark_sidebar(screen);
}
public:
StablePosition()
:EmbarkTool(),
moved_position(false)
{
prev_position[0] = 0;
prev_position[1] = 0;
prev_position[2] = 3;
prev_position[3] = 3;
}
virtual std::string getId() { return "sticky"; }
virtual std::string getName() { return "Stable position"; }
virtual std::string getDesc() { return "Maintains the selected local area while navigating the world map"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_P; }
virtual void before_render(start_sitest* screen) {
if (moved_position)
{
restore_position(screen);
moved_position = false;
}
}
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) {
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
@ -299,46 +310,130 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
case df::interface_key::CURSOR_DOWNRIGHT_FAST:
is_motion = true;
break;
default:
is_motion = false;
}
if (is_motion && !sticky_moved)
if (is_motion && !moved_position)
{
sticky_save(this);
sticky_moved = true;
save_position(screen);
moved_position = true;
}
}
};
};
class embark_tools_settings : public dfhack_viewscreen
{
public:
embark_tools_settings () { };
~embark_tools_settings () { };
void help () { };
std::string getFocusString () { return "embark-tools/options"; };
void render ()
{
parent->render();
int x, y;
auto dim = Screen::getWindowSize();
int width = 50,
height = 4 + 1 + tools.size(), // Padding + lower row
min_x = (dim.x - width) / 2,
max_x = (dim.x + width) / 2,
min_y = (dim.y - height) / 2,
max_y = min_y + height;
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
x = min_x + 2;
y = max_y - 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::SELECT));
OutputString(COLOR_WHITE, x, y, "/");
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
OutputString(COLOR_WHITE, x, y, ": Done");
y = min_y + 2;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* t = *iter;
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t->getToggleKey()));
OutputString(COLOR_WHITE, x, y, ": " + t->getName() +
(t->getEnabled() ? ": Enabled" : ": Disabled"));
y++;
}
};
void feed (std::set<df::interface_key> * input)
{
if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
{
Screen::dismiss(this);
return;
}
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* t = *iter;
if (t->getToggleKey() == key)
{
t->toggleEnabled();
}
}
}
};
};
if (tool_enabled("sand"))
bool tool_exists (std::string tool_name)
{
sand_dirty = true;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name)
return true;
}
if (!prevent_default)
INTERPOSE_NEXT(feed)(input);
return false;
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
bool tool_enabled (std::string tool_name)
{
FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name)
return tool->getEnabled();
}
return false;
}
bool tool_enable (std::string tool_name, bool enable_state)
{
if (tool_enabled("sticky") && sticky_moved)
int n = 0;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name || tool_name == "all")
{
sticky_apply(this);
sticky_moved = false;
tool->setEnabled(enable_state);
n++;
}
}
return (bool)n;
}
INTERPOSE_NEXT(render)();
struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
typedef df::viewscreen_choose_start_sitest interpose_base;
void display_tool_status()
{
auto dim = Screen::getWindowSize();
int x = 1,
y = dim.y - 5;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
OutputString(COLOR_WHITE, x, y, ": Enabled: ");
std::list<std::string> parts;
for (int i = 0; i < NUM_TOOLS; i++)
FOR_ITER_TOOLS(iter)
{
if (embark_tools[i].enabled)
EmbarkTool* tool = *iter;
if (tool->getEnabled())
{
parts.push_back(embark_tools[i].name);
parts.push_back(tool->getName());
parts.push_back(", ");
}
}
@ -354,97 +449,77 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
}
if (tool_enabled("anywhere"))
{
x = 20; y = dim.y - 2;
if (this->page >= 0 && this->page <= 4)
{
// Only display on five map pages, not on site finder or notes
OutputString(COLOR_WHITE, x, y, ": Embark!");
}
}
if (tool_enabled("sand"))
{
if (sand_dirty)
void display_settings()
{
sand_update(this);
Screen::show(new embark_tools_settings);
}
x = dim.x - 28; y = 13;
if (this->page == 0)
inline bool is_valid_page()
{
OutputString(COLOR_YELLOW, x, y, sand_indicator);
return (page >= 0 && page <= 4);
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
/*
* Tool management
*/
bool tool_exists (std::string tool_name)
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
{
for (int i = 0; i < NUM_TOOLS; i++)
bool cancel = false;
FOR_ITER_TOOLS(iter)
{
if (embark_tools[i].id == tool_name)
return true;
EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->before_feed(this, input, cancel);
}
return false;
}
bool tool_enabled (std::string tool_name)
{
for (int i = 0; i < NUM_TOOLS; i++)
if (cancel)
return;
INTERPOSE_NEXT(feed)(input);
if (input->count(df::interface_key::CUSTOM_S) && is_valid_page())
display_settings();
FOR_ITER_TOOLS(iter)
{
if (embark_tools[i].id == tool_name)
return embark_tools[i].enabled;
EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->after_feed(this, input);
}
return false;
}
bool tool_enable (std::string tool_name, bool enable_state)
{
int n = 0;
for (int i = 0; i < NUM_TOOLS; i++)
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
if (embark_tools[i].id == tool_name || tool_name == "all")
FOR_ITER_TOOLS(iter)
{
embark_tools[i].enabled = enable_state;
tool_update(tool_name);
n++;
}
}
return (bool)n;
EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->before_render(this);
}
void tool_update (std::string tool_name)
{
// Called whenever a tool is enabled/disabled
if (tool_name == "sand")
INTERPOSE_NEXT(render)();
display_tool_status();
FOR_ITER_TOOLS(iter)
{
sand_dirty = true;
EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->after_render(this);
}
}
/*
* Plugin management
*/
};
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
DFHACK_PLUGIN("embark-tools");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
tools.push_back(new EmbarkAnywhere);
tools.push_back(new NanoEmbark);
tools.push_back(new SandIndicator);
tools.push_back(new StablePosition);
std::string help = "";
help += "embark-tools (enable/disable) tool [tool...]\n"
"Tools:\n";
for (int i = 0; i < NUM_TOOLS; i++)
FOR_ITER_TOOLS(iter)
{
help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n");
help += (" " + (*iter)->getId() + ": " + (*iter)->getDesc() + "\n");
}
commands.push_back(PluginCommand(
"embark-tools",
@ -504,10 +579,11 @@ command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> &
if (is_enabled)
{
out << "Tool status:" << std::endl;
for (int i = 0; i < NUM_TOOLS; i++)
FOR_ITER_TOOLS(iter)
{
EmbarkTool t = embark_tools[i];
out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl;
EmbarkTool* t = *iter;
out << t->getName() << " (" << t->getId() << "): "
<< (t->getEnabled() ? "Enabled" : "Disabled") << std::endl;
}
}
else

@ -113,8 +113,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
action->data.move.timer = 1;
break;
case unit_action_type::Attack:
// Attacks are executed when timer1 reaches zero, which will be
// on the following tick.
action->data.attack.timer1 = 1;
action->data.attack.timer2 = 1;
// Attack actions are completed, and new ones generated, when
// timer2 reaches zero. If set to 1 this never seems to occur.
// Setting to zero makes next tick generate a new attack action
// every time, thereby allowing target enemy/body part re-selection
// take place.
action->data.attack.timer2 = 0;
break;
case unit_action_type::Hold:
action->data.hold.timer = 1;

@ -270,7 +270,7 @@ struct UnitInfo
enum altsort_mode {
ALTSORT_NAME,
ALTSORT_PROFESSION_OR_SQUAD,
ALTSORT_HAPPINESS,
ALTSORT_STRESS,
ALTSORT_ARRIVAL,
ALTSORT_MAX
};
@ -311,12 +311,17 @@ bool sortBySquad (const UnitInfo *d1, const UnitInfo *d2)
return descending ? gt : !gt;
}
bool sortByHappiness (const UnitInfo *d1, const UnitInfo *d2)
bool sortByStress (const UnitInfo *d1, const UnitInfo *d2)
{
if (!d1->unit->status.current_soul)
return !descending;
if (!d2->unit->status.current_soul)
return descending;
if (descending)
return (d1->unit->status.happiness > d2->unit->status.happiness);
return (d1->unit->status.current_soul->personality.stress_level > d2->unit->status.current_soul->personality.stress_level);
else
return (d1->unit->status.happiness < d2->unit->status.happiness);
return (d1->unit->status.current_soul->personality.stress_level < d2->unit->status.current_soul->personality.stress_level);
}
bool sortByArrival (const UnitInfo *d1, const UnitInfo *d2)
@ -363,11 +368,11 @@ bool sortBySkill (const UnitInfo *d1, const UnitInfo *d2)
else
return d1->unit->status.labors[sort_labor] < d2->unit->status.labors[sort_labor];
}
return sortByName(d1, d2);
return false;
}
enum display_columns {
DISP_COLUMN_HAPPINESS,
DISP_COLUMN_STRESS,
DISP_COLUMN_NAME,
DISP_COLUMN_PROFESSION_OR_SQUAD,
DISP_COLUMN_LABORS,
@ -509,13 +514,13 @@ void viewscreen_unitlaborsst::calcSize()
// min/max width of columns
int col_minwidth[DISP_COLUMN_MAX];
int col_maxwidth[DISP_COLUMN_MAX];
col_minwidth[DISP_COLUMN_HAPPINESS] = 4;
col_maxwidth[DISP_COLUMN_HAPPINESS] = 4;
col_minwidth[DISP_COLUMN_STRESS] = 6;
col_maxwidth[DISP_COLUMN_STRESS] = 6;
col_minwidth[DISP_COLUMN_NAME] = 16;
col_maxwidth[DISP_COLUMN_NAME] = 16; // adjusted in the loop below
col_minwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = 10;
col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = 10; // adjusted in the loop below
col_minwidth[DISP_COLUMN_LABORS] = num_columns*3/5; // 60%
col_minwidth[DISP_COLUMN_LABORS] = 1;
col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS;
// get max_name/max_prof from strings length
@ -778,10 +783,10 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
switch (click_header)
{
case DISP_COLUMN_HAPPINESS:
case DISP_COLUMN_STRESS:
if (enabler->mouse_lbut || enabler->mouse_rbut)
{
input_sort = ALTSORT_HAPPINESS;
input_sort = ALTSORT_STRESS;
if (enabler->mouse_lbut)
events->insert(interface_key::SECONDSCROLL_PAGEUP);
if (enabler->mouse_rbut)
@ -825,7 +830,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
switch (click_body)
{
case DISP_COLUMN_HAPPINESS:
case DISP_COLUMN_STRESS:
// do nothing
break;
@ -917,7 +922,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
descending = events->count(interface_key::SECONDSCROLL_UP);
sort_skill = columns[input_column].skill;
sort_labor = columns[input_column].labor;
std::sort(units.begin(), units.end(), sortBySkill);
std::stable_sort(units.begin(), units.end(), sortBySkill);
}
if (events->count(interface_key::SECONDSCROLL_PAGEUP) || events->count(interface_key::SECONDSCROLL_PAGEDOWN))
@ -926,16 +931,16 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
switch (input_sort)
{
case ALTSORT_NAME:
std::sort(units.begin(), units.end(), sortByName);
std::stable_sort(units.begin(), units.end(), sortByName);
break;
case ALTSORT_PROFESSION_OR_SQUAD:
std::sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession);
std::stable_sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession);
break;
case ALTSORT_HAPPINESS:
std::sort(units.begin(), units.end(), sortByHappiness);
case ALTSORT_STRESS:
std::stable_sort(units.begin(), units.end(), sortByStress);
break;
case ALTSORT_ARRIVAL:
std::sort(units.begin(), units.end(), sortByArrival);
std::stable_sort(units.begin(), units.end(), sortByArrival);
break;
}
}
@ -947,9 +952,9 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
altsort = ALTSORT_PROFESSION_OR_SQUAD;
break;
case ALTSORT_PROFESSION_OR_SQUAD:
altsort = ALTSORT_HAPPINESS;
altsort = ALTSORT_STRESS;
break;
case ALTSORT_HAPPINESS:
case ALTSORT_STRESS:
altsort = ALTSORT_ARRIVAL;
break;
case ALTSORT_ARRIVAL:
@ -1000,7 +1005,7 @@ void viewscreen_unitlaborsst::render()
Screen::clear();
Screen::drawBorder(" Dwarf Manipulator - Manage Labors ");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_HAPPINESS], 2, "Hap.");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_STRESS], 2, "Stress");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_NAME], 2, "Name");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_PROFESSION_OR_SQUAD], 2, show_squad ? "Squad" : "Profession");
@ -1043,23 +1048,22 @@ void viewscreen_unitlaborsst::render()
df::unit *unit = cur->unit;
int8_t fg = 15, bg = 0;
int happy = cur->unit->status.happiness;
string happiness = stl_sprintf("%4i", happy);
if (happy == 0) // miserable
int stress_lvl = unit->status.current_soul ? unit->status.current_soul->personality.stress_level : 0;
// cap at 6 digits
if (stress_lvl < -99999) stress_lvl = -99999;
if (stress_lvl > 999999) stress_lvl = 999999;
string stress = stl_sprintf("%6i", stress_lvl);
if (stress_lvl >= 500000)
fg = 13; // 5:1
else if (happy <= 25) // very unhappy
else if (stress_lvl >= 250000)
fg = 12; // 4:1
else if (happy <= 50) // unhappy
fg = 4; // 4:0
else if (happy < 75) // fine
else if (stress_lvl >= 100000)
fg = 14; // 6:1
else if (happy < 125) // quite content
fg = 6; // 6:0
else if (happy < 150) // happy
else if (stress_lvl >= 0)
fg = 2; // 2:0
else // ecstatic
else
fg = 10; // 2:1
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_HAPPINESS], 4 + row, happiness);
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_STRESS], 4 + row, stress);
fg = 15;
if (row_offset == sel_row)
@ -1232,8 +1236,8 @@ void viewscreen_unitlaborsst::render()
case ALTSORT_PROFESSION_OR_SQUAD:
OutputString(15, x, y, show_squad ? "Squad" : "Profession");
break;
case ALTSORT_HAPPINESS:
OutputString(15, x, y, "Happiness");
case ALTSORT_STRESS:
OutputString(15, x, y, "Stress Level");
break;
case ALTSORT_ARRIVAL:
OutputString(15, x, y, "Arrival");

@ -673,6 +673,8 @@ void lightingEngineViewscreen::doOcupancyAndLights()
}
//df::tile_occupancy o = b->OccupancyAt(gpos);
df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type);
bool is_wall=!ENUM_ATTR(tiletype_shape,passable_high,shape);
bool is_floor=!ENUM_ATTR(tiletype_shape,passable_low,shape);
df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape);
df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type);
@ -685,7 +687,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
{
curCell=rgbf(0,0,0);
}
else if(shape==df::tiletype_shape::WALL)
else if(is_wall)
{
if(tileMat==df::tiletype_material::FROZEN_LIQUID)
applyMaterial(tile,matIce);
@ -700,8 +702,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
{
applyMaterial(tile,matLava,(float)d.bits.flow_size/7.0f,(float)d.bits.flow_size/7.0f);
}
else if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP
|| shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN)
else if(!is_floor)
{
if(bDown)
{
@ -749,20 +750,6 @@ void lightingEngineViewscreen::doOcupancyAndLights()
}
}
//plants
for(int i=0;i<block->plants.size();i++)
{
df::plant* cPlant=block->plants[i];
if (cPlant->grow_counter <180000) //todo maybe smaller light/oclusion?
continue;
df::coord2d pos=cPlant->pos;
pos=worldToViewportCoord(pos,vp,window2d);
int tile=getIndex(pos.x,pos.y);
if(isInRect(pos,vp))
{
applyMaterial(tile,419,cPlant->material);
}
}
//blood and other goo
for(int i=0;i<block->block_events.size();i++)
{

@ -595,6 +595,9 @@ sub get_field_align {
$al = get_field_align($tg);
} elsif ($meta eq 'bytes') {
$al = $field->getAttribute('alignment') || 1;
} elsif ($meta eq 'primitive') {
my $subtype = $field->getAttribute('ld:subtype');
if ($subtype eq 'stl-fstream' and $os eq 'windows') { $al = 8; }
}
return $al;
@ -735,6 +738,14 @@ sub sizeof {
print "sizeof stl-string on $os\n";
}
print "sizeof stl-string\n";
} elsif ($subtype eq 'stl-fstream') { if ($os eq 'linux') {
return 284;
} elsif ($os eq 'windows') {
return 184;
} else {
print "sizeof stl-fstream on $os\n";
}
print "sizeof stl-fstream\n";
} else {
print "sizeof primitive $subtype\n";
}
@ -1011,6 +1022,7 @@ sub render_item_primitive {
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
push @lines_rb, "stl_string";
} elsif ($subtype eq 'stl-fstream') {
} else {
print "no render primitive $subtype\n";
}

@ -543,6 +543,18 @@ static VALUE rb_dfprint_str(VALUE self, VALUE s)
return Qnil;
}
static VALUE rb_dfprint_color(VALUE self, VALUE c, VALUE s)
{
if (r_console) {
color_value old_col = r_console->color();
r_console->color(color_value(rb_num2ulong(c)));
r_console->print("%s", rb_string_value_ptr(&s));
r_console->color(old_col);
} else
console_proxy->print("%s", rb_string_value_ptr(&s));
return Qnil;
}
static VALUE rb_dfprint_err(VALUE self, VALUE s)
{
printerr("%s", rb_string_value_ptr(&s));
@ -1072,6 +1084,7 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1);
rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1);
rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1);
rb_define_singleton_method(rb_cDFHack, "print_color", RUBY_METHOD_FUNC(rb_dfprint_color), 2);
rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1);
rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);
rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1);

@ -179,7 +179,7 @@ module DFHack
if not onlylastpart
out << name.first_name if name.first_name != ''
if name.nickname != ''
case respond_to?(:d_init) && d_init.nickname_dwarf
case respond_to?(:d_init) && d_init.nickname[gametype]
when :REPLACE_ALL; return "`#{name.nickname}'"
when :REPLACE_FIRST; out.pop
end

@ -1 +1 @@
Subproject commit 53159a11598bab7c6728b462e2ff9d34c59f1045
Subproject commit e579cac9fef8f89f382d8b9e832abe2392379897

@ -631,7 +631,11 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
// If no mood type was specified, pick one randomly
if (type == mood_type::None)
{
if (rng.df_trandom(100) > unit->status.happiness)
if (soul && (
(soul->personality.stress_level >= 500000) ||
(soul->personality.stress_level >= 250000 && !rng.df_trandom(2)) ||
(soul->personality.stress_level >= 100000 && !rng.df_trandom(10))
))
{
switch (rng.df_trandom(2))
{
@ -691,7 +695,6 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
unit->relations.mood_copy = unit->mood;
Gui::showAutoAnnouncement(announcement_type::STRANGE_MOOD, unit->pos, msg, color, bright);
unit->status.happiness = 100;
// TODO: make sure unit drops any wrestle items
unit->job.mood_timeout = 50000;
unit->flags1.bits.has_mood = true;
@ -1144,7 +1147,7 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
{
if ((job->job_type == job_type::StrangeMoodBrooding) && (rng.df_trandom(2)))
{
switch (rng.df_trandom(3))
switch (rng.df_trandom(2))
{
case 0:
job->job_items.push_back(item = new df::job_item());
@ -1159,10 +1162,6 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
item->flags2.bits.body_part = true;
item->quantity = 1;
break;
case 2:
// in older versions, they would request additional skulls
// in 0.34.11, the request becomes "nothing"
break;
}
}
else

@ -0,0 +1,301 @@
/*
* Trackstop plugin.
* Shows track stop friction and direction in its 'q' menu.
*/
#include "uicommon.h"
#include "LuaTools.h"
#include "df/building_rollersst.h"
#include "df/building_trapst.h"
#include "df/viewscreen_dwarfmodest.h"
#include "modules/Gui.h"
using namespace DFHack;
using namespace std;
using df::global::world;
using df::global::ui;
using df::building_rollersst;
using df::building_trapst;
using df::enums::trap_type::trap_type;
using df::enums::screw_pump_direction::screw_pump_direction;
DFHACK_PLUGIN("trackstop");
#define AUTOENABLE false
DFHACK_PLUGIN_IS_ENABLED(enabled);
/*
* Interface hooks
*/
struct trackstop_hook : public df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
enum Friction {
Lowest = 10,
Low = 50,
Medium = 500,
High = 10000,
Highest = 50000
};
building_trapst *get_selected_trackstop() {
if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
return nullptr;
}
building_trapst *ts = virtual_cast<building_trapst>(world->selected_building);
if (ts && ts->trap_type == df::trap_type::TrackStop && ts->construction_stage) {
return ts;
}
return nullptr;
}
bool handleInput(set<df::interface_key> *input) {
building_trapst *ts = get_selected_trackstop();
if (!ts) {
return false;
}
if (input->count(interface_key::BUILDING_TRACK_STOP_DUMP)) {
// Change track stop dump direction.
// There might be a more elegant way to do this.
if (!ts->use_dump) {
// No -> North
ts->use_dump = 1;
ts->dump_x_shift = 0;
ts->dump_y_shift = -1;
} else if (ts->dump_x_shift == 0 && ts->dump_y_shift == -1) {
// North -> South
ts->dump_x_shift = 0;
ts->dump_y_shift = 1;
} else if (ts->dump_x_shift == 0 && ts->dump_y_shift == 1) {
// South -> East
ts->dump_x_shift = 1;
ts->dump_y_shift = 0;
} else if (ts->dump_x_shift == 1 && ts->dump_y_shift == 0) {
// East -> West
ts->dump_x_shift = -1;
ts->dump_y_shift = 0;
} else {
// West (or Elsewhere) -> No
ts->use_dump = 0;
ts->dump_x_shift = 0;
ts->dump_y_shift = 0;
}
return true;
} else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_UP)) {
ts->friction = (
(ts->friction < Friction::Lowest)? Friction::Lowest:
(ts->friction < Friction::Low)? Friction::Low:
(ts->friction < Friction::Medium)? Friction::Medium:
(ts->friction < Friction::High)? Friction::High:
(ts->friction < Friction::Highest)? Friction::Highest:
ts->friction
);
return true;
} else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN)) {
ts->friction = (
(ts->friction > Friction::Highest)? Friction::Highest:
(ts->friction > Friction::High)? Friction::High:
(ts->friction > Friction::Medium)? Friction::Medium:
(ts->friction > Friction::Low)? Friction::Low:
(ts->friction > Friction::Lowest)? Friction::Lowest:
ts->friction
);
return true;
}
return false;
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input)) {
if (!handleInput(input)) {
INTERPOSE_NEXT(feed)(input);
}
}
DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
INTERPOSE_NEXT(render)();
building_trapst *ts = get_selected_trackstop();
if (ts) {
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y1 + 1;
OutputString(COLOR_WHITE, x, y, "Track Stop", true, left_margin);
y += 3;
OutputString(COLOR_WHITE, x, y, "Friction: ", false);
OutputString(COLOR_WHITE, x, y, (
(ts->friction <= Friction::Lowest)? "Lowest":
(ts->friction <= Friction::Low)? "Low":
(ts->friction <= Friction::Medium)? "Medium":
(ts->friction <= Friction::High)? "High":
"Highest"
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN));
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_UP));
OutputString(COLOR_WHITE, x, y, ": Change Friction", true, left_margin);
y += 1;
OutputString(COLOR_WHITE, x, y, "Dump on arrival: ", false);
OutputString(COLOR_WHITE, x, y, (
(!ts->use_dump)? "No":
(ts->dump_x_shift == 0 && ts->dump_y_shift == -1)? "North":
(ts->dump_x_shift == 0 && ts->dump_y_shift == 1)? "South":
(ts->dump_x_shift == 1 && ts->dump_y_shift == 0)? "East":
(ts->dump_x_shift == -1 && ts->dump_y_shift == 0)? "West":
"Elsewhere"
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_DUMP));
OutputString(COLOR_WHITE, x, y, ": Activate/change direction", true, left_margin);
}
}
};
struct roller_hook : public df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
enum Speed {
Lowest = 10000,
Low = 20000,
Medium = 30000,
High = 40000,
Highest = 50000
};
building_rollersst *get_selected_roller() {
if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
return nullptr;
}
building_rollersst *roller = virtual_cast<building_rollersst>(world->selected_building);
if (roller && roller->construction_stage) {
return roller;
}
return nullptr;
}
bool handleInput(set<df::interface_key> *input) {
building_rollersst *roller = get_selected_roller();
if (!roller) {
return false;
}
if (input->count(interface_key::BUILDING_ORIENT_NONE)) {
// Flip roller orientation.
// Long rollers can only be oriented along their length.
// Todo: Only add 1 to 1x1 rollers: x ^= ((x&1)<<1)|1
// Todo: This could have been elegant without all the casting,
// but as an enum it might be better off listing each case.
roller->direction = (df::enums::screw_pump_direction::screw_pump_direction)(((int8_t)roller->direction) ^ 2);
return true;
} else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_UP)) {
if (roller->speed < Speed::Highest) {
roller->speed += Speed::Lowest;
}
return true;
} else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_DOWN)) {
if (roller->speed > Speed::Lowest) {
roller->speed -= Speed::Lowest;
}
return true;
}
return false;
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input)) {
if (!handleInput(input)) {
INTERPOSE_NEXT(feed)(input);
}
}
DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
INTERPOSE_NEXT(render)();
building_rollersst *roller = get_selected_roller();
if (roller) {
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y1 + 6;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ORIENT_NONE));
OutputString(COLOR_WHITE, x, y, ": Rolls ", false);
OutputString(COLOR_WHITE, x, y, (
(roller->direction == df::screw_pump_direction::FromNorth)? "Southward":
(roller->direction == df::screw_pump_direction::FromEast)? "Westward":
(roller->direction == df::screw_pump_direction::FromSouth)? "Northward":
(roller->direction == df::screw_pump_direction::FromWest)? "Eastward":
""
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_DOWN));
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_UP));
OutputString(COLOR_WHITE, x, y, ": ");
OutputString(COLOR_WHITE, x, y, (
(roller->speed <= Speed::Lowest)? "Lowest":
(roller->speed <= Speed::Low)? "Low":
(roller->speed <= Speed::Medium)? "Medium":
(roller->speed <= Speed::High)? "High":
"Highest"
));
OutputString(COLOR_WHITE, x, y, " Speed", true, left_margin);
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, render);
IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, render);
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
// Accept the "enable trackstop" / "disable trackstop" commands.
if (enable != enabled) {
// Check for global variables that, if missing, result in total failure.
// Missing enabler and ui_menu_width also produce visible effects, but not nearly as severe.
// This could be moved to the plugin_init step, but that's louder for no real benefit.
if (!(gps && ui && world)) {
out.printerr("trackstop: Missing required global variables.\n");
return CR_FAILURE;
}
if (!INTERPOSE_HOOK(trackstop_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(trackstop_hook, render).apply(enable) ||
!INTERPOSE_HOOK(roller_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(roller_hook, render).apply(enable)) {
out.printerr("Could not %s trackstop hooks!\n", enable? "insert": "remove");
return CR_FAILURE;
}
enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
return plugin_enable(out, AUTOENABLE);
}
DFhackCExport command_result plugin_shutdown(color_ostream &out) {
return plugin_enable(out, false);
}

@ -0,0 +1,6 @@
PROJECT (tweak)
SET(PROJECT_SRCS
tweak.cpp
)
DFHACK_PLUGIN(tweak ${PROJECT_SRCS})

@ -12,11 +12,13 @@
#include "modules/Items.h"
#include "modules/Job.h"
#include "modules/Materials.h"
#include "modules/MapCache.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include <VTableInterpose.h>
#include "../uicommon.h"
#include "df/ui.h"
#include "df/world.h"
#include "df/squad.h"
@ -68,6 +70,20 @@
//#include "df/building_hivest.h"
#include <stdlib.h>
#include <unordered_map>
#include "tweaks/adamantine-cloth-wear.h"
#include "tweaks/advmode-contained.h"
#include "tweaks/civ-agreement-ui.h"
#include "tweaks/craft-age-wear.h"
#include "tweaks/farm-plot-select.h"
#include "tweaks/fast-heat.h"
#include "tweaks/fast-trade.h"
#include "tweaks/import-priority-category.h"
#include "tweaks/manager-quantity.h"
#include "tweaks/military-assign.h"
#include "tweaks/nestbox-color.h"
#include "tweaks/stable-cursor.h"
using std::set;
using std::vector;
@ -83,12 +99,15 @@ using df::global::ui_menu_width;
using df::global::ui_area_map_width;
using namespace DFHack::Gui;
using Screen::Pen;
static command_result tweak(color_ostream &out, vector <string> & parameters);
static std::multimap<std::string, VMethodInterposeLinkBase> tweak_hooks;
DFHACK_PLUGIN("tweak");
#define TWEAK_HOOK(tweak, cls, func) tweak_hooks.insert(std::pair<std::string, VMethodInterposeLinkBase>\
(tweak, INTERPOSE_HOOK(cls, func)))
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
@ -115,42 +134,81 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" tweak stable-cursor [disable]\n"
" Keeps exact position of dwarfmode cursor during exits to main menu.\n"
" E.g. allows switching between t/q/k/d without losing position.\n"
" tweak patrol-duty [disable]\n"
" Causes 'Train' orders to no longer be considered 'patrol duty' so\n"
" soldiers will stop getting unhappy thoughts. Does NOT fix the problem\n"
" when soldiers go off-duty (i.e. civilian).\n"
" tweak confirm-embark [disable]\n"
" Asks for confirmation on the embark setup screen before embarking\n"
" tweak stable-temp [disable]\n"
" Fixes performance bug 6012 by squashing jitter in temperature updates.\n"
" tweak fast-heat <max-ticks>\n"
" Further improves temperature updates by ensuring that 1 degree of\n"
" item temperature is crossed in no more than specified number of frames\n"
" when updating from the environment temperature. Use 0 to disable.\n"
/*" tweak fix-dimensions [disable]\n"
" Fixes subtracting small amount of thread/cloth/liquid from a stack\n"
" by splitting the stack and subtracting from the remaining single item.\n"*/
" tweak adamantine-cloth-wear [disable]\n"
" Stops adamantine clothing from wearing out while being worn (bug 6481).\n"
" tweak advmode-contained [disable]\n"
" Fixes custom reactions with container inputs in advmode. The issue is\n"
" that the screen tries to force you to select the contents separately\n"
" from the container. This forcefully skips child reagents.\n"
" tweak civ-view-agreement\n"
" Fixes overlapping text on the \"view agreement\" screen\n"
" tweak craft-age-wear [disable]\n"
" Makes cloth and leather items wear out at the correct rate (bug 6003).\n"
" tweak farm-plot-select [disable]\n"
" Adds \"Select all\" and \"Deselect all\" options to farm plot menus\n"
" tweak fast-heat <max-ticks>\n"
" Further improves temperature updates by ensuring that 1 degree of\n"
" item temperature is crossed in no more than specified number of frames\n"
" when updating from the environment temperature. Use 0 to disable.\n"
" tweak fast-trade [disable]\n"
" Makes Shift-Enter in the Move Goods to Depot and Trade screens select\n"
" the current item (fully, in case of a stack), and scroll down one line.\n"
" tweak import-priority-category [disable]\n"
" When meeting with a liaison, makes Shift+Left/Right arrow adjust\n"
" the priority of an entire category of imports.\n"
" tweak manager-quantity [disable]\n"
" Removes the limit of 30 jobs per manager order\n"
" tweak nestbox-color [disable]\n"
" Makes built nestboxes use the color of their material\n"
" tweak military-color-assigned [disable]\n"
" Color squad candidates already assigned to other squads in brown/green\n"
" to make them stand out more in the list.\n"
" tweak military-stable-assign [disable]\n"
" Preserve list order and cursor position when assigning to squad,\n"
" i.e. stop the rightmost list of the Positions page of the military\n"
" screen from constantly jumping to the top.\n"
" tweak military-color-assigned [disable]\n"
" Color squad candidates already assigned to other squads in brown/green\n"
" to make them stand out more in the list.\n"
// " tweak military-training [disable]\n"
// " Speed up melee squad training, removing inverse dependency on unit count.\n"
" tweak craft-age-wear [disable]\n"
" Makes cloth and leather items wear out at the correct rate (bug 6003).\n"
" tweak adamantine-cloth-wear [disable]\n"
" Stops adamantine clothing from wearing out while being worn (bug 6481).\n"
));
TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_armor_hook, incWearTimer);
TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_helm_hook, incWearTimer);
TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_gloves_hook, incWearTimer);
TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_shoes_hook, incWearTimer);
TWEAK_HOOK("adamantine-cloth-wear", adamantine_cloth_wear_pants_hook, incWearTimer);
TWEAK_HOOK("advmode-contained", advmode_contained_hook, feed);
TWEAK_HOOK("civ-view-agreement", civ_agreement_view_hook, render);
TWEAK_HOOK("craft-age-wear", craft_age_wear_hook, ageItem);
TWEAK_HOOK("farm-plot-select", farm_select_hook, feed);
TWEAK_HOOK("farm-plot-select", farm_select_hook, render);
TWEAK_HOOK("fast-heat", fast_heat_hook, updateTempFromMap);
TWEAK_HOOK("fast-heat", fast_heat_hook, updateTemperature);
TWEAK_HOOK("fast-heat", fast_heat_hook, adjustTemperature);
TWEAK_HOOK("fast-trade", fast_trade_assign_hook, feed);
TWEAK_HOOK("fast-trade", fast_trade_select_hook, feed);
TWEAK_HOOK("import-priority-category", takerequest_hook, feed);
TWEAK_HOOK("import-priority-category", takerequest_hook, render);
TWEAK_HOOK("manager-quantity", manager_quantity_hook, feed);
TWEAK_HOOK("military-color-assigned", military_assign_hook, render);
TWEAK_HOOK("military-stable-assign", military_assign_hook, feed);
TWEAK_HOOK("nestbox-color", nestbox_color_hook, drawBuilding);
TWEAK_HOOK("stable-cursor", stable_cursor_hook, feed);
return CR_OK;
}
@ -216,271 +274,6 @@ command_result fix_clothing_ownership(color_ostream &out, df::unit* unit)
return CR_OK;
}
/*
* Save or restore cursor position on change to/from main dwarfmode menu.
*/
static df::coord last_view, last_cursor;
struct stable_cursor_hook : df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool check_default()
{
switch (ui->main.mode) {
case ui_sidebar_mode::Default:
return true;
case ui_sidebar_mode::Build:
return ui_build_selector &&
(ui_build_selector->building_type < 0 ||
ui_build_selector->stage < 1);
default:
return false;
}
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
bool was_default = check_default();
df::coord view = Gui::getViewportPos();
df::coord cursor = Gui::getCursorPos();
INTERPOSE_NEXT(feed)(input);
bool is_default = check_default();
df::coord cur_cursor = Gui::getCursorPos();
if (is_default && !was_default)
{
last_view = view; last_cursor = cursor;
}
else if (!is_default && was_default &&
Gui::getViewportPos() == last_view &&
last_cursor.isValid() && cur_cursor.isValid())
{
Gui::setCursorCoords(last_cursor.x, last_cursor.y, last_cursor.z);
// Force update of ui state
set<df::interface_key> tmp;
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_UP_Z);
else
tmp.insert(interface_key::CURSOR_DOWN_Z);
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear();
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_DOWN_Z);
else
tmp.insert(interface_key::CURSOR_UP_Z);
INTERPOSE_NEXT(feed)(&tmp);
}
else if (!is_default && cur_cursor.isValid())
{
last_cursor = df::coord();
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(stable_cursor_hook, feed);
struct patrol_duty_hook : df::squad_order_trainst
{
typedef df::squad_order_trainst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, isPatrol, ())
{
return false;
}
};
IMPLEMENT_VMETHOD_INTERPOSE(patrol_duty_hook, isPatrol);
enum confirm_embark_states
{
ECS_INACTIVE = 0,
ECS_CONFIRM,
ECS_ACCEPTED
};
static confirm_embark_states confirm_embark_state = ECS_INACTIVE;
struct confirm_embark_hook : df::viewscreen_setupdwarfgamest
{
typedef df::viewscreen_setupdwarfgamest interpose_base;
void OutputString(int8_t fg, int &x, int y, std::string text)
{
Screen::paintString(Screen::Pen(' ', fg, COLOR_BLACK), x, y, text);
x += text.length();
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
bool intercept = false;
if (this->show_play_now == 0)
{
if (confirm_embark_state == ECS_INACTIVE)
{
if (input->count(df::interface_key::SETUP_EMBARK))
{
confirm_embark_state = ECS_CONFIRM;
intercept = true;
}
}
else if (confirm_embark_state == ECS_CONFIRM)
{
intercept = true;
if (input->count(df::interface_key::MENU_CONFIRM))
confirm_embark_state = ECS_ACCEPTED;
else if (input->size())
confirm_embark_state = ECS_INACTIVE;
}
}
if (!intercept)
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key))
{
if (confirm_embark_state == ECS_CONFIRM)
{
if (key == df::interface_key::OPTIONS)
return true;
}
return INTERPOSE_NEXT(key_conflict)(key);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
auto dim = Screen::getWindowSize();
int x = 0, y = 0;
if (confirm_embark_state != ECS_INACTIVE)
{
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), 0, 0, dim.x - 1, dim.y - 1);
}
if (confirm_embark_state == ECS_CONFIRM)
{
x = 2, y = 2;
OutputString(COLOR_WHITE, x, y, "Really embark? (");
OutputString(COLOR_LIGHTGREEN, x, y, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM));
OutputString(COLOR_WHITE, x, y, " = yes, other = no)");
x = 2, y = 4;
int32_t points = this->points_remaining;
OutputString(COLOR_WHITE, x, y, "Points left: ");
OutputString((points ? COLOR_YELLOW : COLOR_LIGHTGREEN), x, y, std::to_string((unsigned long long/*won't compile on windows otherwise*/)points));
x = dim.x - 10, y = dim.y - 1;
OutputString(COLOR_WHITE, x, y, "DFHack");
}
else if (confirm_embark_state == ECS_ACCEPTED)
{
std::set<df::interface_key> input;
input.insert(df::interface_key::SETUP_EMBARK);
this->feed(&input);
confirm_embark_state = ECS_INACTIVE;
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, key_conflict);
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, render);
struct stable_temp_hook : df::item_actual {
typedef df::item_actual interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult))
{
if (temperature.whole != temp)
{
// Bug 6012 is caused by fixed-point precision mismatch jitter
// when an item is being pushed by two sources at N and N+1.
// This check suppresses it altogether.
if (temp == temperature.whole+1 ||
(temp == temperature.whole-1 && temperature.fraction == 0))
temp = temperature.whole;
// When SPEC_HEAT is NONE, the original function seems to not
// change the temperature, yet return true, which is silly.
else if (getSpecHeat() == 60001)
temp = temperature.whole;
}
return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult);
}
DEFINE_VMETHOD_INTERPOSE(bool, updateContaminants, ())
{
if (contaminants)
{
// Force 1-degree difference in contaminant temperature to 0
for (size_t i = 0; i < contaminants->size(); i++)
{
auto obj = (*contaminants)[i];
if (abs(obj->temperature.whole - temperature.whole) == 1)
{
obj->temperature.whole = temperature.whole;
obj->temperature.fraction = temperature.fraction;
}
}
}
return INTERPOSE_NEXT(updateContaminants)();
}
};
IMPLEMENT_VMETHOD_INTERPOSE(stable_temp_hook, adjustTemperature);
IMPLEMENT_VMETHOD_INTERPOSE(stable_temp_hook, updateContaminants);
static int map_temp_mult = -1;
static int max_heat_ticks = 0;
struct fast_heat_hook : df::item_actual {
typedef df::item_actual interpose_base;
DEFINE_VMETHOD_INTERPOSE(
bool, updateTempFromMap,
(bool local, bool contained, bool adjust, int32_t rate_mult)
) {
int cmult = map_temp_mult;
map_temp_mult = rate_mult;
bool rv = INTERPOSE_NEXT(updateTempFromMap)(local, contained, adjust, rate_mult);
map_temp_mult = cmult;
return rv;
}
DEFINE_VMETHOD_INTERPOSE(
bool, updateTemperature,
(uint16_t temp, bool local, bool contained, bool adjust, int32_t rate_mult)
) {
// Some items take ages to cross the last degree, so speed them up
if (map_temp_mult > 0 && temp != temperature.whole && max_heat_ticks > 0)
{
int spec = getSpecHeat();
if (spec != 60001)
rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature.whole));
}
return INTERPOSE_NEXT(updateTemperature)(temp, local, contained, adjust, rate_mult);
}
DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult))
{
if (map_temp_mult > 0)
rate_mult = map_temp_mult;
return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTempFromMap);
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTemperature);
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, adjustTemperature);
static void correct_dimension(df::item_actual *self, int32_t &delta, int32_t dim)
{
// Zero dimension or remainder?
@ -570,201 +363,7 @@ struct dimension_cloth_hook : df::item_clothst {
IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension);
struct advmode_contained_hook : df::viewscreen_layer_unit_actionst {
typedef df::viewscreen_layer_unit_actionst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
auto old_reaction = cur_reaction;
auto old_reagent = reagent;
INTERPOSE_NEXT(feed)(input);
if (cur_reaction && (cur_reaction != old_reaction || reagent != old_reagent))
{
old_reagent = reagent;
// Skip reagents already contained by others
while (reagent < (int)cur_reaction->reagents.size()-1)
{
if (!cur_reaction->reagents[reagent]->flags.bits.IN_CONTAINER)
break;
reagent++;
}
if (old_reagent != reagent)
{
// Reproduces a tiny part of the orginal screen code
choice_items.clear();
auto preagent = cur_reaction->reagents[reagent];
reagent_amnt_left = preagent->quantity;
for (int i = held_items.size()-1; i >= 0; i--)
{
if (!preagent->matchesRoot(held_items[i], cur_reaction->index))
continue;
if (linear_index(sel_items, held_items[i]) >= 0)
continue;
choice_items.push_back(held_items[i]);
}
layer_objects[6]->setListLength(choice_items.size());
if (!choice_items.empty())
{
layer_objects[4]->active = layer_objects[5]->active = false;
layer_objects[6]->active = true;
}
else if (layer_objects[6]->active)
{
layer_objects[6]->active = false;
layer_objects[5]->active = true;
}
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(advmode_contained_hook, feed);
struct fast_trade_assign_hook : df::viewscreen_layer_assigntradest {
typedef df::viewscreen_layer_assigntradest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (layer_objects[1]->active && input->count(interface_key::SELECT_ALL))
{
set<df::interface_key> tmp; tmp.insert(interface_key::SELECT);
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN);
INTERPOSE_NEXT(feed)(&tmp);
}
else
INTERPOSE_NEXT(feed)(input);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_assign_hook, feed);
struct fast_trade_select_hook : df::viewscreen_tradegoodsst {
typedef df::viewscreen_tradegoodsst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (!(is_unloading || !has_traders || in_edit_count)
&& input->count(interface_key::SELECT_ALL))
{
set<df::interface_key> tmp; tmp.insert(interface_key::SELECT);
INTERPOSE_NEXT(feed)(&tmp);
if (in_edit_count)
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN);
INTERPOSE_NEXT(feed)(&tmp);
}
else
INTERPOSE_NEXT(feed)(input);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_select_hook, feed);
struct military_assign_hook : df::viewscreen_layer_militaryst {
typedef df::viewscreen_layer_militaryst interpose_base;
inline bool inPositionsMode() {
return page == Positions && !(in_create_squad || in_new_squad);
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (inPositionsMode() && !layer_objects[0]->active)
{
auto pos_list = layer_objects[1];
auto plist = layer_objects[2];
auto &cand = positions.candidates;
// Save the candidate list and cursors
std::vector<df::unit*> copy = cand;
int cursor = plist->getListCursor();
int pos_cursor = pos_list->getListCursor();
INTERPOSE_NEXT(feed)(input);
if (inPositionsMode() && !layer_objects[0]->active)
{
bool is_select = input->count(interface_key::SELECT);
// Resort the candidate list and restore cursor
// on add to squad OR scroll in the position list.
if (!plist->active || is_select)
{
// Since we don't know the actual sorting order, preserve
// the ordering of the items in the list before keypress.
// This does the right thing even if the list was sorted
// with sort-units.
std::set<df::unit*> prev, next;
prev.insert(copy.begin(), copy.end());
next.insert(cand.begin(), cand.end());
std::vector<df::unit*> out;
// (old-before-cursor) (new) |cursor| (old-after-cursor)
for (int i = 0; i < cursor && i < (int)copy.size(); i++)
if (next.count(copy[i])) out.push_back(copy[i]);
for (size_t i = 0; i < cand.size(); i++)
if (!prev.count(cand[i])) out.push_back(cand[i]);
int new_cursor = out.size();
for (int i = cursor; i < (int)copy.size(); i++)
if (next.count(copy[i])) out.push_back(copy[i]);
cand.swap(out);
plist->setListLength(cand.size());
if (new_cursor < (int)cand.size())
plist->setListCursor(new_cursor);
}
// Preserve the position list index on remove from squad
if (pos_list->active && is_select)
pos_list->setListCursor(pos_cursor);
}
}
else
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (inPositionsMode())
{
auto plist = layer_objects[2];
int x1 = plist->getX1(), y1 = plist->getY1();
int x2 = plist->getX2(), y2 = plist->getY2();
int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible();
int si = plist->getListCursor();
for (int y = y1, i = i1; i <= i2; i++, y++)
{
auto unit = vector_get(positions.candidates, i);
if (!unit || unit->military.squad_id < 0)
continue;
for (int x = x1; x <= x2; x++)
{
Pen cur_tile = Screen::readTile(x, y);
if (!cur_tile.valid()) continue;
cur_tile.fg = (i == si) ? COLOR_BROWN : COLOR_GREEN;
Screen::paintTile(cur_tile, x, y);
}
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, render);
/*
// Unit updates are executed based on an action divisor variable,
// which is computed from the alive unit count and has range 10-100.
static int adjust_unit_divisor(int value) {
@ -943,6 +542,7 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
}
}
};
*/
/*
IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process);
@ -1007,104 +607,6 @@ struct military_training_id_hook : df::activity_event_individual_skill_drillst {
IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process);
*/
struct craft_age_wear_hook : df::item_crafted {
typedef df::item_crafted interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, ageItem, (int amount))
{
int orig_age = age;
age += amount;
if (age > 200000000)
age = 200000000;
if (age == orig_age)
return false;
MaterialInfo mat(mat_type, mat_index);
if (!mat.isValid())
return false;
int wear = 0;
if (mat.material->flags.is_set(material_flags::WOOD))
wear = 5;
else if (mat.material->flags.is_set(material_flags::LEATHER) ||
mat.material->flags.is_set(material_flags::THREAD_PLANT) ||
mat.material->flags.is_set(material_flags::SILK) ||
mat.material->flags.is_set(material_flags::YARN))
wear = 1;
else
return false;
wear = ((orig_age % wear) + (age - orig_age)) / wear;
if (wear > 0)
return incWearTimer(wear);
else
return false;
}
};
IMPLEMENT_VMETHOD_INTERPOSE(craft_age_wear_hook, ageItem);
static bool inc_wear_timer (df::item_constructed *item, int amount)
{
if (item->flags.bits.artifact)
return false;
MaterialInfo mat(item->mat_type, item->mat_index);
if (mat.isInorganic() && mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL))
return false;
item->wear_timer += amount;
return (item->wear_timer > 806400);
}
struct adamantine_cloth_wear_armor_hook : df::item_armorst {
typedef df::item_armorst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_armor_hook, incWearTimer);
struct adamantine_cloth_wear_helm_hook : df::item_helmst {
typedef df::item_helmst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_helm_hook, incWearTimer);
struct adamantine_cloth_wear_gloves_hook : df::item_glovesst {
typedef df::item_glovesst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_gloves_hook, incWearTimer);
struct adamantine_cloth_wear_shoes_hook : df::item_shoesst {
typedef df::item_shoesst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_shoes_hook, incWearTimer);
struct adamantine_cloth_wear_pants_hook : df::item_pantsst {
typedef df::item_pantsst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_pants_hook, incWearTimer);
static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector <string> &parameters)
{
if (vector_get(parameters, 1) == "disable")
@ -1121,6 +623,26 @@ static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vect
}
}
static command_result enable_tweak(string tweak, color_ostream &out, vector <string> &parameters)
{
bool recognized = false;
string cmd = parameters[0];
for (auto it = tweak_hooks.begin(); it != tweak_hooks.end(); ++it)
{
if (it->first == cmd)
{
recognized = true;
enable_hook(out, it->second, parameters);
}
}
if (!recognized)
{
out.printerr("Unrecognized tweak: %s\n", cmd.c_str());
return CR_WRONG_USAGE;
}
return CR_OK;
}
static command_result tweak(color_ostream &out, vector <string> &parameters)
{
CoreSuspender suspend;
@ -1219,25 +741,6 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
unit->profession2 = df::profession::TRADER;
return fix_clothing_ownership(out, unit);
}
else if (cmd == "stable-cursor")
{
enable_hook(out, INTERPOSE_HOOK(stable_cursor_hook, feed), parameters);
}
else if (cmd == "patrol-duty")
{
enable_hook(out, INTERPOSE_HOOK(patrol_duty_hook, isPatrol), parameters);
}
else if (cmd == "confirm-embark")
{
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, feed), parameters);
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, key_conflict), parameters);
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, render), parameters);
}
else if (cmd == "stable-temp")
{
enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, adjustTemperature), parameters);
enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, updateContaminants), parameters);
}
else if (cmd == "fast-heat")
{
if (parameters.size() < 2)
@ -1245,9 +748,8 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
max_heat_ticks = atoi(parameters[1].c_str());
if (max_heat_ticks <= 0)
parameters[1] = "disable";
enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, updateTempFromMap), parameters);
enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, updateTemperature), parameters);
enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, adjustTemperature), parameters);
enable_tweak(cmd, out, parameters);
return CR_OK;
}
/*else if (cmd == "fix-dimensions")
{
@ -1257,23 +759,6 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
enable_hook(out, INTERPOSE_HOOK(dimension_thread_hook, subtractDimension), parameters);
enable_hook(out, INTERPOSE_HOOK(dimension_cloth_hook, subtractDimension), parameters);
}*/
else if (cmd == "advmode-contained")
{
enable_hook(out, INTERPOSE_HOOK(advmode_contained_hook, feed), parameters);
}
else if (cmd == "fast-trade")
{
enable_hook(out, INTERPOSE_HOOK(fast_trade_assign_hook, feed), parameters);
enable_hook(out, INTERPOSE_HOOK(fast_trade_select_hook, feed), parameters);
}
else if (cmd == "military-stable-assign")
{
enable_hook(out, INTERPOSE_HOOK(military_assign_hook, feed), parameters);
}
else if (cmd == "military-color-assigned")
{
enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters);
}
/*
else if (cmd == "military-training")
{
@ -1282,20 +767,10 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters);
}*/
else if (cmd == "craft-age-wear")
{
enable_hook(out, INTERPOSE_HOOK(craft_age_wear_hook, ageItem), parameters);
}
else if (cmd == "adamantine-cloth-wear")
else
{
enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_armor_hook, incWearTimer), parameters);
enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_helm_hook, incWearTimer), parameters);
enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_gloves_hook, incWearTimer), parameters);
enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_shoes_hook, incWearTimer), parameters);
enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_pants_hook, incWearTimer), parameters);
return enable_tweak(cmd, out, parameters);
}
else
return CR_WRONG_USAGE;
return CR_OK;
}

@ -0,0 +1,65 @@
using namespace DFHack;
using namespace df::enums;
static bool inc_wear_timer (df::item_constructed *item, int amount)
{
if (item->flags.bits.artifact)
return false;
MaterialInfo mat(item->mat_type, item->mat_index);
if (mat.isInorganic() && mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL))
return false;
item->wear_timer += amount;
return (item->wear_timer > 806400);
}
struct adamantine_cloth_wear_armor_hook : df::item_armorst {
typedef df::item_armorst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_armor_hook, incWearTimer);
struct adamantine_cloth_wear_helm_hook : df::item_helmst {
typedef df::item_helmst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_helm_hook, incWearTimer);
struct adamantine_cloth_wear_gloves_hook : df::item_glovesst {
typedef df::item_glovesst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_gloves_hook, incWearTimer);
struct adamantine_cloth_wear_shoes_hook : df::item_shoesst {
typedef df::item_shoesst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_shoes_hook, incWearTimer);
struct adamantine_cloth_wear_pants_hook : df::item_pantsst {
typedef df::item_pantsst interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount))
{
return inc_wear_timer(this, amount);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_pants_hook, incWearTimer);

@ -0,0 +1,59 @@
using namespace std;
struct advmode_contained_hook : df::viewscreen_layer_unit_actionst {
typedef df::viewscreen_layer_unit_actionst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
auto old_reaction = cur_reaction;
auto old_reagent = reagent;
INTERPOSE_NEXT(feed)(input);
if (cur_reaction && (cur_reaction != old_reaction || reagent != old_reagent))
{
old_reagent = reagent;
// Skip reagents already contained by others
while (reagent < (int)cur_reaction->reagents.size()-1)
{
if (!cur_reaction->reagents[reagent]->flags.bits.IN_CONTAINER)
break;
reagent++;
}
if (old_reagent != reagent)
{
// Reproduces a tiny part of the orginal screen code
choice_items.clear();
auto preagent = cur_reaction->reagents[reagent];
reagent_amnt_left = preagent->quantity;
for (int i = held_items.size()-1; i >= 0; i--)
{
if (!preagent->matchesRoot(held_items[i], cur_reaction->index))
continue;
if (linear_index(sel_items, held_items[i]) >= 0)
continue;
choice_items.push_back(held_items[i]);
}
layer_objects[6]->setListLength(choice_items.size());
if (!choice_items.empty())
{
layer_objects[4]->active = layer_objects[5]->active = false;
layer_objects[6]->active = true;
}
else if (layer_objects[6]->active)
{
layer_objects[6]->active = false;
layer_objects[5]->active = true;
}
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(advmode_contained_hook, feed);

@ -0,0 +1,34 @@
#include "df/meeting_event.h"
#include "df/viewscreen_entityst.h"
using namespace std;
using namespace df::enums;
using namespace DFHack::Gui;
using namespace DFHack::Screen;
#define DLOG DFHack::Core::getInstance().getConsole().printerr
struct civ_agreement_view_hook : df::viewscreen_entityst {
typedef df::viewscreen_entityst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (page == 2)
{
fillRect(Pen(0, 0, ' '), 2, 22, 22, 22);
int x, y;
getWindowSize(x, y);
x = 2;
y -= 3;
OutputString(COLOR_LIGHTGREEN, x, y, getKeyDisplay(interface_key::CHANGETAB));
OutputString(COLOR_WHITE, x, y, " to change modes.");
x = 2;
y++;
OutputString(COLOR_LIGHTGREEN, x, y, getKeyDisplay(interface_key::SELECT));
OutputString(COLOR_WHITE, x, y, ": View agreement");
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(civ_agreement_view_hook, render);

@ -0,0 +1,34 @@
struct craft_age_wear_hook : df::item_crafted {
typedef df::item_crafted interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, ageItem, (int amount))
{
int orig_age = age;
age += amount;
if (age > 200000000)
age = 200000000;
if (age == orig_age)
return false;
MaterialInfo mat(mat_type, mat_index);
if (!mat.isValid())
return false;
int wear = 0;
if (mat.material->flags.is_set(material_flags::WOOD))
wear = 5;
else if (mat.material->flags.is_set(material_flags::LEATHER) ||
mat.material->flags.is_set(material_flags::THREAD_PLANT) ||
mat.material->flags.is_set(material_flags::SILK) ||
mat.material->flags.is_set(material_flags::YARN))
wear = 1;
else
return false;
wear = ((orig_age % wear) + (age - orig_age)) / wear;
if (wear > 0)
return incWearTimer(wear);
else
return false;
}
};
IMPLEMENT_VMETHOD_INTERPOSE(craft_age_wear_hook, ageItem);

@ -0,0 +1,96 @@
#include "BitArray.h"
#include "df/building_farmplotst.h"
#include "df/plant_raw.h"
using namespace df::enums;
using df::global::ui;
using df::global::ui_building_item_cursor;
using df::global::world;
struct farm_select_hook : df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
df::building_farmplotst* getFarmPlot()
{
if (ui->main.mode != ui_sidebar_mode::QueryBuilding)
return NULL;
VIRTUAL_CAST_VAR(farm_plot, df::building_farmplotst, world->selected_building);
return farm_plot;
}
bool isValidCrop (int32_t crop_id, int season, df::building_farmplotst* farm_plot)
{
// Adapted from autofarm
using namespace df::enums::plant_raw_flags;
// Discovered?
if (ui->tasks.discovered_plants[crop_id])
{
// Possible to plant?
df::plant_raw* raws = world->raws.plants.all[crop_id];
if (raws->flags.is_set(SEED) && raws->flags.is_set((df::plant_raw_flags)season))
{
// Right depth?
DFCoord cursor (farm_plot->centerx, farm_plot->centery, farm_plot->z);
MapExtras::MapCache mc;
MapExtras::Block * b = mc.BlockAt(cursor / 16);
if (!b || !b->is_valid())
return false;
auto &block = *b->getRaw();
df::tile_designation &des =
block.designation[farm_plot->centerx % 16][farm_plot->centery % 16];
if ((raws->underground_depth_min == 0 || raws->underground_depth_max == 0) != des.bits.subterranean)
{
return true;
}
}
}
return false;
}
inline int32_t getSelectedCropId() { return ui->selected_farm_crops[*ui_building_item_cursor]; }
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
df::building_farmplotst* farm_plot = getFarmPlot();
if (farm_plot)
{
if (input->count(interface_key::SELECT_ALL))
{
int32_t crop_id = getSelectedCropId();
for (int season = 0; season < 4; season++)
{
if (isValidCrop(crop_id, season, farm_plot))
{
farm_plot->plant_id[season] = crop_id;
}
}
}
else if (input->count(interface_key::DESELECT_ALL))
{
for (int season = 0; season < 4; season++)
{
farm_plot->plant_id[season] = -1;
}
}
}
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (!getFarmPlot())
return;
auto dims = Gui::getDwarfmodeViewDims();
int x = dims.menu_x1 + 1,
y = dims.y2 - 5,
left = x;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::SELECT_ALL));
OutputString(COLOR_WHITE, x, y, ": All seasons", true, left);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::DESELECT_ALL));
OutputString(COLOR_WHITE, x, y, ": Fallow all seasons", true, left);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(farm_select_hook, render);
IMPLEMENT_VMETHOD_INTERPOSE(farm_select_hook, feed);

@ -0,0 +1,48 @@
using namespace df::enums;
static int map_temp_mult = -1;
static int max_heat_ticks = 0;
struct fast_heat_hook : df::item_actual {
typedef df::item_actual interpose_base;
DEFINE_VMETHOD_INTERPOSE(
bool, updateTempFromMap,
(bool local, bool contained, bool adjust, int32_t rate_mult)
) {
int cmult = map_temp_mult;
map_temp_mult = rate_mult;
bool rv = INTERPOSE_NEXT(updateTempFromMap)(local, contained, adjust, rate_mult);
map_temp_mult = cmult;
return rv;
}
DEFINE_VMETHOD_INTERPOSE(
bool, updateTemperature,
(uint16_t temp, bool local, bool contained, bool adjust, int32_t rate_mult)
) {
// Some items take ages to cross the last degree, so speed them up
if (map_temp_mult > 0 && temp != temperature.whole && max_heat_ticks > 0)
{
int spec = getSpecHeat();
if (spec != 60001)
rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature.whole));
}
return INTERPOSE_NEXT(updateTemperature)(temp, local, contained, adjust, rate_mult);
}
DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult))
{
if (map_temp_mult > 0)
rate_mult = map_temp_mult;
return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTempFromMap);
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTemperature);
IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, adjustTemperature);

@ -0,0 +1,43 @@
using namespace std;
struct fast_trade_assign_hook : df::viewscreen_layer_assigntradest {
typedef df::viewscreen_layer_assigntradest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (layer_objects[1]->active && input->count(interface_key::SELECT_ALL))
{
set<df::interface_key> tmp; tmp.insert(interface_key::SELECT);
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN);
INTERPOSE_NEXT(feed)(&tmp);
}
else
INTERPOSE_NEXT(feed)(input);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_assign_hook, feed);
struct fast_trade_select_hook : df::viewscreen_tradegoodsst {
typedef df::viewscreen_tradegoodsst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (!(is_unloading || !has_traders || in_edit_count)
&& input->count(interface_key::SELECT_ALL))
{
set<df::interface_key> tmp; tmp.insert(interface_key::SELECT);
INTERPOSE_NEXT(feed)(&tmp);
if (in_edit_count)
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN);
INTERPOSE_NEXT(feed)(&tmp);
}
else
INTERPOSE_NEXT(feed)(input);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_select_hook, feed);

@ -0,0 +1,39 @@
#include "df/meeting_diplomat_info.h"
#include "df/entity_sell_requests.h"
#include "df/viewscreen_topicmeeting_takerequestsst.h"
using namespace std;
using namespace DFHack;
using namespace df::enums;
struct takerequest_hook : df::viewscreen_topicmeeting_takerequestsst {
typedef df::viewscreen_topicmeeting_takerequestsst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key>* input))
{
if (input->count(interface_key::CURSOR_RIGHT_FAST) ||
input->count(interface_key::CURSOR_LEFT_FAST))
{
int delta = 0 + input->count(interface_key::CURSOR_RIGHT_FAST)
- input->count(interface_key::CURSOR_LEFT_FAST);
vector<int8_t> &cur_priorities = meeting->sell_requests->priority[type_categories[type_idx]];
for (size_t i = 0; i < cur_priorities.size(); i++)
{
cur_priorities[i] += delta;
if (cur_priorities[i] > 4)
cur_priorities[i] = 4;
if (cur_priorities[i] < 0)
cur_priorities[i] = 0;
}
}
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
int x = 45, y = 23;
OutputString(COLOR_LIGHTRED, x, y, "Shift+Left/Right");
OutputString(COLOR_GREY, x, y, ": Adjust category");
}
};
IMPLEMENT_VMETHOD_INTERPOSE(takerequest_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(takerequest_hook, render);

@ -0,0 +1,53 @@
#include <stdexcept>
#include "df/viewscreen_createquotast.h"
#include "df/manager_order.h"
using df::global::world;
struct manager_quantity_hook : df::viewscreen_createquotast {
typedef df::viewscreen_createquotast interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key>* input))
{
bool cancel = false;
bool wanted_quantity = want_quantity;
if (want_quantity)
{
for (auto it = input->begin(); it != input->end(); ++it)
{
char c = DFHack::Screen::keyToChar(*it);
if (c >= '0' && c <= '9')
{
cancel = true;
size_t len = strlen(str_filter);
if (len < 5)
{
str_filter[len] = c;
str_filter[len + 1] = '\0';
}
}
}
}
if (cancel)
return;
// Native feed() adds manager order, updates want_quantity, and removes SELECT from input
int select = input->count(df::interface_key::SELECT);
INTERPOSE_NEXT(feed)(input);
if (wanted_quantity && select && strlen(str_filter) > 0)
{
df::manager_order* order = world->manager_orders[world->manager_orders.size() - 1];
int16_t count = 1;
try
{
count = std::stoi(str_filter);
}
catch (...) { }
if (count < 1)
count = 1;
order->amount_total = count;
order->amount_left = count;
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(manager_quantity_hook, feed);

@ -0,0 +1,99 @@
using namespace DFHack;
using namespace df::enums;
using Screen::Pen;
struct military_assign_hook : df::viewscreen_layer_militaryst {
typedef df::viewscreen_layer_militaryst interpose_base;
inline bool inPositionsMode() {
return page == Positions && !(in_create_squad || in_new_squad);
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (inPositionsMode() && !layer_objects[0]->active)
{
auto pos_list = layer_objects[1];
auto plist = layer_objects[2];
auto &cand = positions.candidates;
// Save the candidate list and cursors
std::vector<df::unit*> copy = cand;
int cursor = plist->getListCursor();
int pos_cursor = pos_list->getListCursor();
INTERPOSE_NEXT(feed)(input);
if (inPositionsMode() && !layer_objects[0]->active)
{
bool is_select = input->count(interface_key::SELECT);
// Resort the candidate list and restore cursor
// on add to squad OR scroll in the position list.
if (!plist->active || is_select)
{
// Since we don't know the actual sorting order, preserve
// the ordering of the items in the list before keypress.
// This does the right thing even if the list was sorted
// with sort-units.
std::set<df::unit*> prev, next;
prev.insert(copy.begin(), copy.end());
next.insert(cand.begin(), cand.end());
std::vector<df::unit*> out;
// (old-before-cursor) (new) |cursor| (old-after-cursor)
for (int i = 0; i < cursor && i < (int)copy.size(); i++)
if (next.count(copy[i])) out.push_back(copy[i]);
for (size_t i = 0; i < cand.size(); i++)
if (!prev.count(cand[i])) out.push_back(cand[i]);
int new_cursor = out.size();
for (int i = cursor; i < (int)copy.size(); i++)
if (next.count(copy[i])) out.push_back(copy[i]);
cand.swap(out);
plist->setListLength(cand.size());
if (new_cursor < (int)cand.size())
plist->setListCursor(new_cursor);
}
// Preserve the position list index on remove from squad
if (pos_list->active && is_select)
pos_list->setListCursor(pos_cursor);
}
}
else
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (inPositionsMode())
{
auto plist = layer_objects[2];
int x1 = plist->getX1(), y1 = plist->getY1();
int x2 = plist->getX2(), y2 = plist->getY2();
int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible();
int si = plist->getListCursor();
for (int y = y1, i = i1; i <= i2; i++, y++)
{
auto unit = vector_get(positions.candidates, i);
if (!unit || unit->military.squad_id < 0)
continue;
for (int x = x1; x <= x2; x++)
{
Pen cur_tile = Screen::readTile(x, y);
if (!cur_tile.valid()) continue;
cur_tile.fg = (i == si) ? COLOR_BROWN : COLOR_GREEN;
Screen::paintTile(cur_tile, x, y);
}
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, render);

@ -0,0 +1,22 @@
#include "df/building_flags.h"
#include "df/building_drawbuffer.h"
#include "df/building_nest_boxst.h"
using namespace df::enums;
struct nestbox_color_hook : df::building_nest_boxst {
typedef df::building_nest_boxst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer* db, int16_t unk))
{
INTERPOSE_NEXT(drawBuilding)(db, unk);
if (flags.bits.exists)
{
MaterialInfo mat(mat_type, mat_index);
db->fore[0][0] = mat.material->build_color[0];
db->back[0][0] = mat.material->build_color[1];
db->bright[0][0] = mat.material->build_color[2];
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(nestbox_color_hook, drawBuilding);

@ -0,0 +1,77 @@
/*
* Save or restore cursor position on change to/from main dwarfmode menu.
*/
using namespace std;
using namespace DFHack;
using namespace df::enums;
using df::global::ui;
using df::global::ui_build_selector;
using df::global::ui_menu_width;
static df::coord last_view, last_cursor;
struct stable_cursor_hook : df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool check_default()
{
switch (ui->main.mode) {
case ui_sidebar_mode::Default:
return true;
case ui_sidebar_mode::Build:
return ui_build_selector &&
(ui_build_selector->building_type < 0 ||
ui_build_selector->stage < 1);
default:
return false;
}
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
bool was_default = check_default();
df::coord view = Gui::getViewportPos();
df::coord cursor = Gui::getCursorPos();
INTERPOSE_NEXT(feed)(input);
bool is_default = check_default();
df::coord cur_cursor = Gui::getCursorPos();
if (is_default && !was_default)
{
last_view = view; last_cursor = cursor;
}
else if (!is_default && was_default &&
Gui::getViewportPos() == last_view &&
last_cursor.isValid() && cur_cursor.isValid())
{
Gui::setCursorCoords(last_cursor.x, last_cursor.y, last_cursor.z);
// Force update of ui state
set<df::interface_key> tmp;
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_UP_Z);
else
tmp.insert(interface_key::CURSOR_DOWN_Z);
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear();
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_DOWN_Z);
else
tmp.insert(interface_key::CURSOR_UP_Z);
INTERPOSE_NEXT(feed)(&tmp);
}
else if (!is_default && cur_cursor.isValid())
{
last_cursor = df::coord();
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(stable_cursor_hook, feed);

@ -131,15 +131,24 @@ local function zoomed_searcher(startn, end_or_sz)
end
local finder_searches = {}
local function exec_finder(finder, names)
local function exec_finder(finder, names, validators)
if type(names) ~= 'table' then
names = { names }
end
if type(validators) ~= 'table' then
validators = { validators }
end
local search = force_scan['all']
for _,v in ipairs(names) do
for k,v in ipairs(names) do
if force_scan[v] or not is_known(v) then
table.insert(finder_searches, v)
search = true
elseif validators[k] then
if not validators[k](df.global[v]) then
dfhack.printerr('Validation failed for '..v..', will try to find again')
table.insert(finder_searches, v)
search = true
end
end
end
if search then
@ -505,6 +514,7 @@ end
local function is_valid_world(world)
if not ms.is_valid_vector(world.units.all, 4)
or not ms.is_valid_vector(world.units.active, 4)
or not ms.is_valid_vector(world.units.bad, 4)
or not ms.is_valid_vector(world.history.figures, 4)
or not ms.is_valid_vector(world.features.map_features, 4)
@ -776,11 +786,6 @@ end
--
local function find_current_weather()
print('\nPlease load the save previously processed with prepare-save.')
if not utils.prompt_yes_no('Proceed?', true) then
return
end
local zone
if os_type == 'windows' then
zone = zoomed_searcher('crime_next_id', 512)
@ -839,7 +844,14 @@ local function find_ui_selected_unit()
end
for i,unit in ipairs(df.global.world.units.active) do
dfhack.units.setNickname(unit, i)
-- This function does a lot of things and accesses histfigs, souls and so on:
--dfhack.units.setNickname(unit, i)
-- Instead use just a simple bit of code that only requires the start of the
-- unit to be valid. It may not work properly with vampires or reset later
-- if unpaused, but is sufficient for this script and won't crash:
unit.name.nickname = tostring(i)
unit.name.has_name = true
end
local addr = searcher:find_menu_cursor([[
@ -1506,17 +1518,23 @@ print('\nInitial globals (need title screen):\n')
exec_finder(find_gview, 'gview')
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
exec_finder(find_announcements, 'announcements')
exec_finder(find_d_init, 'd_init')
exec_finder(find_enabler, 'enabler')
exec_finder(find_gps, 'gps')
exec_finder(find_d_init, 'd_init', is_valid_d_init)
exec_finder(find_enabler, 'enabler', is_valid_enabler)
exec_finder(find_gps, 'gps', is_valid_gps)
print('\nCompound globals (need loaded world):\n')
exec_finder(find_world, 'world')
exec_finder(find_ui, 'ui')
print('\nPlease load the save previously processed with prepare-save.')
if not utils.prompt_yes_no('Proceed?', true) then
searcher:reset()
return
end
exec_finder(find_world, 'world', is_valid_world)
exec_finder(find_ui, 'ui', is_valid_ui)
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
exec_finder(find_ui_build_selector, 'ui_build_selector')
exec_finder(find_init, 'init')
exec_finder(find_init, 'init', is_valid_init)
print('\nPrimitive globals:\n')

@ -0,0 +1,33 @@
-- Remove all aquifers from the map
local function drain()
local layers = {}
local layer_count = 0
local tile_count = 0
for k, block in ipairs(df.global.world.map.map_blocks) do
if block.flags.has_aquifer then
block.flags.has_aquifer = false
block.flags.check_aquifer = false
for x, row in ipairs(block.designation) do
for y, tile in ipairs(row) do
if tile.water_table then
tile.water_table = false
tile_count = tile_count + 1
end
end
end
if not layers[block.map_pos.z] then
layers[block.map_pos.z] = true
layer_count = layer_count + 1
end
end
end
print("Cleared "..tile_count.." aquifer tile"..((tile_count ~= 1) and "s" or "")..
" in "..layer_count.." layer"..((layer_count ~= 1) and "s" or "")..".")
end
drain(...)

@ -1,13 +0,0 @@
# remove all aquifers from the map
count = 0
df.each_map_block { |b|
if b.designation[0][0].water_table or b.designation[8][8].water_table
count += 1
df.each_map_block_z(b.map_pos.z) { |bz|
bz.designation.each { |dx| dx.each { |dy| dy.water_table = false } }
}
end
}
puts "cleared #{count} aquifer#{'s' if count > 1}"

@ -31,7 +31,7 @@ target file:
will be displayed if the script is successful.
character encoding:
The text will likely be using system-default encoding, and as such
will likely NOT display special characters (eg:é,õ,ç) correctly. To
will likely NOT display special characters (eg:È,ı,Á) correctly. To
fix this, you need to modify the character set that you are reading
the document with. 'Notepad++' is a freely available program which
can do this using the following steps:
@ -99,22 +99,15 @@ local function format_for_forum(strin)
end
if flerb == 'textviewer' then
print(scrn)
printall(scrn)
local lines = scrn.formatted_text
local lines = scrn.src_text
local line = ""
if lines ~= nil then
local log = io.open('forumdwarves.txt', 'a')
log:write("[color=silver]")
for n,x in ipairs(lines) do
print(x)
printall(x)
print(x.text)
printall(x.text)
if (x ~= nil) and (x.text ~= nil) then
log:write(format_for_forum(x.text), ' ')
--log:write(x[0],'\n')
if x ~= nil and x.value ~= nil then
log:write(format_for_forum(x.value), ' ')
end
end
log:write("[/color]\n")

@ -98,7 +98,7 @@ function equipHandler(unit, item, isEquip)
local table = {}
table.mode = mode
table.item = df.item.find(item)
table.unit = unit
table.unit = df.unit.find(unit)
handler(table)
end
@ -201,7 +201,7 @@ arguments:
\\CONTAMINANT_MATERIAL_INDEX
\\MODE
\\UNIT_ID
\\anything -> anything
\\anything -> \anything
anything -> anything
]])
return

@ -24,13 +24,13 @@ if args.help then
print this help message
repeat -cancel bob
cancels the repetition with the name bob
repeat -name jim -time delay -timeUnits units -printResult true -command printArgs 3 1 2
repeat -name jim -time delay -timeUnits units -printResult true -command [ printArgs 3 1 2 ]
-name sets the name for the purposes of cancelling and making sure you don't schedule the same repeating event twice
if not specified, it's set to the first argument after -command
-time delay -timeUnits units
delay is some positive integer
units is some valid time unit for dfhack.timeout(delay,timeUnits,function)
-command ...
-command [ ... ]
specify the command to be run
]])
return

@ -16,9 +16,12 @@ when 'add'
$superdwarf_ids.each { |id|
if u = df.unit_find(id) and not u.flags1.dead
# faster walk/work
if u.counters.job_counter > 0
u.counters.job_counter = 0
u.actions.each { |a|
case a.type
when :Move, :Climb, :Job, :Job2
a.data.send(a.type.to_s.downcase).timer = 0
end
}
# no sleep
if u.counters2.sleepiness_timer > 10000