Merge pull request #349 from lethosor/tweak-reorganization

'tweak' plugin cleanup/reorganization, new tweaks
develop
expwnent 2014-11-02 12:24:41 -05:00
commit b81346fa21
19 changed files with 962 additions and 729 deletions

10
NEWS

@ -1,12 +1,16 @@
DFHack future
Fixes:
- replaced drainaquifer.rb with a faster less buggy drain-aquifer.lua
Fixes:
- autotrade: "Mark all" no longer double-marks bin contents
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:

@ -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% }
@ -520,49 +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="#track-stop-menu" id="idtrackstop">Track Stop Menu</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>
@ -1931,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
@ -1976,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>
@ -2108,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>
@ -2311,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>
@ -3090,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>
@ -3153,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">
@ -3168,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" />
@ -3209,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
@ -3240,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
@ -3268,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
@ -3277,7 +3352,7 @@ enable automelt
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="#idtrackstop">Track Stop Menu</a></h2>
<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
@ -3291,7 +3366,7 @@ It re-uses the keybindings from the track stop building interface:</p>
</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>
@ -3310,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" />
@ -3334,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
@ -3345,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" />
@ -3368,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
@ -3383,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
@ -3393,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,
@ -3413,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
@ -3423,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>
@ -3452,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" />
@ -3460,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" />
@ -3468,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" />
@ -3515,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" />
@ -3552,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>
@ -3563,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>
@ -3599,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
@ -3610,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
@ -3625,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>
@ -3649,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
@ -3680,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
@ -3689,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
@ -3700,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
----------

@ -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 #
###########################

@ -159,7 +159,7 @@ if (BUILD_SUPPORTED)
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)

@ -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,6 @@ 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.
@ -1009,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")
@ -1123,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;
@ -1221,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)
@ -1247,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")
{
@ -1259,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")
{
@ -1284,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);