Merge remote-tracking branch 'origin/develop' into tweak-reorganization

Conflicts:
	NEWS
	Readme.html
	plugins/tweak/tweak.cpp
develop
lethosor 2014-11-02 11:40:53 -05:00
commit 14b947837c
36 changed files with 1088 additions and 505 deletions

@ -58,8 +58,8 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif() endif()
# set up versioning. # 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_RELEASE "r0" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")

@ -1,4 +1,9 @@
DFHack future DFHack future
Fixes:
- replaced drainaquifer.rb with a faster less buggy drain-aquifer.lua
- 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: New tweaks:
- farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus - 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 - import-priority-category: Allows changing the priority of all goods in a
@ -10,8 +15,10 @@ DFHack future
DFHack 0.40.13-r1 DFHack 0.40.13-r1
Internals: Internals:
- unified spatter structs - unified spatter structs
- added ruby df.print_color(color, string) method for dfhack console
Fixes: Fixes:
- no more -e after terminating - no more -e after terminating
- fixed superdwarf
DFHack 0.40.12-r1 DFHack 0.40.12-r1
Fixes: Fixes:

@ -525,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="#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="#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="#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="#deathcause" id="id136">deathcause</a></li>
<li><a class="reference internal" href="#dfstatus" id="id137">dfstatus</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> <li><a class="reference internal" href="#embark" id="id138">embark</a></li>
@ -554,39 +554,40 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#search" id="id159">Search</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="#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="#stockpile-automation" id="id161">Stockpile Automation</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id162">gui/advfort</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-assign-rack" id="id163">gui/assign-rack</a></li> <li><a class="reference internal" href="#gui-advfort" id="id163">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id164">gui/choose-weapons</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-clone-uniform" id="id165">gui/clone-uniform</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-companion-order" id="id166">gui/companion-order</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-gm-editor" id="id167">gui/gm-editor</a></li> <li><a class="reference internal" href="#gui-companion-order" id="id167">gui/companion-order</a></li>
<li><a class="reference internal" href="#hotkeys" id="id168">Hotkeys</a></li> <li><a class="reference internal" href="#gui-gm-editor" id="id168">gui/gm-editor</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id169">gui/liquids</a></li> <li><a class="reference internal" href="#hotkeys" id="id169">Hotkeys</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id170">gui/mechanisms</a></li> <li><a class="reference internal" href="#gui-liquids" id="id170">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id171">gui/mod-manager</a></li> <li><a class="reference internal" href="#gui-mechanisms" id="id171">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-rename" id="id172">gui/rename</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-room-list" id="id173">gui/room-list</a></li> <li><a class="reference internal" href="#gui-rename" id="id173">gui/rename</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id174">gui/guide-path</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-workflow" id="id175">gui/workflow</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-workshop-job" id="id176">gui/workshop-job</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> </ul>
</li> </li>
<li><a class="reference internal" href="#behavior-mods" id="id177">Behavior Mods</a><ul> <li><a class="reference internal" href="#behavior-mods" id="id178">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id178">Siege Engine</a><ul> <li><a class="reference internal" href="#siege-engine" id="id179">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id179">Rationale</a></li> <li><a class="reference internal" href="#rationale" id="id180">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id180">Configuration UI</a></li> <li><a class="reference internal" href="#configuration-ui" id="id181">Configuration UI</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#power-meter" id="id181">Power Meter</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="id182">Steam Engine</a><ul> <li><a class="reference internal" href="#steam-engine" id="id183">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id183">Rationale</a></li> <li><a class="reference internal" href="#id1" id="id184">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id184">Construction</a></li> <li><a class="reference internal" href="#construction" id="id185">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id185">Operation</a></li> <li><a class="reference internal" href="#operation" id="id186">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id186">Explosions</a></li> <li><a class="reference internal" href="#explosions" id="id187">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id187">Save files</a></li> <li><a class="reference internal" href="#save-files" id="id188">Save files</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#add-spatter" id="id188">Add Spatter</a></li> <li><a class="reference internal" href="#add-spatter" id="id189">Add Spatter</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -2344,9 +2345,38 @@ workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
<h2><a class="toc-backref" href="#id109">Fortress activity management</a></h2> <h2><a class="toc-backref" href="#id109">Fortress activity management</a></h2>
<div class="section" id="seedwatch"> <div class="section" id="seedwatch">
<h3><a class="toc-backref" href="#id110">seedwatch</a></h3> <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 <p>Watches the numbers of seeds available and enables/disables seed and plant cooking.</p>
have of them.</p> <p>Each plant type can be assigned a limit. If their number falls below that limit,
<p>See 'seedwatch help' for detailed description.</p> 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>
<div class="section" id="zone"> <div class="section" id="zone">
<h3><a class="toc-backref" href="#id111">zone</a></h3> <h3><a class="toc-backref" href="#id111">zone</a></h3>
@ -2907,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 <p>The script takes the plan filename, starting from the root df folder (where
Dwarf Fortress.exe is found).</p> Dwarf Fortress.exe is found).</p>
</div> </div>
<div class="section" id="drainaquifer"> <div class="section" id="drain-aquifer">
<h2><a class="toc-backref" href="#id135">drainaquifer</a></h2> <h2><a class="toc-backref" href="#id135">drain-aquifer</a></h2>
<p>Remove all 'aquifer' tag from the map blocks. Irreversible.</p> <p>Remove all 'aquifer' tag from the map blocks. Irreversible.</p>
</div> </div>
<div class="section" id="deathcause"> <div class="section" id="deathcause">
@ -3321,8 +3351,22 @@ enable automelt
<p>When querying a stockpile an option will appear to toggle automelt for this stockpile. <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> Any items placed in this stockpile will be designated to be melted.</p>
</div> </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"> <div class="section" id="gui-advfort">
<h2><a class="toc-backref" href="#id162">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 <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 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> keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments:</p>
@ -3341,7 +3385,7 @@ implies -a</li>
</div> </div>
</div> </div>
<div class="section" id="gui-assign-rack"> <div class="section" id="gui-assign-rack">
<h2><a class="toc-backref" href="#id163">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 <p>Bind to a key (the example config uses P), and activate when viewing a weapon
rack in the 'q' mode.</p> rack in the 'q' mode.</p>
<img alt="images/assign-rack.png" src="images/assign-rack.png" /> <img alt="images/assign-rack.png" src="images/assign-rack.png" />
@ -3365,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> of currently assigned racks for every valid squad.</p>
</div> </div>
<div class="section" id="gui-choose-weapons"> <div class="section" id="gui-choose-weapons">
<h2><a class="toc-backref" href="#id164">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 <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> page of the military screen.</p>
<p>Depending on the cursor location, it rewrites all 'individual choice weapon' entries <p>Depending on the cursor location, it rewrites all 'individual choice weapon' entries
@ -3376,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> and may lead to inappropriate weapons being selected.</p>
</div> </div>
<div class="section" id="gui-clone-uniform"> <div class="section" id="gui-clone-uniform">
<h2><a class="toc-backref" href="#id165">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 <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> page of the military screen with the cursor in the leftmost list.</p>
<p>When invoked, the script duplicates the currently selected uniform template, <p>When invoked, the script duplicates the currently selected uniform template,
and selects the newly created copy.</p> and selects the newly created copy.</p>
</div> </div>
<div class="section" id="gui-companion-order"> <div class="section" id="gui-companion-order">
<h2><a class="toc-backref" href="#id166">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 <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> 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" /> <img alt="images/companion-order.png" src="images/companion-order.png" />
@ -3399,7 +3443,7 @@ case. Must be in look or talk mode to issue command on tile.</p>
</ul> </ul>
</div> </div>
<div class="section" id="gui-gm-editor"> <div class="section" id="gui-gm-editor">
<h2><a class="toc-backref" href="#id167">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> <p>There are three ways to open this editor:</p>
<ul class="simple"> <ul class="simple">
<li>using gui/gm-editor command/keybinding - opens editor on what is selected <li>using gui/gm-editor command/keybinding - opens editor on what is selected
@ -3414,7 +3458,7 @@ the same as version above.</li>
in-game help.</p> in-game help.</p>
</div> </div>
<div class="section" id="hotkeys"> <div class="section" id="hotkeys">
<h2><a class="toc-backref" href="#id168">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> <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" /> <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 <p>Type <tt class="docutils literal">hotkeys</tt> into the DFHack console to open the screen, or bind the command to a
@ -3424,7 +3468,7 @@ keybinding add Ctrl-F1 hotkeys
</pre> </pre>
</div> </div>
<div class="section" id="gui-liquids"> <div class="section" id="gui-liquids">
<h2><a class="toc-backref" href="#id169">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> <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" /> <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, <p>This script is a gui front-end to the liquids plugin and works similar to it,
@ -3444,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> <p>After setting up the desired operations using the described keys, use <tt class="docutils literal">Enter</tt> to apply them.</p>
</div> </div>
<div class="section" id="gui-mechanisms"> <div class="section" id="gui-mechanisms">
<h2><a class="toc-backref" href="#id170">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> <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" /> <img alt="images/mechanisms.png" src="images/mechanisms.png" />
<p>Lists mechanisms connected to the building, and their links. Navigating the list centers <p>Lists mechanisms connected to the building, and their links. Navigating the list centers
@ -3454,13 +3498,13 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter
re-entering the mechanisms ui.</p> re-entering the mechanisms ui.</p>
</div> </div>
<div class="section" id="gui-mod-manager"> <div class="section" id="gui-mod-manager">
<h2><a class="toc-backref" href="#id171">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 <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> 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" /> <img alt="images/mod-manager.png" src="images/mod-manager.png" />
</div> </div>
<div class="section" id="gui-rename"> <div class="section" id="gui-rename">
<h2><a class="toc-backref" href="#id172">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 <p>Backed by the rename plugin, this script allows entering the desired name
via a simple dialog in the game ui.</p> via a simple dialog in the game ui.</p>
<ul> <ul>
@ -3483,7 +3527,7 @@ their species string.</p>
unit profession change to Ctrl-Shift-T.</p> unit profession change to Ctrl-Shift-T.</p>
</div> </div>
<div class="section" id="gui-room-list"> <div class="section" id="gui-room-list">
<h2><a class="toc-backref" href="#id173">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, <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> either immediately or after opening the assign owner page.</p>
<img alt="images/room-list.png" src="images/room-list.png" /> <img alt="images/room-list.png" src="images/room-list.png" />
@ -3491,7 +3535,7 @@ either immediately or after opening the assign owner page.</p>
list, and allows unassigning them.</p> list, and allows unassigning them.</p>
</div> </div>
<div class="section" id="gui-guide-path"> <div class="section" id="gui-guide-path">
<h2><a class="toc-backref" href="#id174">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 <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> the cursor over a Guide order.</p>
<img alt="images/guide-path.png" src="images/guide-path.png" /> <img alt="images/guide-path.png" src="images/guide-path.png" />
@ -3499,7 +3543,7 @@ the cursor over a Guide order.</p>
computes it when the order is executed for the first time.</p> computes it when the order is executed for the first time.</p>
</div> </div>
<div class="section" id="gui-workflow"> <div class="section" id="gui-workflow">
<h2><a class="toc-backref" href="#id175">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 <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> in a workshop in the 'q' mode.</p>
<img alt="images/workflow.png" src="images/workflow.png" /> <img alt="images/workflow.png" src="images/workflow.png" />
@ -3546,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> limit (maximum) and the dark green line is that minus the gap (minimum).</p>
</div> </div>
<div class="section" id="gui-workshop-job"> <div class="section" id="gui-workshop-job">
<h2><a class="toc-backref" href="#id176">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 <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> a workshop in the 'q' mode.</p>
<img alt="images/workshop-job.png" src="images/workshop-job.png" /> <img alt="images/workshop-job.png" src="images/workshop-job.png" />
@ -3583,7 +3627,7 @@ you have to unset the material first.</p>
</div> </div>
</div> </div>
<div class="section" id="behavior-mods"> <div class="section" id="behavior-mods">
<h1><a class="toc-backref" href="#id177">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 <p>These plugins, when activated via configuration UI or by detecting certain
structures in RAWs, modify the game engine behavior concerning the target structures in RAWs, modify the game engine behavior concerning the target
objects to add features not otherwise present.</p> objects to add features not otherwise present.</p>
@ -3594,20 +3638,20 @@ technical challenge, and do not represent any long-term plans to produce more
similar modifications of the game.</p> similar modifications of the game.</p>
</div> </div>
<div class="section" id="siege-engine"> <div class="section" id="siege-engine">
<h2><a class="toc-backref" href="#id178">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 <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 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 four directions. Also, catapults can be ordered to load arbitrary objects, not
just stones.</p> just stones.</p>
<div class="section" id="rationale"> <div class="section" id="rationale">
<h3><a class="toc-backref" href="#id179">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 <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 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, 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> e.g. like making siegers bring their own, are something only Toady can do.</p>
</div> </div>
<div class="section" id="configuration-ui"> <div class="section" id="configuration-ui">
<h3><a class="toc-backref" href="#id180">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 <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 script. Bind it to a key (the example config uses Alt-A) and activate after selecting
a siege engine in 'q' mode.</p> a siege engine in 'q' mode.</p>
@ -3630,7 +3674,7 @@ menu.</p>
</div> </div>
</div> </div>
<div class="section" id="power-meter"> <div class="section" id="power-meter">
<h2><a class="toc-backref" href="#id181">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 <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> 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 <p>The configuration front-end is implemented by the gui/power-meter script. Bind it to a
@ -3641,11 +3685,11 @@ in the build menu.</p>
configuration page, but configures parameters relevant to the modded power meter building.</p> configuration page, but configures parameters relevant to the modded power meter building.</p>
</div> </div>
<div class="section" id="steam-engine"> <div class="section" id="steam-engine">
<h2><a class="toc-backref" href="#id182">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 <p>The steam-engine plugin detects custom workshops with STEAM_ENGINE in
their token, and turns them into real steam engines.</p> their token, and turns them into real steam engines.</p>
<div class="section" id="id1"> <div class="section" id="id1">
<h3><a class="toc-backref" href="#id183">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 <p>The vanilla game contains only water wheels and windmills as sources of
power, but windmills give relatively little power, and water wheels require power, but windmills give relatively little power, and water wheels require
flowing water, which must either be a real river and thus immovable and flowing water, which must either be a real river and thus immovable and
@ -3656,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> in a new way with some glue code and a bit of custom logic.</p>
</div> </div>
<div class="section" id="construction"> <div class="section" id="construction">
<h3><a class="toc-backref" href="#id184">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 <p>The workshop needs water as its input, which it takes via a
passable floor tile below it, like usual magma workshops do. passable floor tile below it, like usual magma workshops do.
The magma version also needs magma.</p> The magma version also needs magma.</p>
@ -3680,7 +3724,7 @@ short axles that can be built later than both of the engines.</p>
</div> </div>
</div> </div>
<div class="section" id="operation"> <div class="section" id="operation">
<h3><a class="toc-backref" href="#id185">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 <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, 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 and perform it. As a result, a &quot;boiling water&quot; item will appear
@ -3711,7 +3755,7 @@ decrease it by further 4%, and also decrease the whole steam
use rate by 10%.</p> use rate by 10%.</p>
</div> </div>
<div class="section" id="explosions"> <div class="section" id="explosions">
<h3><a class="toc-backref" href="#id186">Explosions</a></h3> <h3><a class="toc-backref" href="#id187">Explosions</a></h3>
<p>The engine must be constructed using barrel, pipe and piston <p>The engine must be constructed using barrel, pipe and piston
from fire-safe, or in the magma version magma-safe metals.</p> from fire-safe, or in the magma version magma-safe metals.</p>
<p>During operation weak parts get gradually worn out, and <p>During operation weak parts get gradually worn out, and
@ -3720,7 +3764,7 @@ toppled during operation by a building destroyer, or a
tantruming dwarf.</p> tantruming dwarf.</p>
</div> </div>
<div class="section" id="save-files"> <div class="section" id="save-files">
<h3><a class="toc-backref" href="#id187">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 <p>It should be safe to load and view engine-using fortresses
from a DF version without DFHack installed, except that in such from a DF version without DFHack installed, except that in such
case the engines won't work. However actually making modifications case the engines won't work. However actually making modifications
@ -3731,7 +3775,7 @@ being generated.</p>
</div> </div>
</div> </div>
<div class="section" id="add-spatter"> <div class="section" id="add-spatter">
<h2><a class="toc-backref" href="#id188">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> <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 produce contaminants on the items instead of improvements. The produced
contaminants are immune to being washed away by water or destroyed by contaminants are immune to being washed away by water or destroyed by

@ -1626,10 +1626,31 @@ Fortress activity management
seedwatch seedwatch
--------- ---------
Tool for turning cooking of seeds and plants on/off depending on how much you Watches the numbers of seeds available and enables/disables seed and plant cooking.
have of them.
See 'seedwatch help' for detailed description. 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:
``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 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 The script takes the plan filename, starting from the root df folder (where
Dwarf Fortress.exe is found). Dwarf Fortress.exe is found).
drainaquifer drain-aquifer
============ =============
Remove all 'aquifer' tag from the map blocks. Irreversible. Remove all 'aquifer' tag from the map blocks. Irreversible.
deathcause 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. 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. 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 gui/advfort
=========== ===========

@ -189,7 +189,7 @@ enable search
enable automaterial enable automaterial
# Other interface improvement tools # 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 # allow the fortress bookkeeper to queue jobs through the manager
stockflow enable stockflow enable

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

@ -39,6 +39,14 @@ namespace df {
stl_bit_vector_identity identity_traits<std::vector<bool> >::identity; stl_bit_vector_identity identity_traits<std::vector<bool> >::identity;
bit_array_identity identity_traits<BitArray<int> >::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; buffer_container_identity buffer_container_identity::base_instance;
#undef NUMBER_IDENTITY_TRAITS #undef NUMBER_IDENTITY_TRAITS

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

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

@ -65,6 +65,17 @@ namespace DFHack
virtual identity_type type() { return IDTYPE_PRIMITIVE; } 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 { class DFHACK_EXPORT pointer_identity : public primitive_identity {
type_identity *target; type_identity *target;
@ -170,6 +181,7 @@ namespace df
{ {
using DFHack::function_identity_base; using DFHack::function_identity_base;
using DFHack::primitive_identity; using DFHack::primitive_identity;
using DFHack::opaque_identity;
using DFHack::pointer_identity; using DFHack::pointer_identity;
using DFHack::container_identity; using DFHack::container_identity;
using DFHack::ptr_container_identity; using DFHack::ptr_container_identity;
@ -488,6 +500,11 @@ namespace df
static stl_string_identity *get() { return &identity; } 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*> { template<> struct DFHACK_EXPORT identity_traits<char*> {
static ptr_string_identity identity; static ptr_string_identity identity;
static ptr_string_identity *get() { return &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 end
for caste,creature in ipairs(syndrome.syn_affected_creature) do for caste,creature in ipairs(syndrome.syn_affected_creature) do
local affectedCreature = creature.value 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 if affectedCreature == unitRaceName and (affectedCaste == unitCasteName or affectedCaste == "ALL") then
affected = true affected = true
end end

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

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

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

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

@ -7,6 +7,7 @@
#include "df/building_def.h" #include "df/building_def.h"
#include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_dwarfmodest.h"
#include "df/building_stockpilest.h" #include "df/building_stockpilest.h"
#include "modules/Buildings.h"
#include "modules/Items.h" #include "modules/Items.h"
#include "df/ui.h" #include "df/ui.h"
#include "modules/Maps.h" #include "modules/Maps.h"
@ -23,10 +24,36 @@ DFHACK_PLUGIN("automelt");
static const string PERSISTENCE_KEY = "automelt/stockpiles"; 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 // Precompute a bitmask with the bad flags
df::item_flags bad_flags; df::item_flags bad_flags;
bad_flags.whole = 0; bad_flags.whole = 0;
@ -39,27 +66,16 @@ static void mark_all_in_stockpiles(vector<PersistentStockpileInfo> &stockpiles)
#undef F #undef F
size_t marked_count = 0; 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++) for (auto it = stockpiles.begin(); it != stockpiles.end(); it++)
{ {
if (!it->inStockpile(item)) if (!it->isValid())
continue; continue;
++marked_count; auto spid = it->getId();
insert_into_vector(melting_items, &df::item::id, item); Buildings::StockpileIterator stored;
item->flags.bits.melt = true; 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++) 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)) 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++) 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)) 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" " digFlood digAll0\n"
" disable digAll mode\n" " disable digAll mode\n"
"\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; return CR_OK;
} }

@ -11,98 +11,13 @@
#include <VTableInterpose.h> #include <VTableInterpose.h>
#include "ColorText.h" #include "ColorText.h"
#include "uicommon.h"
#include "df/viewscreen_choose_start_sitest.h" #include "df/viewscreen_choose_start_sitest.h"
#include "df/interface_key.h" #include "df/interface_key.h"
using namespace DFHack; using namespace DFHack;
struct EmbarkTool #define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++)
{
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
*/
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) 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); update_embark_sidebar(screen);
} }
std::string sand_indicator = ""; typedef df::viewscreen_choose_start_sitest start_sitest;
bool sand_dirty = true; // Flag set when update is needed typedef std::set<df::interface_key> ikey_set;
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;
}
int sticky_pos[] = {0, 0, 3, 3}; class EmbarkTool
bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen)
{ {
sticky_pos[0] = screen->location.embark_pos_min.x; protected:
sticky_pos[1] = screen->location.embark_pos_max.x; bool enabled;
sticky_pos[2] = screen->location.embark_pos_min.y; public:
sticky_pos[3] = screen->location.embark_pos_max.y; EmbarkTool()
} :enabled(false)
{ }
void sticky_apply (df::viewscreen_choose_start_sitest * screen) virtual bool getEnabled() { return enabled; }
{ virtual void setEnabled(bool state) { enabled = state; }
if (screen->finder.finder_state != -1) virtual void toggleEnabled() { setEnabled(!enabled); }
{ virtual std::string getId() = 0;
// Site finder is active - don't override default local position virtual std::string getName() = 0;
return; virtual std::string getDesc() = 0;
} virtual df::interface_key getToggleKey() = 0;
screen->location.embark_pos_min.x = sticky_pos[0]; virtual void before_render(start_sitest* screen) { };
screen->location.embark_pos_max.x = sticky_pos[1]; virtual void after_render(start_sitest* screen) { };
screen->location.embark_pos_min.y = sticky_pos[2]; virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
screen->location.embark_pos_max.y = sticky_pos[3]; virtual void after_feed(start_sitest* screen, ikey_set* input) { };
update_embark_sidebar(screen); };
} 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); virtual std::string getId() { return "id"; }
x += text.length(); 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)) class EmbarkAnywhere : public EmbarkTool
{
bool prevent_default = false;
if (tool_enabled("anywhere"))
{ {
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; auto dim = Screen::getWindowSize();
if (key == df::interface_key::SETUP_EMBARK) int x = 20, y = dim.y - 2;
if (screen->page >= 0 && screen->page <= 4)
{ {
prevent_default = true; OutputString(COLOR_WHITE, x, y, ": Embark!");
this->in_embark_normal = 1;
}
} }
} }
virtual void before_feed(start_sitest* screen, ikey_set *input, bool &cancel)
if (input->count(df::interface_key::CUSTOM_S))
{ {
Screen::show(new embark_tools_settings); if (input->count(df::interface_key::SETUP_EMBARK))
return; {
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++) 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) if (is_resize)
{ {
prevent_default = true; cancel = true;
resize_embark(this, dx, dy); 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++) for (auto iter = input->begin(); iter != input->end(); iter++)
{ {
df::interface_key key = *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: case df::interface_key::CURSOR_DOWNRIGHT_FAST:
is_motion = true; is_motion = true;
break; break;
default:
is_motion = false;
} }
if (is_motion && !sticky_moved) if (is_motion && !moved_position)
{ {
sticky_save(this); save_position(screen);
sticky_moved = true; 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) return false;
INTERPOSE_NEXT(feed)(input);
} }
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); tool->setEnabled(enable_state);
sticky_moved = false; 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(); auto dim = Screen::getWindowSize();
int x = 1, int x = 1,
y = dim.y - 5; y = dim.y - 5;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S)); OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
OutputString(COLOR_WHITE, x, y, ": Enabled: "); OutputString(COLOR_WHITE, x, y, ": Enabled: ");
std::list<std::string> parts; 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(", "); parts.push_back(", ");
} }
} }
@ -354,97 +449,77 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{ {
OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)"); 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"))
{ void display_settings()
if (sand_dirty)
{ {
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);
/* DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
* Tool management
*/
bool tool_exists (std::string tool_name)
{ {
for (int i = 0; i < NUM_TOOLS; i++) bool cancel = false;
FOR_ITER_TOOLS(iter)
{ {
if (embark_tools[i].id == tool_name) EmbarkTool* tool = *iter;
return true; if (tool->getEnabled())
tool->before_feed(this, input, cancel);
} }
return false; if (cancel)
} return;
INTERPOSE_NEXT(feed)(input);
bool tool_enabled (std::string tool_name) if (input->count(df::interface_key::CUSTOM_S) && is_valid_page())
{ display_settings();
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
if (embark_tools[i].id == tool_name) EmbarkTool* tool = *iter;
return embark_tools[i].enabled; if (tool->getEnabled())
tool->after_feed(this, input);
} }
return false;
} }
DEFINE_VMETHOD_INTERPOSE(void, render, ())
bool tool_enable (std::string tool_name, bool enable_state)
{
int n = 0;
for (int i = 0; i < NUM_TOOLS; i++)
{ {
if (embark_tools[i].id == tool_name || tool_name == "all") FOR_ITER_TOOLS(iter)
{ {
embark_tools[i].enabled = enable_state; EmbarkTool* tool = *iter;
tool_update(tool_name); if (tool->getEnabled())
n++; tool->before_render(this);
}
}
return (bool)n;
} }
INTERPOSE_NEXT(render)();
void tool_update (std::string tool_name) display_tool_status();
{ FOR_ITER_TOOLS(iter)
// Called whenever a tool is enabled/disabled
if (tool_name == "sand")
{ {
sand_dirty = true; EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->after_render(this);
} }
} }
};
/* IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
* Plugin management IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
*/
DFHACK_PLUGIN("embark-tools"); DFHACK_PLUGIN("embark-tools");
DFHACK_PLUGIN_IS_ENABLED(is_enabled); 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) 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 = ""; std::string help = "";
help += "embark-tools (enable/disable) tool [tool...]\n" help += "embark-tools (enable/disable) tool [tool...]\n"
"Tools:\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( commands.push_back(PluginCommand(
"embark-tools", "embark-tools",
@ -504,10 +579,11 @@ command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> &
if (is_enabled) if (is_enabled)
{ {
out << "Tool status:" << std::endl; out << "Tool status:" << std::endl;
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
EmbarkTool t = embark_tools[i]; EmbarkTool* t = *iter;
out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl; out << t->getName() << " (" << t->getId() << "): "
<< (t->getEnabled() ? "Enabled" : "Disabled") << std::endl;
} }
} }
else else

@ -113,8 +113,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
action->data.move.timer = 1; action->data.move.timer = 1;
break; break;
case unit_action_type::Attack: 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.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; break;
case unit_action_type::Hold: case unit_action_type::Hold:
action->data.hold.timer = 1; action->data.hold.timer = 1;

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

@ -673,6 +673,8 @@ void lightingEngineViewscreen::doOcupancyAndLights()
} }
//df::tile_occupancy o = b->OccupancyAt(gpos); //df::tile_occupancy o = b->OccupancyAt(gpos);
df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); 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_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape);
df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type); df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type);
@ -685,7 +687,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
{ {
curCell=rgbf(0,0,0); curCell=rgbf(0,0,0);
} }
else if(shape==df::tiletype_shape::WALL) else if(is_wall)
{ {
if(tileMat==df::tiletype_material::FROZEN_LIQUID) if(tileMat==df::tiletype_material::FROZEN_LIQUID)
applyMaterial(tile,matIce); 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); 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 else if(!is_floor)
|| shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN)
{ {
if(bDown) 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 //blood and other goo
for(int i=0;i<block->block_events.size();i++) for(int i=0;i<block->block_events.size();i++)
{ {

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

@ -543,6 +543,18 @@ static VALUE rb_dfprint_str(VALUE self, VALUE s)
return Qnil; 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) static VALUE rb_dfprint_err(VALUE self, VALUE s)
{ {
printerr("%s", rb_string_value_ptr(&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, "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, "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_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, "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, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);
rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1);

@ -179,7 +179,7 @@ module DFHack
if not onlylastpart if not onlylastpart
out << name.first_name if name.first_name != '' out << name.first_name if name.first_name != ''
if name.nickname != '' 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_ALL; return "`#{name.nickname}'"
when :REPLACE_FIRST; out.pop when :REPLACE_FIRST; out.pop
end end

@ -631,7 +631,11 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
// If no mood type was specified, pick one randomly // If no mood type was specified, pick one randomly
if (type == mood_type::None) 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)) 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; unit->relations.mood_copy = unit->mood;
Gui::showAutoAnnouncement(announcement_type::STRANGE_MOOD, unit->pos, msg, color, bright); Gui::showAutoAnnouncement(announcement_type::STRANGE_MOOD, unit->pos, msg, color, bright);
unit->status.happiness = 100;
// TODO: make sure unit drops any wrestle items // TODO: make sure unit drops any wrestle items
unit->job.mood_timeout = 50000; unit->job.mood_timeout = 50000;
unit->flags1.bits.has_mood = true; 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))) if ((job->job_type == job_type::StrangeMoodBrooding) && (rng.df_trandom(2)))
{ {
switch (rng.df_trandom(3)) switch (rng.df_trandom(2))
{ {
case 0: case 0:
job->job_items.push_back(item = new df::job_item()); 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->flags2.bits.body_part = true;
item->quantity = 1; item->quantity = 1;
break; break;
case 2:
// in older versions, they would request additional skulls
// in 0.34.11, the request becomes "nothing"
break;
} }
} }
else 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 == 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 == screw_pump_direction::FromNorth)? "Southward":
(roller->direction == screw_pump_direction::FromEast)? "Westward":
(roller->direction == screw_pump_direction::FromSouth)? "Northward":
(roller->direction == 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);
}

@ -363,6 +363,7 @@ struct dimension_cloth_hook : df::item_clothst {
IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension); IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension);
/*
// Unit updates are executed based on an action divisor variable, // Unit updates are executed based on an action divisor variable,
// which is computed from the alive unit count and has range 10-100. // which is computed from the alive unit count and has range 10-100.
static int adjust_unit_divisor(int value) { static int adjust_unit_divisor(int value) {
@ -541,6 +542,7 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
} }
} }
}; };
*/
/* /*
IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process); IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process);

@ -131,15 +131,24 @@ local function zoomed_searcher(startn, end_or_sz)
end end
local finder_searches = {} local finder_searches = {}
local function exec_finder(finder, names) local function exec_finder(finder, names, validators)
if type(names) ~= 'table' then if type(names) ~= 'table' then
names = { names } names = { names }
end end
if type(validators) ~= 'table' then
validators = { validators }
end
local search = force_scan['all'] 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 if force_scan[v] or not is_known(v) then
table.insert(finder_searches, v) table.insert(finder_searches, v)
search = true 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
end end
if search then if search then
@ -505,6 +514,7 @@ end
local function is_valid_world(world) local function is_valid_world(world)
if not ms.is_valid_vector(world.units.all, 4) 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.units.bad, 4)
or not ms.is_valid_vector(world.history.figures, 4) or not ms.is_valid_vector(world.history.figures, 4)
or not ms.is_valid_vector(world.features.map_features, 4) or not ms.is_valid_vector(world.features.map_features, 4)
@ -776,11 +786,6 @@ end
-- --
local function find_current_weather() 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 local zone
if os_type == 'windows' then if os_type == 'windows' then
zone = zoomed_searcher('crime_next_id', 512) zone = zoomed_searcher('crime_next_id', 512)
@ -839,7 +844,14 @@ local function find_ui_selected_unit()
end end
for i,unit in ipairs(df.global.world.units.active) do 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 end
local addr = searcher:find_menu_cursor([[ 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_gview, 'gview')
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' }) exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
exec_finder(find_announcements, 'announcements') exec_finder(find_announcements, 'announcements')
exec_finder(find_d_init, 'd_init') exec_finder(find_d_init, 'd_init', is_valid_d_init)
exec_finder(find_enabler, 'enabler') exec_finder(find_enabler, 'enabler', is_valid_enabler)
exec_finder(find_gps, 'gps') exec_finder(find_gps, 'gps', is_valid_gps)
print('\nCompound globals (need loaded world):\n') print('\nCompound globals (need loaded world):\n')
exec_finder(find_world, 'world') print('\nPlease load the save previously processed with prepare-save.')
exec_finder(find_ui, 'ui') 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_sidebar_menus, 'ui_sidebar_menus')
exec_finder(find_ui_build_selector, 'ui_build_selector') 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') 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. will be displayed if the script is successful.
character encoding: character encoding:
The text will likely be using system-default encoding, and as such 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 fix this, you need to modify the character set that you are reading
the document with. 'Notepad++' is a freely available program which the document with. 'Notepad++' is a freely available program which
can do this using the following steps: can do this using the following steps:
@ -99,22 +99,15 @@ local function format_for_forum(strin)
end end
if flerb == 'textviewer' then if flerb == 'textviewer' then
print(scrn) local lines = scrn.src_text
printall(scrn)
local lines = scrn.formatted_text
local line = "" local line = ""
if lines ~= nil then if lines ~= nil then
local log = io.open('forumdwarves.txt', 'a') local log = io.open('forumdwarves.txt', 'a')
log:write("[color=silver]") log:write("[color=silver]")
for n,x in ipairs(lines) do for n,x in ipairs(lines) do
print(x) if x ~= nil and x.value ~= nil then
printall(x) log:write(format_for_forum(x.value), ' ')
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')
end end
end end
log:write("[/color]\n") log:write("[/color]\n")

@ -24,13 +24,13 @@ if args.help then
print this help message print this help message
repeat -cancel bob repeat -cancel bob
cancels the repetition with the name 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 -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 if not specified, it's set to the first argument after -command
-time delay -timeUnits units -time delay -timeUnits units
delay is some positive integer delay is some positive integer
units is some valid time unit for dfhack.timeout(delay,timeUnits,function) units is some valid time unit for dfhack.timeout(delay,timeUnits,function)
-command ... -command [ ... ]
specify the command to be run specify the command to be run
]]) ]])
return return

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