Merge branch 'develop' of https://github.com/DFHack/dfhack into develop

Conflicts:
	library/xml
develop
JapaMala 2014-06-21 16:43:44 +05:30
commit 3f717af0b7
23 changed files with 1753 additions and 187 deletions

@ -59,7 +59,7 @@ endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.34.11") set(DF_VERSION "0.34.11")
SET(DFHACK_RELEASE "r4" CACHE STRING "Current release revision.") SET(DFHACK_RELEASE "r5" 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}")

@ -364,7 +364,7 @@ ul.auto-toc {
<li>RossM &lt;<a class="reference external" href="mailto:Ross&#64;Gnome">Ross&#64;Gnome</a>&gt;</li> <li>RossM &lt;<a class="reference external" href="mailto:Ross&#64;Gnome">Ross&#64;Gnome</a>&gt;</li>
<li>Tom Prince &lt;<a class="reference external" href="mailto:tom.prince&#64;ualberta.net">tom.prince&#64;ualberta.net</a>&gt;</li> <li>Tom Prince &lt;<a class="reference external" href="mailto:tom.prince&#64;ualberta.net">tom.prince&#64;ualberta.net</a>&gt;</li>
<li>Jared Adams &lt;<a class="reference external" href="mailto:jaxad0127&#64;gmail.com">jaxad0127&#64;gmail.com</a>&gt;</li> <li>Jared Adams &lt;<a class="reference external" href="mailto:jaxad0127&#64;gmail.com">jaxad0127&#64;gmail.com</a>&gt;</li>
<li>expwnent &lt;<a class="reference external" href="mailto:q309185&#64;gmail.com">q309185&#64;gmail.com</a>&gt;</li> <li>expwnent</li>
<li>Erik Youngren &lt;<a class="reference external" href="mailto:artanis.00&#64;gmail.com">artanis.00&#64;gmail.com</a>&gt;</li> <li>Erik Youngren &lt;<a class="reference external" href="mailto:artanis.00&#64;gmail.com">artanis.00&#64;gmail.com</a>&gt;</li>
<li>Espen Wiborg &lt;<a class="reference external" href="mailto:espen.wiborg&#64;telio.no">espen.wiborg&#64;telio.no</a>&gt;</li> <li>Espen Wiborg &lt;<a class="reference external" href="mailto:espen.wiborg&#64;telio.no">espen.wiborg&#64;telio.no</a>&gt;</li>
<li>Tim Walberg &lt;<a class="reference external" href="mailto:twalberg&#64;comcast.net">twalberg&#64;comcast.net</a>&gt;</li> <li>Tim Walberg &lt;<a class="reference external" href="mailto:twalberg&#64;comcast.net">twalberg&#64;comcast.net</a>&gt;</li>
@ -401,6 +401,7 @@ ul.auto-toc {
<li>Caldfir &lt;<a class="reference external" href="mailto:caldfir&#64;hotmail.com">caldfir&#64;hotmail.com</a>&gt;</li> <li>Caldfir &lt;<a class="reference external" href="mailto:caldfir&#64;hotmail.com">caldfir&#64;hotmail.com</a>&gt;</li>
<li>Antalia &lt;<a class="reference external" href="mailto:tamarakorr&#64;gmail.com">tamarakorr&#64;gmail.com</a>&gt;</li> <li>Antalia &lt;<a class="reference external" href="mailto:tamarakorr&#64;gmail.com">tamarakorr&#64;gmail.com</a>&gt;</li>
<li>Angus Mezick &lt;<a class="reference external" href="mailto:amezick&#64;gmail.com">amezick&#64;gmail.com</a>&gt;</li> <li>Angus Mezick &lt;<a class="reference external" href="mailto:amezick&#64;gmail.com">amezick&#64;gmail.com</a>&gt;</li>
<li>PeridexisErrant &lt;<a class="reference external" href="mailto:PeridexisErrant&#64;gmail.com">PeridexisErrant&#64;gmail.com</a>&gt;</li>
</ul> </ul>
<p>And those are the cool people who made <strong>stonesense</strong>.</p> <p>And those are the cool people who made <strong>stonesense</strong>.</p>
<ul class="simple"> <ul class="simple">

@ -24,7 +24,7 @@ The following is a list of people who have contributed to **DFHack**.
- RossM <Ross@Gnome> - RossM <Ross@Gnome>
- Tom Prince <tom.prince@ualberta.net> - Tom Prince <tom.prince@ualberta.net>
- Jared Adams <jaxad0127@gmail.com> - Jared Adams <jaxad0127@gmail.com>
- expwnent <q309185@gmail.com> - expwnent
- Erik Youngren <artanis.00@gmail.com> - Erik Youngren <artanis.00@gmail.com>
- Espen Wiborg <espen.wiborg@telio.no> - Espen Wiborg <espen.wiborg@telio.no>
- Tim Walberg <twalberg@comcast.net> - Tim Walberg <twalberg@comcast.net>

13
NEWS

@ -1,12 +1,17 @@
DFHack future DFHack future
The future has not yet happened. Stay tuned!
DFHack v0.34.11-r5
Internals: Internals:
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua). - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
- support for basic filesystem operations (e.g. chdir, mkdir, rmdir, stat) in C++ and Lua
- Lua API for listing files in directory. Needed for mod-manager. - Lua API for listing files in directory. Needed for mod-manager.
- Lua API for creating unit combat reports and writing to gamelog. - Lua API for creating unit combat reports and writing to gamelog.
- Lua API for running arbitrary DFHack commands
- support for multiple raw/init.d/*.lua init scripts in one save. - support for multiple raw/init.d/*.lua init scripts in one save.
- eventful now has a more friendly way of making custom sidebars - eventful now has a more friendly way of making custom sidebars
- new plugin: building-hacks. Allows to add custom functionality and/or animations to buildings.
- on Linux and OSX the console now supports moving the cursor back and forward by a whole word. - on Linux and OSX the console now supports moving the cursor back and forward by a whole word.
New scripts: New scripts:
@ -19,19 +24,23 @@ DFHack future
- dfstatus: show an overview of critical stock quantities, including food, drinks, wood, and bars. - dfstatus: show an overview of critical stock quantities, including food, drinks, wood, and bars.
New commands: New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- 'plant create' - spawn a new shrub under the cursor - 'plant create' - spawn a new shrub under the cursor
- command-prompt: a dfhack command prompt in df. - command-prompt: a dfhack command prompt in df.
New tweaks: New tweaks:
- craft-age-wear: make crafted items wear out with time like in old versions (bug 6003) - craft-age-wear: make crafted items wear out with time like in old versions (bug 6003)
- adamantine-cloth-wear: stop adamantine clothing from wearing out (bug 6481) - adamantine-cloth-wear: stop adamantine clothing from wearing out (bug 6481)
- confirm-embark: adds a prompt before embarking (on the "prepare carefully" screen)
New plugins: New plugins:
- rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df. - rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df.
- stockflow (by eswald): queues manager jobs of the configured type based on the state of a stockpile. - stockflow (by eswald): queues manager jobs of the configured type based on the state of a stockpile.
- embark-tools: implementations of Embark Anywhere, Nano Embark, and a few other embark-related utilities
- building-hacks: Allows to add custom functionality and/or animations to buildings.
- petcapRemover: triggers pregnancies in creatures so that you can effectively raise the default pet population cap from the default 50
Misc improvements: Misc improvements:
- plant: move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- digfort: improved csv parsing, add start() comment handling - digfort: improved csv parsing, add start() comment handling
- exterminate: allow specifying a caste (exterminate gob:male) - exterminate: allow specifying a caste (exterminate gob:male)
- createitem: in adventure mode it now defaults to the controlled unit as maker. - createitem: in adventure mode it now defaults to the controlled unit as maker.

@ -519,79 +519,81 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#other" id="id126">Other</a><ul> <li><a class="reference internal" href="#other" id="id126">Other</a><ul>
<li><a class="reference internal" href="#catsplosion" id="id127">catsplosion</a></li> <li><a class="reference internal" href="#catsplosion" id="id127">catsplosion</a></li>
<li><a class="reference internal" href="#dfusion" id="id128">dfusion</a></li> <li><a class="reference internal" href="#dfusion" id="id128">dfusion</a></li>
<li><a class="reference internal" href="#misery" id="id129">misery</a></li> <li><a class="reference internal" href="#embark-tools" id="id129">embark-tools</a></li>
<li><a class="reference internal" href="#strangemood" id="id130">strangemood</a></li> <li><a class="reference internal" href="#petcapremover" id="id130">petcapRemover</a></li>
<li><a class="reference internal" href="#log-region" id="id131">log-region</a></li> <li><a class="reference internal" href="#misery" id="id131">misery</a></li>
<li><a class="reference internal" href="#strangemood" id="id132">strangemood</a></li>
<li><a class="reference internal" href="#log-region" id="id133">log-region</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#scripts" id="id132">Scripts</a><ul> <li><a class="reference internal" href="#scripts" id="id134">Scripts</a><ul>
<li><a class="reference internal" href="#fix" id="id133">fix/*</a></li> <li><a class="reference internal" href="#fix" id="id135">fix/*</a></li>
<li><a class="reference internal" href="#gui" id="id134">gui/*</a></li> <li><a class="reference internal" href="#gui" id="id136">gui/*</a></li>
<li><a class="reference internal" href="#binpatch" id="id135">binpatch</a></li> <li><a class="reference internal" href="#binpatch" id="id137">binpatch</a></li>
<li><a class="reference internal" href="#quicksave" id="id136">quicksave</a></li> <li><a class="reference internal" href="#quicksave" id="id138">quicksave</a></li>
<li><a class="reference internal" href="#setfps" id="id137">setfps</a></li> <li><a class="reference internal" href="#setfps" id="id139">setfps</a></li>
<li><a class="reference internal" href="#siren" id="id138">siren</a></li> <li><a class="reference internal" href="#siren" id="id140">siren</a></li>
<li><a class="reference internal" href="#growcrops" id="id139">growcrops</a></li> <li><a class="reference internal" href="#growcrops" id="id141">growcrops</a></li>
<li><a class="reference internal" href="#removebadthoughts" id="id140">removebadthoughts</a></li> <li><a class="reference internal" href="#removebadthoughts" id="id142">removebadthoughts</a></li>
<li><a class="reference internal" href="#exterminate" id="id141">exterminate</a></li> <li><a class="reference internal" href="#exterminate" id="id143">exterminate</a></li>
<li><a class="reference internal" href="#source" id="id142">source</a></li> <li><a class="reference internal" href="#source" id="id144">source</a></li>
<li><a class="reference internal" href="#masspit" id="id143">masspit</a></li> <li><a class="reference internal" href="#masspit" id="id145">masspit</a></li>
<li><a class="reference internal" href="#digfort" id="id144">digfort</a></li> <li><a class="reference internal" href="#digfort" id="id146">digfort</a></li>
<li><a class="reference internal" href="#invasion-now" id="id145">invasion-now</a></li> <li><a class="reference internal" href="#invasion-now" id="id147">invasion-now</a></li>
<li><a class="reference internal" href="#digmat" id="id146">digmat</a></li> <li><a class="reference internal" href="#digmat" id="id148">digmat</a></li>
<li><a class="reference internal" href="#superdwarf" id="id147">superdwarf</a></li> <li><a class="reference internal" href="#superdwarf" id="id149">superdwarf</a></li>
<li><a class="reference internal" href="#drainaquifer" id="id148">drainaquifer</a></li> <li><a class="reference internal" href="#drainaquifer" id="id150">drainaquifer</a></li>
<li><a class="reference internal" href="#deathcause" id="id149">deathcause</a></li> <li><a class="reference internal" href="#deathcause" id="id151">deathcause</a></li>
<li><a class="reference internal" href="#lua" id="id150">lua</a></li> <li><a class="reference internal" href="#lua" id="id152">lua</a></li>
<li><a class="reference internal" href="#embark" id="id151">embark</a></li> <li><a class="reference internal" href="#embark" id="id153">embark</a></li>
<li><a class="reference internal" href="#lever" id="id152">lever</a></li> <li><a class="reference internal" href="#lever" id="id154">lever</a></li>
<li><a class="reference internal" href="#stripcaged" id="id153">stripcaged</a></li> <li><a class="reference internal" href="#stripcaged" id="id155">stripcaged</a></li>
<li><a class="reference internal" href="#undump-buildings" id="id154">undump-buildings</a></li> <li><a class="reference internal" href="#undump-buildings" id="id156">undump-buildings</a></li>
<li><a class="reference internal" href="#create-items" id="id155">create-items</a></li> <li><a class="reference internal" href="#create-items" id="id157">create-items</a></li>
<li><a class="reference internal" href="#locate-ore" id="id156">locate-ore</a></li> <li><a class="reference internal" href="#locate-ore" id="id158">locate-ore</a></li>
<li><a class="reference internal" href="#soundsense-season" id="id157">soundsense-season</a></li> <li><a class="reference internal" href="#soundsense-season" id="id159">soundsense-season</a></li>
<li><a class="reference internal" href="#multicmd" id="id158">multicmd</a></li> <li><a class="reference internal" href="#multicmd" id="id160">multicmd</a></li>
<li><a class="reference internal" href="#dfstatus" id="id159">dfstatus</a></li> <li><a class="reference internal" href="#dfstatus" id="id161">dfstatus</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#in-game-interface-tools" id="id160">In-game interface tools</a><ul> <li><a class="reference internal" href="#in-game-interface-tools" id="id162">In-game interface tools</a><ul>
<li><a class="reference internal" href="#dwarf-manipulator" id="id161">Dwarf Manipulator</a></li> <li><a class="reference internal" href="#dwarf-manipulator" id="id163">Dwarf Manipulator</a></li>
<li><a class="reference internal" href="#search" id="id162">Search</a></li> <li><a class="reference internal" href="#search" id="id164">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id163">AutoMaterial</a></li> <li><a class="reference internal" href="#automaterial" id="id165">AutoMaterial</a></li>
<li><a class="reference internal" href="#gui-liquids" id="id164">gui/liquids</a></li> <li><a class="reference internal" href="#gui-liquids" id="id166">gui/liquids</a></li>
<li><a class="reference internal" href="#gui-mechanisms" id="id165">gui/mechanisms</a></li> <li><a class="reference internal" href="#gui-mechanisms" id="id167">gui/mechanisms</a></li>
<li><a class="reference internal" href="#gui-rename" id="id166">gui/rename</a></li> <li><a class="reference internal" href="#gui-rename" id="id168">gui/rename</a></li>
<li><a class="reference internal" href="#gui-room-list" id="id167">gui/room-list</a></li> <li><a class="reference internal" href="#gui-room-list" id="id169">gui/room-list</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id168">gui/choose-weapons</a></li> <li><a class="reference internal" href="#gui-choose-weapons" id="id170">gui/choose-weapons</a></li>
<li><a class="reference internal" href="#gui-clone-uniform" id="id169">gui/clone-uniform</a></li> <li><a class="reference internal" href="#gui-clone-uniform" id="id171">gui/clone-uniform</a></li>
<li><a class="reference internal" href="#gui-guide-path" id="id170">gui/guide-path</a></li> <li><a class="reference internal" href="#gui-guide-path" id="id172">gui/guide-path</a></li>
<li><a class="reference internal" href="#gui-workshop-job" id="id171">gui/workshop-job</a></li> <li><a class="reference internal" href="#gui-workshop-job" id="id173">gui/workshop-job</a></li>
<li><a class="reference internal" href="#gui-workflow" id="id172">gui/workflow</a></li> <li><a class="reference internal" href="#gui-workflow" id="id174">gui/workflow</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id173">gui/assign-rack</a></li> <li><a class="reference internal" href="#gui-assign-rack" id="id175">gui/assign-rack</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id174">gui/advfort</a></li> <li><a class="reference internal" href="#gui-advfort" id="id176">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-companion-order" id="id175">gui/companion-order</a></li> <li><a class="reference internal" href="#gui-companion-order" id="id177">gui/companion-order</a></li>
<li><a class="reference internal" href="#gui-gm-editor" id="id176">gui/gm-editor</a></li> <li><a class="reference internal" href="#gui-gm-editor" id="id178">gui/gm-editor</a></li>
<li><a class="reference internal" href="#gui-mod-manager" id="id177">gui/mod-manager</a></li> <li><a class="reference internal" href="#gui-mod-manager" id="id179">gui/mod-manager</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#behavior-mods" id="id178">Behavior Mods</a><ul> <li><a class="reference internal" href="#behavior-mods" id="id180">Behavior Mods</a><ul>
<li><a class="reference internal" href="#siege-engine" id="id179">Siege Engine</a><ul> <li><a class="reference internal" href="#siege-engine" id="id181">Siege Engine</a><ul>
<li><a class="reference internal" href="#rationale" id="id180">Rationale</a></li> <li><a class="reference internal" href="#rationale" id="id182">Rationale</a></li>
<li><a class="reference internal" href="#configuration-ui" id="id181">Configuration UI</a></li> <li><a class="reference internal" href="#configuration-ui" id="id183">Configuration UI</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#power-meter" id="id182">Power Meter</a></li> <li><a class="reference internal" href="#power-meter" id="id184">Power Meter</a></li>
<li><a class="reference internal" href="#steam-engine" id="id183">Steam Engine</a><ul> <li><a class="reference internal" href="#steam-engine" id="id185">Steam Engine</a><ul>
<li><a class="reference internal" href="#id1" id="id184">Rationale</a></li> <li><a class="reference internal" href="#id1" id="id186">Rationale</a></li>
<li><a class="reference internal" href="#construction" id="id185">Construction</a></li> <li><a class="reference internal" href="#construction" id="id187">Construction</a></li>
<li><a class="reference internal" href="#operation" id="id186">Operation</a></li> <li><a class="reference internal" href="#operation" id="id188">Operation</a></li>
<li><a class="reference internal" href="#explosions" id="id187">Explosions</a></li> <li><a class="reference internal" href="#explosions" id="id189">Explosions</a></li>
<li><a class="reference internal" href="#save-files" id="id188">Save files</a></li> <li><a class="reference internal" href="#save-files" id="id190">Save files</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#add-spatter" id="id189">Add Spatter</a></li> <li><a class="reference internal" href="#add-spatter" id="id191">Add Spatter</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -2232,7 +2234,10 @@ With this tweak, items made from cloth and leather will gain a level of wear eve
</td> </td>
</tr> </tr>
<tr class="field"><th class="field-name" colspan="2">adamantine-cloth-wear:</th></tr> <tr class="field"><th class="field-name" colspan="2">adamantine-cloth-wear:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first last">Prevents adamantine clothing from wearing out while being worn (bug 6481).</p> <tr class="field"><td>&nbsp;</td><td class="field-body"><p class="first">Prevents adamantine clothing from wearing out while being worn (bug 6481).</p>
</td>
</tr>
<tr class="field"><th class="field-name">confirm-embark:</th><td class="field-body"><p class="first last">Adds a prompt before embarking (on the &quot;prepare carefully&quot; screen).</p>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -2957,8 +2962,37 @@ twice.</p>
</ul> </ul>
</div> </div>
</div> </div>
<div class="section" id="embark-tools">
<h3><a class="toc-backref" href="#id129">embark-tools</a></h3>
<p>A collection of embark-related tools.</p>
<p>Usage:</p>
<pre class="literal-block">
embark-tools enable/disable tool [tool]...
</pre>
<p>Tools:</p>
<ul class="simple">
<li><tt class="docutils literal">anywhere</tt>: Allows embarking anywhere (including sites, mountain-only biomes, and oceans). Use with caution.</li>
<li><tt class="docutils literal">nano</tt>: An implementation of nano embark - allows resizing below 2x2 when enabled.</li>
<li><tt class="docutils literal">sand</tt>: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators.</li>
<li><tt class="docutils literal">sticky</tt>: Maintains the selected local area while navigating the world map</li>
</ul>
</div>
<div class="section" id="petcapremover">
<h3><a class="toc-backref" href="#id130">petcapRemover</a></h3>
<p>This plugin allows you to remove or raise the pet population cap. In vanilla DF, pets will not reproduce unless the population is below 50 and the number of children of that species is below a certain percentage. This plugin allows removing the second restriction and removing or raising the first. Pets still require PET or PET_EXOTIC tags in order to reproduce. Type help petcapRemover for exact usage. In order to make population more stable and avoid sudden population booms as you go below the raised population cap, this plugin counts pregnancies toward the new population cap. It can still go over, but only in the case of multiple births.</p>
<dl class="docutils">
<dt><cite>petcapRemover</cite></dt>
<dd>cause pregnancies now and schedule the next check</dd>
<dt><cite>petcapRemover every n</cite></dt>
<dd>set how often in ticks the plugin checks for possible pregnancies</dd>
<dt><cite>petcapRemover cap n</cite></dt>
<dd>set the new cap to n. if n = 0, no cap</dd>
<dt><cite>petcapRemover pregtime n</cite></dt>
<dd>sets the pregnancy duration to n ticks. natural pregnancies are 300000 ticks for the current race and 200000 for everyone else</dd>
</dl>
</div>
<div class="section" id="misery"> <div class="section" id="misery">
<h3><a class="toc-backref" href="#id129">misery</a></h3> <h3><a class="toc-backref" href="#id131">misery</a></h3>
<p>When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).</p> <p>When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).</p>
<p>Usage:</p> <p>Usage:</p>
<table class="docutils field-list" frame="void" rules="none"> <table class="docutils field-list" frame="void" rules="none">
@ -2980,7 +3014,7 @@ twice.</p>
</table> </table>
</div> </div>
<div class="section" id="strangemood"> <div class="section" id="strangemood">
<h3><a class="toc-backref" href="#id130">strangemood</a></h3> <h3><a class="toc-backref" href="#id132">strangemood</a></h3>
<p>Creates a strange mood job the same way the game itself normally does it.</p> <p>Creates a strange mood job the same way the game itself normally does it.</p>
<p>Options:</p> <p>Options:</p>
<blockquote> <blockquote>
@ -3004,13 +3038,13 @@ Valid values are &quot;miner&quot;, &quot;carpenter&quot;, &quot;engraver&quot;,
<p>Known limitations: if the selected unit is currently performing a job, the mood will not be started.</p> <p>Known limitations: if the selected unit is currently performing a job, the mood will not be started.</p>
</div> </div>
<div class="section" id="log-region"> <div class="section" id="log-region">
<h3><a class="toc-backref" href="#id131">log-region</a></h3> <h3><a class="toc-backref" href="#id133">log-region</a></h3>
<p>When enabled in dfhack.init, each time a fort is loaded identifying information will be written to the gamelog. Assists in parsing the file if you switch between forts, and adds information for story-building.</p> <p>When enabled in dfhack.init, each time a fort is loaded identifying information will be written to the gamelog. Assists in parsing the file if you switch between forts, and adds information for story-building.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="section" id="scripts"> <div class="section" id="scripts">
<h1><a class="toc-backref" href="#id132">Scripts</a></h1> <h1><a class="toc-backref" href="#id134">Scripts</a></h1>
<p>Lua or ruby scripts placed in the hack/scripts/ directory are considered for <p>Lua or ruby scripts placed in the hack/scripts/ directory are considered for
execution as if they were native DFHack commands. They are listed at the end execution as if they were native DFHack commands. They are listed at the end
of the 'ls' command output.</p> of the 'ls' command output.</p>
@ -3019,7 +3053,7 @@ only be listed by ls if called as 'ls -a'. This is intended as a way to hide
scripts that are obscure, developer-oriented, or should be used as keybindings.</p> scripts that are obscure, developer-oriented, or should be used as keybindings.</p>
<p>Some notable scripts:</p> <p>Some notable scripts:</p>
<div class="section" id="fix"> <div class="section" id="fix">
<h2><a class="toc-backref" href="#id133">fix/*</a></h2> <h2><a class="toc-backref" href="#id135">fix/*</a></h2>
<p>Scripts in this subdirectory fix various bugs and issues, some of them obscure.</p> <p>Scripts in this subdirectory fix various bugs and issues, some of them obscure.</p>
<ul> <ul>
<li><p class="first">fix/dead-units</p> <li><p class="first">fix/dead-units</p>
@ -3056,12 +3090,12 @@ hopefully avoid jamming it again, and unsuspends them.</p>
</ul> </ul>
</div> </div>
<div class="section" id="gui"> <div class="section" id="gui">
<h2><a class="toc-backref" href="#id134">gui/*</a></h2> <h2><a class="toc-backref" href="#id136">gui/*</a></h2>
<p>Scripts that implement dialogs inserted into the main game window are put in this <p>Scripts that implement dialogs inserted into the main game window are put in this
directory.</p> directory.</p>
</div> </div>
<div class="section" id="binpatch"> <div class="section" id="binpatch">
<h2><a class="toc-backref" href="#id135">binpatch</a></h2> <h2><a class="toc-backref" href="#id137">binpatch</a></h2>
<p>Checks, applies or removes binary patches directly in memory at runtime:</p> <p>Checks, applies or removes binary patches directly in memory at runtime:</p>
<pre class="literal-block"> <pre class="literal-block">
binpatch check/apply/remove &lt;patchname&gt; binpatch check/apply/remove &lt;patchname&gt;
@ -3071,17 +3105,17 @@ script uses <tt class="docutils literal"><span class="pre">hack/patches/&lt;df-v
the version appropriate for the currently loaded executable.</p> the version appropriate for the currently loaded executable.</p>
</div> </div>
<div class="section" id="quicksave"> <div class="section" id="quicksave">
<h2><a class="toc-backref" href="#id136">quicksave</a></h2> <h2><a class="toc-backref" href="#id138">quicksave</a></h2>
<p>If called in dwarf mode, makes DF immediately auto-save the game by setting a flag <p>If called in dwarf mode, makes DF immediately auto-save the game by setting a flag
normally used in seasonal auto-save.</p> normally used in seasonal auto-save.</p>
</div> </div>
<div class="section" id="setfps"> <div class="section" id="setfps">
<h2><a class="toc-backref" href="#id137">setfps</a></h2> <h2><a class="toc-backref" href="#id139">setfps</a></h2>
<p>Run <tt class="docutils literal">setfps &lt;number&gt;</tt> to set the FPS cap at runtime, in case you want to watch <p>Run <tt class="docutils literal">setfps &lt;number&gt;</tt> to set the FPS cap at runtime, in case you want to watch
combat in slow motion or something :)</p> combat in slow motion or something :)</p>
</div> </div>
<div class="section" id="siren"> <div class="section" id="siren">
<h2><a class="toc-backref" href="#id138">siren</a></h2> <h2><a class="toc-backref" href="#id140">siren</a></h2>
<p>Wakes up sleeping units, cancels breaks and stops parties either everywhere, <p>Wakes up sleeping units, cancels breaks and stops parties either everywhere,
or in the burrows given as arguments. In return, adds bad thoughts about or in the burrows given as arguments. In return, adds bad thoughts about
noise, tiredness and lack of protection. Also, the units with interrupted noise, tiredness and lack of protection. Also, the units with interrupted
@ -3089,7 +3123,7 @@ breaks will go on break again a lot sooner. The script is intended for
emergencies, e.g. when a siege appears, and all your military is partying.</p> emergencies, e.g. when a siege appears, and all your military is partying.</p>
</div> </div>
<div class="section" id="growcrops"> <div class="section" id="growcrops">
<h2><a class="toc-backref" href="#id139">growcrops</a></h2> <h2><a class="toc-backref" href="#id141">growcrops</a></h2>
<p>Instantly grow seeds inside farming plots.</p> <p>Instantly grow seeds inside farming plots.</p>
<p>With no argument, this command list the various seed types currently in <p>With no argument, this command list the various seed types currently in
use in your farming plots. use in your farming plots.
@ -3101,7 +3135,7 @@ growcrops plump 40
</pre> </pre>
</div> </div>
<div class="section" id="removebadthoughts"> <div class="section" id="removebadthoughts">
<h2><a class="toc-backref" href="#id140">removebadthoughts</a></h2> <h2><a class="toc-backref" href="#id142">removebadthoughts</a></h2>
<p>This script remove negative thoughts from your dwarves. Very useful against <p>This script remove negative thoughts from your dwarves. Very useful against
tantrum spirals.</p> tantrum spirals.</p>
<p>The script can target a single creature, when used with the <tt class="docutils literal">him</tt> argument, <p>The script can target a single creature, when used with the <tt class="docutils literal">him</tt> argument,
@ -3115,7 +3149,7 @@ but in the short term your dwarves will get much more joyful.</p>
quickly after you unpause.</p> quickly after you unpause.</p>
</div> </div>
<div class="section" id="exterminate"> <div class="section" id="exterminate">
<h2><a class="toc-backref" href="#id141">exterminate</a></h2> <h2><a class="toc-backref" href="#id143">exterminate</a></h2>
<p>Kills any unit of a given race.</p> <p>Kills any unit of a given race.</p>
<p>With no argument, lists the available races and count eligible targets.</p> <p>With no argument, lists the available races and count eligible targets.</p>
<p>With the special argument <tt class="docutils literal">him</tt>, targets only the selected creature.</p> <p>With the special argument <tt class="docutils literal">him</tt>, targets only the selected creature.</p>
@ -3147,7 +3181,7 @@ exterminate elve magma
</pre> </pre>
</div> </div>
<div class="section" id="source"> <div class="section" id="source">
<h2><a class="toc-backref" href="#id142">source</a></h2> <h2><a class="toc-backref" href="#id144">source</a></h2>
<p>Create an infinite magma or water source or drain on a tile.</p> <p>Create an infinite magma or water source or drain on a tile.</p>
<p>This script registers a map tile as a liquid source, and every 12 game ticks <p>This script registers a map tile as a liquid source, and every 12 game ticks
that tile receives or remove 1 new unit of flow based on the configuration.</p> that tile receives or remove 1 new unit of flow based on the configuration.</p>
@ -3169,14 +3203,14 @@ source add water 0 - water drain
</pre> </pre>
</div> </div>
<div class="section" id="masspit"> <div class="section" id="masspit">
<h2><a class="toc-backref" href="#id143">masspit</a></h2> <h2><a class="toc-backref" href="#id145">masspit</a></h2>
<p>Designate all creatures in cages on top of a pit/pond activity zone for pitting. <p>Designate all creatures in cages on top of a pit/pond activity zone for pitting.
Works best with an animal stockpile on top of the zone.</p> Works best with an animal stockpile on top of the zone.</p>
<p>Works with a zone number as argument (eg <tt class="docutils literal">Activity Zone #6</tt> -&gt; <tt class="docutils literal">masspit 6</tt>) <p>Works with a zone number as argument (eg <tt class="docutils literal">Activity Zone #6</tt> -&gt; <tt class="docutils literal">masspit 6</tt>)
or with the game cursor on top of the area.</p> or with the game cursor on top of the area.</p>
</div> </div>
<div class="section" id="digfort"> <div class="section" id="digfort">
<h2><a class="toc-backref" href="#id144">digfort</a></h2> <h2><a class="toc-backref" href="#id146">digfort</a></h2>
<p>A script to designate an area for digging according to a plan in csv format.</p> <p>A script to designate an area for digging according to a plan in csv format.</p>
<p>This script, inspired from quickfort, can designate an area for digging. <p>This script, inspired from quickfort, can designate an area for digging.
Your plan should be stored in a .csv file like this:</p> Your plan should be stored in a .csv file like this:</p>
@ -3198,7 +3232,7 @@ as an offset for the pattern: instead of starting at the cursor, it will start
Dwarf Fortress.exe is found).</p> Dwarf Fortress.exe is found).</p>
</div> </div>
<div class="section" id="invasion-now"> <div class="section" id="invasion-now">
<h2><a class="toc-backref" href="#id145">invasion-now</a></h2> <h2><a class="toc-backref" href="#id147">invasion-now</a></h2>
<p>Triggers an invasion, or several in the near future.</p> <p>Triggers an invasion, or several in the near future.</p>
<p><cite>invasion-now civName</cite> trigger an invasion from the civilization with the id civName, starting in about ten ticks</p> <p><cite>invasion-now civName</cite> trigger an invasion from the civilization with the id civName, starting in about ten ticks</p>
<p><cite>invasion-now civName start</cite> trigger an invasion from civName in a number of ticks between 10*start and 11*start-1 (inclusive)</p> <p><cite>invasion-now civName start</cite> trigger an invasion from civName in a number of ticks between 10*start and 11*start-1 (inclusive)</p>
@ -3206,7 +3240,7 @@ Dwarf Fortress.exe is found).</p>
<p>Probably fails if the start time of a triggered invasion is later than the start of the next year.</p> <p>Probably fails if the start time of a triggered invasion is later than the start of the next year.</p>
</div> </div>
<div class="section" id="digmat"> <div class="section" id="digmat">
<h2><a class="toc-backref" href="#id146">digmat</a></h2> <h2><a class="toc-backref" href="#id148">digmat</a></h2>
<p>Designates a tile for digging. Monitors the tile, and when it is dug out, add <p>Designates a tile for digging. Monitors the tile, and when it is dug out, add
surrounding discovered tiles of the same material for digging. Similar to 'digv', surrounding discovered tiles of the same material for digging. Similar to 'digv',
but less cheaty. Works for stone layers, soil layers, veins, etc.</p> but less cheaty. Works for stone layers, soil layers, veins, etc.</p>
@ -3215,7 +3249,7 @@ same designation for future digging (eg dig up/downstairs). When digging stairs,
also designate tiles on z-1 and z+1 when they are discovered.</p> also designate tiles on z-1 and z+1 when they are discovered.</p>
</div> </div>
<div class="section" id="superdwarf"> <div class="section" id="superdwarf">
<h2><a class="toc-backref" href="#id147">superdwarf</a></h2> <h2><a class="toc-backref" href="#id149">superdwarf</a></h2>
<p>Similar to fastdwarf, per-creature.</p> <p>Similar to fastdwarf, per-creature.</p>
<p>To make any creature superfast, target it ingame using 'v' and:</p> <p>To make any creature superfast, target it ingame using 'v' and:</p>
<pre class="literal-block"> <pre class="literal-block">
@ -3225,17 +3259,17 @@ superdwarf add
<p>This plugin also shortens the 'sleeping' and 'on break' periods of targets.</p> <p>This plugin also shortens the 'sleeping' and 'on break' periods of targets.</p>
</div> </div>
<div class="section" id="drainaquifer"> <div class="section" id="drainaquifer">
<h2><a class="toc-backref" href="#id148">drainaquifer</a></h2> <h2><a class="toc-backref" href="#id150">drainaquifer</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">
<h2><a class="toc-backref" href="#id149">deathcause</a></h2> <h2><a class="toc-backref" href="#id151">deathcause</a></h2>
<p>Focus a body part ingame, and this script will display the cause of death of <p>Focus a body part ingame, and this script will display the cause of death of
the creature. the creature.
Also works when selecting units from the 'u'nitlist viewscreen.</p> Also works when selecting units from the 'u'nitlist viewscreen.</p>
</div> </div>
<div class="section" id="lua"> <div class="section" id="lua">
<h2><a class="toc-backref" href="#id150">lua</a></h2> <h2><a class="toc-backref" href="#id152">lua</a></h2>
<p>There are the following ways to invoke this command:</p> <p>There are the following ways to invoke this command:</p>
<ol class="arabic"> <ol class="arabic">
<li><p class="first"><tt class="docutils literal">lua</tt> (without any parameters)</p> <li><p class="first"><tt class="docutils literal">lua</tt> (without any parameters)</p>
@ -3254,11 +3288,11 @@ directory. If the filename is not supplied, it loads &quot;dfhack.lua&quot;.</p>
</ol> </ol>
</div> </div>
<div class="section" id="embark"> <div class="section" id="embark">
<h2><a class="toc-backref" href="#id151">embark</a></h2> <h2><a class="toc-backref" href="#id153">embark</a></h2>
<p>Allows to embark anywhere. Currently windows only.</p> <p>Allows to embark anywhere. Currently windows only.</p>
</div> </div>
<div class="section" id="lever"> <div class="section" id="lever">
<h2><a class="toc-backref" href="#id152">lever</a></h2> <h2><a class="toc-backref" href="#id154">lever</a></h2>
<p>Allow manipulation of in-game levers from the dfhack console.</p> <p>Allow manipulation of in-game levers from the dfhack console.</p>
<p>Can list levers, including state and links, with:</p> <p>Can list levers, including state and links, with:</p>
<pre class="literal-block"> <pre class="literal-block">
@ -3272,7 +3306,7 @@ lever pull 42 --now
</pre> </pre>
</div> </div>
<div class="section" id="stripcaged"> <div class="section" id="stripcaged">
<h2><a class="toc-backref" href="#id153">stripcaged</a></h2> <h2><a class="toc-backref" href="#id155">stripcaged</a></h2>
<p>For dumping items inside cages. Will mark selected items for dumping, then <p>For dumping items inside cages. Will mark selected items for dumping, then
a dwarf may come and actually dump it. See also <tt class="docutils literal">autodump</tt>.</p> a dwarf may come and actually dump it. See also <tt class="docutils literal">autodump</tt>.</p>
<p>With the <tt class="docutils literal">items</tt> argument, only dumps items laying in the cage, excluding <p>With the <tt class="docutils literal">items</tt> argument, only dumps items laying in the cage, excluding
@ -3290,11 +3324,11 @@ stripcaged weapons 25321 34228
</pre> </pre>
</div> </div>
<div class="section" id="undump-buildings"> <div class="section" id="undump-buildings">
<h2><a class="toc-backref" href="#id154">undump-buildings</a></h2> <h2><a class="toc-backref" href="#id156">undump-buildings</a></h2>
<p>Undesignates building base materials for dumping.</p> <p>Undesignates building base materials for dumping.</p>
</div> </div>
<div class="section" id="create-items"> <div class="section" id="create-items">
<h2><a class="toc-backref" href="#id155">create-items</a></h2> <h2><a class="toc-backref" href="#id157">create-items</a></h2>
<p>Spawn arbitrary items under the cursor.</p> <p>Spawn arbitrary items under the cursor.</p>
<p>The first argument gives the item category, the second gives the material, <p>The first argument gives the item category, the second gives the material,
and the optionnal third gives the number of items to create (defaults to 20).</p> and the optionnal third gives the number of items to create (defaults to 20).</p>
@ -3316,7 +3350,7 @@ create-items bar adamantine
</pre> </pre>
</div> </div>
<div class="section" id="locate-ore"> <div class="section" id="locate-ore">
<h2><a class="toc-backref" href="#id156">locate-ore</a></h2> <h2><a class="toc-backref" href="#id158">locate-ore</a></h2>
<p>Scan the map for metal ores.</p> <p>Scan the map for metal ores.</p>
<p>Finds and designate for digging one tile of a specific metal ore. <p>Finds and designate for digging one tile of a specific metal ore.
Only works for native metal ores, does not handle reaction stuff (eg STEEL).</p> Only works for native metal ores, does not handle reaction stuff (eg STEEL).</p>
@ -3329,7 +3363,7 @@ locate-ore iron</dd>
</dl> </dl>
</div> </div>
<div class="section" id="soundsense-season"> <div class="section" id="soundsense-season">
<h2><a class="toc-backref" href="#id157">soundsense-season</a></h2> <h2><a class="toc-backref" href="#id159">soundsense-season</a></h2>
<p>It is a well known issue that Soundsense cannot detect the correct <p>It is a well known issue that Soundsense cannot detect the correct
current season when a savegame is loaded and has to play random current season when a savegame is loaded and has to play random
season music until a season switch occurs.</p> season music until a season switch occurs.</p>
@ -3338,7 +3372,7 @@ to gamelog.txt on every map load to fix this. For best results
call the script from <tt class="docutils literal">dfhack.init</tt>.</p> call the script from <tt class="docutils literal">dfhack.init</tt>.</p>
</div> </div>
<div class="section" id="multicmd"> <div class="section" id="multicmd">
<h2><a class="toc-backref" href="#id158">multicmd</a></h2> <h2><a class="toc-backref" href="#id160">multicmd</a></h2>
<p>Run multiple dfhack commands. The argument is split around the <p>Run multiple dfhack commands. The argument is split around the
character ; and all parts are run sequencially as independent character ; and all parts are run sequencially as independent
dfhack commands. Useful for hotkeys.</p> dfhack commands. Useful for hotkeys.</p>
@ -3348,12 +3382,12 @@ dfhack commands. Useful for hotkeys.</p>
</dl> </dl>
</div> </div>
<div class="section" id="dfstatus"> <div class="section" id="dfstatus">
<h2><a class="toc-backref" href="#id159">dfstatus</a></h2> <h2><a class="toc-backref" href="#id161">dfstatus</a></h2>
<p>Show a quick overview of critical stock quantities, including food, dirnks, wood, and various bars.</p> <p>Show a quick overview of critical stock quantities, including food, dirnks, wood, and various bars.</p>
</div> </div>
</div> </div>
<div class="section" id="in-game-interface-tools"> <div class="section" id="in-game-interface-tools">
<h1><a class="toc-backref" href="#id160">In-game interface tools</a></h1> <h1><a class="toc-backref" href="#id162">In-game interface tools</a></h1>
<p>These tools work by displaying dialogs or overlays in the game window, and <p>These tools work by displaying dialogs or overlays in the game window, and
are mostly implemented by lua scripts.</p> are mostly implemented by lua scripts.</p>
<div class="note"> <div class="note">
@ -3368,18 +3402,20 @@ guideline because it arguably just fixes small usability bugs in the game UI.</p
you must enable the plugins which provide them.</p> you must enable the plugins which provide them.</p>
</div> </div>
<div class="section" id="dwarf-manipulator"> <div class="section" id="dwarf-manipulator">
<h2><a class="toc-backref" href="#id161">Dwarf Manipulator</a></h2> <h2><a class="toc-backref" href="#id163">Dwarf Manipulator</a></h2>
<p>Implemented by the 'manipulator' plugin.</p> <p>Implemented by the 'manipulator' plugin.</p>
<p>To activate, open the unit screen and press 'l'.</p> <p>To activate, open the unit screen and press 'l'.</p>
<img alt="images/manipulator.png" src="images/manipulator.png" /> <img alt="images/manipulator.png" src="images/manipulator.png" />
<p>This tool implements a Dwarf Therapist-like interface within the game UI. The <p>This tool implements a Dwarf Therapist-like interface within the game UI. The
far left column displays the unit's Happiness (color-coded based on its far left column displays the unit's Happiness (color-coded based on its
value), and the right half of the screen displays each dwarf's labor settings value), Name, Profession/Squad, and the right half of the screen displays each
and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand dwarf's labor settings and skill levels (0-9 for Dabbling thru Professional, A-E for
Master, and U-Z for Legendary thru Legendary+5).</p> Great thru Grand Master, and U-Z for Legendary thru Legendary+5).</p>
<p>Cells with teal backgrounds denote skills not controlled by labors, e.g. <p>Cells with teal backgrounds denote skills not controlled by labors, e.g.
military and social skills.</p> military and social skills.</p>
<img alt="images/manipulator2.png" src="images/manipulator2.png" /> <img alt="images/manipulator2.png" src="images/manipulator2.png" />
<p>Press <tt class="docutils literal">t</tt> to toggle between Profession and Squad view.</p>
<img alt="images/manipulator3.png" src="images/manipulator3.png" />
<p>Use the arrow keys or number pad to move the cursor around, holding Shift to <p>Use the arrow keys or number pad to move the cursor around, holding Shift to
move 10 tiles at a time.</p> move 10 tiles at a time.</p>
<p>Press the Z-Up (&lt;) and Z-Down (&gt;) keys to move quickly between labor/skill <p>Press the Z-Up (&lt;) and Z-Down (&gt;) keys to move quickly between labor/skill
@ -3388,7 +3424,7 @@ in the list. Backspace seeks to the top left corner.</p>
<p>Press Enter to toggle the selected labor for the selected unit, or Shift+Enter <p>Press Enter to toggle the selected labor for the selected unit, or Shift+Enter
to toggle all labors within the selected category.</p> to toggle all labors within the selected category.</p>
<p>Press the <tt class="docutils literal">+-</tt> keys to sort the unit list according to the currently selected <p>Press the <tt class="docutils literal">+-</tt> keys to sort the unit list according to the currently selected
skill/labor, and press the <tt class="docutils literal">*/</tt> keys to sort the unit list by Name, Profession, skill/labor, and press the <tt class="docutils literal">*/</tt> keys to sort the unit list by Name, Profession/Squad,
Happiness, or Arrival order (using Tab to select which sort method to use here).</p> Happiness, or Arrival order (using Tab to select which sort method to use here).</p>
<p>With a unit selected, you can press the &quot;v&quot; key to view its properties (and <p>With a unit selected, you can press the &quot;v&quot; key to view its properties (and
possibly set a custom nickname or profession) or the &quot;c&quot; key to exit possibly set a custom nickname or profession) or the &quot;c&quot; key to exit
@ -3396,18 +3432,18 @@ Manipulator and zoom to its position within your fortress.</p>
<p>The following mouse shortcuts are also available:</p> <p>The following mouse shortcuts are also available:</p>
<ul class="simple"> <ul class="simple">
<li>Click on a column header to sort the unit list. Left-click to sort it in one <li>Click on a column header to sort the unit list. Left-click to sort it in one
direction (descending for happiness or labors/skills, ascending for name or direction (descending for happiness or labors/skills, ascending for name,
profession) and right-click to sort it in the opposite direction.</li> profession or squad) and right-click to sort it in the opposite direction.</li>
<li>Left-click on a labor cell to toggle that labor. Right-click to move the <li>Left-click on a labor cell to toggle that labor. Right-click to move the
cursor onto that cell instead of toggling it.</li> cursor onto that cell instead of toggling it.</li>
<li>Left-click on a unit's name or profession to view its properties.</li> <li>Left-click on a unit's name, profession or squad to view its properties.</li>
<li>Right-click on a unit's name or profession to zoom to it.</li> <li>Right-click on a unit's name, profession or squad to zoom to it.</li>
</ul> </ul>
<p>Pressing ESC normally returns to the unit screen, but Shift-ESC would exit <p>Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen.</p> directly to the main dwarf mode screen.</p>
</div> </div>
<div class="section" id="search"> <div class="section" id="search">
<h2><a class="toc-backref" href="#id162">Search</a></h2> <h2><a class="toc-backref" href="#id164">Search</a></h2>
<p>Implemented by the 'search' plugin.</p> <p>Implemented by the 'search' plugin.</p>
<p>The search plugin adds search to the Stocks, Animals, Trading, Stockpile, <p>The search plugin adds search to the Stocks, Animals, Trading, Stockpile,
Noble (assignment candidates), Military (position candidates), Burrows Noble (assignment candidates), Military (position candidates), Burrows
@ -3438,7 +3474,7 @@ only fat or tallow by forbidding fats, then searching for fat/tallow, and
using Permit Fats again while the list is filtered.</p> using Permit Fats again while the list is filtered.</p>
</div> </div>
<div class="section" id="automaterial"> <div class="section" id="automaterial">
<h2><a class="toc-backref" href="#id163">AutoMaterial</a></h2> <h2><a class="toc-backref" href="#id165">AutoMaterial</a></h2>
<p>Implemented by the 'automaterial' plugin.</p> <p>Implemented by the 'automaterial' plugin.</p>
<p>This makes building constructions (walls, floors, fortifications, etc) a little bit <p>This makes building constructions (walls, floors, fortifications, etc) a little bit
easier by saving you from having to trawl through long lists of materials each time easier by saving you from having to trawl through long lists of materials each time
@ -3466,7 +3502,7 @@ materials, it returns you back to this screen. If you use this along with severa
enabled materials, you should be able to place complex constructions more conveniently.</p> enabled materials, you should be able to place complex constructions more conveniently.</p>
</div> </div>
<div class="section" id="gui-liquids"> <div class="section" id="gui-liquids">
<h2><a class="toc-backref" href="#id164">gui/liquids</a></h2> <h2><a class="toc-backref" href="#id166">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,
@ -3486,7 +3522,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="#id165">gui/mechanisms</a></h2> <h2><a class="toc-backref" href="#id167">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
@ -3496,7 +3532,7 @@ 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-rename"> <div class="section" id="gui-rename">
<h2><a class="toc-backref" href="#id166">gui/rename</a></h2> <h2><a class="toc-backref" href="#id168">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>
@ -3519,7 +3555,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="#id167">gui/room-list</a></h2> <h2><a class="toc-backref" href="#id169">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" />
@ -3527,7 +3563,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-choose-weapons"> <div class="section" id="gui-choose-weapons">
<h2><a class="toc-backref" href="#id168">gui/choose-weapons</a></h2> <h2><a class="toc-backref" href="#id170">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
@ -3538,14 +3574,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="#id169">gui/clone-uniform</a></h2> <h2><a class="toc-backref" href="#id171">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-guide-path"> <div class="section" id="gui-guide-path">
<h2><a class="toc-backref" href="#id170">gui/guide-path</a></h2> <h2><a class="toc-backref" href="#id172">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" />
@ -3553,7 +3589,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-workshop-job"> <div class="section" id="gui-workshop-job">
<h2><a class="toc-backref" href="#id171">gui/workshop-job</a></h2> <h2><a class="toc-backref" href="#id173">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" />
@ -3589,7 +3625,7 @@ and then try to change the input item type, now it won't let you select <em>plan
you have to unset the material first.</p> you have to unset the material first.</p>
</div> </div>
<div class="section" id="gui-workflow"> <div class="section" id="gui-workflow">
<h2><a class="toc-backref" href="#id172">gui/workflow</a></h2> <h2><a class="toc-backref" href="#id174">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" />
@ -3636,7 +3672,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-assign-rack"> <div class="section" id="gui-assign-rack">
<h2><a class="toc-backref" href="#id173">gui/assign-rack</a></h2> <h2><a class="toc-backref" href="#id175">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" />
@ -3660,7 +3696,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-advfort"> <div class="section" id="gui-advfort">
<h2><a class="toc-backref" href="#id174">gui/advfort</a></h2> <h2><a class="toc-backref" href="#id176">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>
@ -3679,7 +3715,7 @@ implies -a</li>
</div> </div>
</div> </div>
<div class="section" id="gui-companion-order"> <div class="section" id="gui-companion-order">
<h2><a class="toc-backref" href="#id175">gui/companion-order</a></h2> <h2><a class="toc-backref" href="#id177">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" />
@ -3695,7 +3731,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="#id176">gui/gm-editor</a></h2> <h2><a class="toc-backref" href="#id178">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
@ -3710,14 +3746,14 @@ the same as version above.</li>
in-game help.</p> in-game help.</p>
</div> </div>
<div class="section" id="gui-mod-manager"> <div class="section" id="gui-mod-manager">
<h2><a class="toc-backref" href="#id177">gui/mod-manager</a></h2> <h2><a class="toc-backref" href="#id179">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> </div>
<div class="section" id="behavior-mods"> <div class="section" id="behavior-mods">
<h1><a class="toc-backref" href="#id178">Behavior Mods</a></h1> <h1><a class="toc-backref" href="#id180">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>
@ -3728,20 +3764,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="#id179">Siege Engine</a></h2> <h2><a class="toc-backref" href="#id181">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="#id180">Rationale</a></h3> <h3><a class="toc-backref" href="#id182">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="#id181">Configuration UI</a></h3> <h3><a class="toc-backref" href="#id183">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>
@ -3764,7 +3800,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="#id182">Power Meter</a></h2> <h2><a class="toc-backref" href="#id184">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
@ -3775,11 +3811,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="#id183">Steam Engine</a></h2> <h2><a class="toc-backref" href="#id185">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="#id184">Rationale</a></h3> <h3><a class="toc-backref" href="#id186">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
@ -3790,7 +3826,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="#id185">Construction</a></h3> <h3><a class="toc-backref" href="#id187">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>
@ -3814,7 +3850,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="#id186">Operation</a></h3> <h3><a class="toc-backref" href="#id188">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
@ -3845,7 +3881,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="#id187">Explosions</a></h3> <h3><a class="toc-backref" href="#id189">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
@ -3854,7 +3890,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="#id188">Save files</a></h3> <h3><a class="toc-backref" href="#id190">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
@ -3865,7 +3901,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="#id189">Add Spatter</a></h2> <h2><a class="toc-backref" href="#id191">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

@ -1397,6 +1397,8 @@ Subcommands that persist until disabled or DF quit:
:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (bug 6481). :adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (bug 6481).
:confirm-embark: Adds a prompt before embarking (on the "prepare carefully" screen).
fix-armory fix-armory
---------- ----------
@ -2053,6 +2055,35 @@ See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic
* Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
* The game will be suspended while you're using dfusion. Don't panic when it doesn't respond. * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond.
embark-tools
------------
A collection of embark-related tools.
Usage::
embark-tools enable/disable tool [tool]...
Tools:
* ``anywhere``: Allows embarking anywhere (including sites, mountain-only biomes, and oceans). Use with caution.
* ``nano``: An implementation of nano embark - allows resizing below 2x2 when enabled.
* ``sand``: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators.
* ``sticky``: Maintains the selected local area while navigating the world map
petcapRemover
-------------
This plugin allows you to remove or raise the pet population cap. In vanilla DF, pets will not reproduce unless the population is below 50 and the number of children of that species is below a certain percentage. This plugin allows removing the second restriction and removing or raising the first. Pets still require PET or PET_EXOTIC tags in order to reproduce. Type help petcapRemover for exact usage. In order to make population more stable and avoid sudden population booms as you go below the raised population cap, this plugin counts pregnancies toward the new population cap. It can still go over, but only in the case of multiple births.
`petcapRemover`
cause pregnancies now and schedule the next check
`petcapRemover every n`
set how often in ticks the plugin checks for possible pregnancies
`petcapRemover cap n`
set the new cap to n. if n = 0, no cap
`petcapRemover pregtime n`
sets the pregnancy duration to n ticks. natural pregnancies are 300000 ticks for the current race and 200000 for everyone else
misery misery
------ ------
When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default). When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).
@ -2523,15 +2554,19 @@ To activate, open the unit screen and press 'l'.
This tool implements a Dwarf Therapist-like interface within the game UI. The This tool implements a Dwarf Therapist-like interface within the game UI. The
far left column displays the unit's Happiness (color-coded based on its far left column displays the unit's Happiness (color-coded based on its
value), and the right half of the screen displays each dwarf's labor settings value), Name, Profession/Squad, and the right half of the screen displays each
and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand dwarf's labor settings and skill levels (0-9 for Dabbling thru Professional, A-E for
Master, and U-Z for Legendary thru Legendary+5). Great thru Grand Master, and U-Z for Legendary thru Legendary+5).
Cells with teal backgrounds denote skills not controlled by labors, e.g. Cells with teal backgrounds denote skills not controlled by labors, e.g.
military and social skills. military and social skills.
.. image:: images/manipulator2.png .. image:: images/manipulator2.png
Press ``t`` to toggle between Profession and Squad view.
.. image:: images/manipulator3.png
Use the arrow keys or number pad to move the cursor around, holding Shift to Use the arrow keys or number pad to move the cursor around, holding Shift to
move 10 tiles at a time. move 10 tiles at a time.
@ -2543,7 +2578,7 @@ Press Enter to toggle the selected labor for the selected unit, or Shift+Enter
to toggle all labors within the selected category. to toggle all labors within the selected category.
Press the ``+-`` keys to sort the unit list according to the currently selected Press the ``+-`` keys to sort the unit list according to the currently selected
skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession, skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession/Squad,
Happiness, or Arrival order (using Tab to select which sort method to use here). Happiness, or Arrival order (using Tab to select which sort method to use here).
With a unit selected, you can press the "v" key to view its properties (and With a unit selected, you can press the "v" key to view its properties (and
@ -2553,12 +2588,12 @@ Manipulator and zoom to its position within your fortress.
The following mouse shortcuts are also available: The following mouse shortcuts are also available:
* Click on a column header to sort the unit list. Left-click to sort it in one * Click on a column header to sort the unit list. Left-click to sort it in one
direction (descending for happiness or labors/skills, ascending for name or direction (descending for happiness or labors/skills, ascending for name,
profession) and right-click to sort it in the opposite direction. profession or squad) and right-click to sort it in the opposite direction.
* Left-click on a labor cell to toggle that labor. Right-click to move the * Left-click on a labor cell to toggle that labor. Right-click to move the
cursor onto that cell instead of toggling it. cursor onto that cell instead of toggling it.
* Left-click on a unit's name or profession to view its properties. * Left-click on a unit's name, profession or squad to view its properties.
* Right-click on a unit's name or profession to zoom to it. * Right-click on a unit's name, profession or squad to zoom to it.
Pressing ESC normally returns to the unit screen, but Shift-ESC would exit Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen. directly to the main dwarf mode screen.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

@ -127,6 +127,7 @@ include/modules/Vermin.h
include/modules/World.h include/modules/World.h
include/modules/Graphic.h include/modules/Graphic.h
include/modules/Once.h include/modules/Once.h
include/modules/Filesystem.h
) )
SET( MODULE_SOURCES SET( MODULE_SOURCES
@ -152,6 +153,7 @@ modules/World.cpp
modules/Graphic.cpp modules/Graphic.cpp
modules/Windows.cpp modules/Windows.cpp
modules/Once.cpp modules/Once.cpp
modules/Filesystem.cpp
) )
IF(WIN32) IF(WIN32)

@ -52,6 +52,7 @@ distribution.
#include "modules/Buildings.h" #include "modules/Buildings.h"
#include "modules/Constructions.h" #include "modules/Constructions.h"
#include "modules/Random.h" #include "modules/Random.h"
#include "modules/Filesystem.h"
#include "LuaWrapper.h" #include "LuaWrapper.h"
#include "LuaTools.h" #include "LuaTools.h"
@ -1923,6 +1924,20 @@ static const luaL_Reg dfhack_screen_funcs[] = {
{ NULL, NULL } { NULL, NULL }
}; };
/***** Filesystem module *****/
static const LuaWrapper::FunctionReg dfhack_filesystem_module[] = {
WRAPM(Filesystem, getcwd),
WRAPM(Filesystem, chdir),
WRAPM(Filesystem, mkdir),
WRAPM(Filesystem, rmdir),
WRAPM(Filesystem, exists),
WRAPM(Filesystem, isfile),
WRAPM(Filesystem, isdir),
{NULL, NULL}
};
/***** Internal module *****/ /***** Internal module *****/
static void *checkaddr(lua_State *L, int idx, bool allow_null = false) static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
@ -2227,6 +2242,64 @@ static int internal_getDir(lua_State *L)
} }
return 1; return 1;
} }
static int internal_runCommand(lua_State *L)
{
buffered_color_ostream out;
command_result res;
if (lua_gettop(L) == 0)
{
lua_pushstring(L, "");
}
int type_1 = lua_type(L, 1);
if (type_1 == LUA_TTABLE)
{
std::string command = "";
std::vector<std::string> args;
lua_pushnil(L); // first key
while (lua_next(L, 1) != 0)
{
if (command == "")
command = lua_tostring(L, -1);
else
args.push_back(lua_tostring(L, -1));
lua_pop(L, 1); // remove value, leave key
}
CoreSuspender suspend;
res = Core::getInstance().runCommand(out, command, args);
}
else if (type_1 == LUA_TSTRING)
{
std::string command = lua_tostring(L, 1);
CoreSuspender suspend;
res = Core::getInstance().runCommand(out, command);
}
else
{
lua_pushnil(L);
lua_pushfstring(L, "Expected table, got %s", lua_typename(L, type_1));
return 2;
}
auto fragments = out.fragments();
lua_newtable(L);
lua_pushinteger(L, (int)res);
lua_setfield(L, -2, "status");
int i = 1;
for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++)
{
int color = iter->first;
std::string output = iter->second;
lua_createtable(L, 2, 0);
lua_pushinteger(L, color);
lua_rawseti(L, -2, 1);
lua_pushstring(L, output.c_str());
lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, i);
}
lua_pushvalue(L, -1);
return 1;
}
static const luaL_Reg dfhack_internal_funcs[] = { static const luaL_Reg dfhack_internal_funcs[] = {
{ "getAddress", internal_getAddress }, { "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress }, { "setAddress", internal_setAddress },
@ -2240,6 +2313,7 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "memscan", internal_memscan }, { "memscan", internal_memscan },
{ "diffscan", internal_diffscan }, { "diffscan", internal_diffscan },
{ "getDir", internal_getDir }, { "getDir", internal_getDir },
{ "runCommand", internal_runCommand },
{ NULL, NULL } { NULL, NULL }
}; };
@ -2265,5 +2339,6 @@ void OpenDFHackApi(lua_State *state)
OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs); OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs);
OpenModule(state, "constructions", dfhack_constructions_module); OpenModule(state, "constructions", dfhack_constructions_module);
OpenModule(state, "screen", dfhack_screen_module, dfhack_screen_funcs); OpenModule(state, "screen", dfhack_screen_module, dfhack_screen_funcs);
OpenModule(state, "filesystem", dfhack_filesystem_module);
OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs); OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs);
} }

@ -0,0 +1,194 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/* Based on luafilesystem
Copyright © 2003-2014 Kepler Project.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "Export.h"
#ifndef _WIN32
#ifndef _AIX
#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */
#else
#define _LARGE_FILES 1 /* AIX */
#endif
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#include <io.h>
#include <sys/locking.h>
#ifdef __BORLANDC__
#include <utime.h>
#else
#include <sys/utime.h>
#endif
#include <fcntl.h>
#else
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <utime.h>
#endif
#define LFS_VERSION "1.6.2"
#define LFS_LIBNAME "lfs"
#if LUA_VERSION_NUM < 502
# define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l))
#endif
/* Define 'strerror' for systems that do not implement it */
#ifdef NO_STRERROR
#define strerror(_) "System unable to describe the error"
#endif
/* Define 'getcwd' for systems that do not implement it */
#ifdef NO_GETCWD
#define getcwd(p,s) NULL
#define getcwd_error "Function 'getcwd' not provided by system"
#else
#define getcwd_error strerror(errno)
#ifdef _WIN32
/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
#define LFS_MAXPATHLEN MAX_PATH
#else
/* For MAXPATHLEN: */
#include <sys/param.h>
#define LFS_MAXPATHLEN MAXPATHLEN
#endif
#endif
typedef struct dir_data {
int closed;
#ifdef _WIN32
intptr_t hFile;
char pattern[MAX_PATH+1];
#else
DIR *dir;
#endif
} dir_data;
#ifdef _WIN32
#ifdef __BORLANDC__
#define lfs_setmode(L,file,m) ((void)L, setmode(_fileno(file), m))
#define STAT_STRUCT struct stati64
#else
#define lfs_setmode(L,file,m) ((void)L, _setmode(_fileno(file), m))
#define STAT_STRUCT struct _stati64
#endif
#define STAT_FUNC _stati64
#define LSTAT_FUNC STAT_FUNC
#else
#define _O_TEXT 0
#define _O_BINARY 0
#define lfs_setmode(L,file,m) ((void)L, (void)file, (void)m, 0)
#define STAT_STRUCT struct stat
#define STAT_FUNC stat
#define LSTAT_FUNC lstat
#endif
#ifdef _WIN32
#ifndef S_ISDIR
#define S_ISDIR(mode) (mode&_S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(mode) (mode&_S_IFREG)
#endif
#ifndef S_ISLNK
#define S_ISLNK(mode) (0)
#endif
#ifndef S_ISSOCK
#define S_ISSOCK(mode) (0)
#endif
#ifndef S_ISFIFO
#define S_ISFIFO(mode) (0)
#endif
#ifndef S_ISCHR
#define S_ISCHR(mode) (mode&_S_IFCHR)
#endif
#ifndef S_ISBLK
#define S_ISBLK(mode) (0)
#endif
#endif
enum _filetype {
FILETYPE_NONE = -2,
FILETYPE_UNKNOWN = -1,
FILETYPE_FILE = 1,
FILETYPE_DIRECTORY,
FILETYPE_LINK,
FILETYPE_SOCKET,
FILETYPE_NAMEDPIPE,
FILETYPE_CHAR_DEVICE,
FILETYPE_BLOCK_DEVICE
};
namespace DFHack {
namespace Filesystem {
DFHACK_EXPORT bool chdir (std::string path);
DFHACK_EXPORT std::string getcwd ();
DFHACK_EXPORT bool mkdir (std::string path);
DFHACK_EXPORT bool rmdir (std::string path);
DFHACK_EXPORT bool stat (std::string path, STAT_STRUCT &info);
DFHACK_EXPORT bool exists (std::string path);
DFHACK_EXPORT _filetype filetype (std::string path);
DFHACK_EXPORT bool isfile (std::string path);
DFHACK_EXPORT bool isdir (std::string path);
}
}

@ -256,6 +256,8 @@ DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profes
DFHACK_EXPORT int8_t getProfessionColor(df::unit *unit, bool ignore_noble = false); DFHACK_EXPORT int8_t getProfessionColor(df::unit *unit, bool ignore_noble = false);
DFHACK_EXPORT int8_t getCasteProfessionColor(int race, int caste, df::profession pid); DFHACK_EXPORT int8_t getCasteProfessionColor(int race, int caste, df::profession pid);
DFHACK_EXPORT std::string getSquadName(df::unit *unit);
} }
} }
#endif #endif

@ -10,6 +10,14 @@ local dfhack = dfhack
local base_env = dfhack.BASE_G local base_env = dfhack.BASE_G
local _ENV = base_env local _ENV = base_env
CR_LINK_FAILURE = -3
CR_NEEDS_CONSOLE = -2
CR_NOT_IMPLEMENTED = -1
CR_OK = 0
CR_FAILURE = 1
CR_WRONG_USAGE = 2
CR_NOT_FOUND = 3
-- Console color constants -- Console color constants
COLOR_RESET = -1 COLOR_RESET = -1
@ -256,7 +264,8 @@ function dfhack.interpreter(prompt,hfile,env)
print("Shortcuts:\n".. print("Shortcuts:\n"..
" '= foo' => '_1,_2,... = foo'\n".. " '= foo' => '_1,_2,... = foo'\n"..
" '! foo' => 'print(foo)'\n".. " '! foo' => 'print(foo)'\n"..
"Both save the first result as '_'.") " '~ foo' => 'printall(foo)'\n"..
"All of these save the first result as '_'.")
print_banner = false print_banner = false
end end
@ -357,6 +366,31 @@ function dfhack.run_script(name,...)
return f(...) return f(...)
end end
function dfhack.run_command(...)
args = {...}
if type(args[1]) == 'table' then
command = args[1]
elseif #args > 1 and type(args[2]) == 'table' then
-- {args[1]} + args[2]
command = args[2]
table.insert(command, 1, args[1])
elseif #args == 1 and type(args[1]) == 'string' then
command = args[1]
elseif #args > 1 and type(args[1]) == 'string' then
command = args
else
error('Invalid arguments')
end
result = internal.runCommand(command)
output = ""
for i, f in pairs(result) do
if type(f) == 'table' then
output = output .. f[2]
end
end
return output, result.status
end
-- Per-save init file -- Per-save init file
function dfhack.getSavePath() function dfhack.getSavePath()

@ -0,0 +1,145 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/* Based on luafilesystem
Copyright © 2003-2014 Kepler Project.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <string>
#include "modules/Filesystem.h"
bool DFHack::Filesystem::chdir (std::string path)
{
return !(bool)::chdir(path.c_str());
}
std::string DFHack::Filesystem::getcwd ()
{
char *path;
char buf[LFS_MAXPATHLEN];
std::string result = "";
#ifdef _WIN32
if ((path = ::_getcwd(buf, LFS_MAXPATHLEN)) != NULL)
#else
if ((path = ::getcwd(buf, LFS_MAXPATHLEN)) != NULL)
#endif
result = buf;
return result;
}
bool DFHack::Filesystem::mkdir (std::string path)
{
int fail;
#ifdef _WIN32
fail = ::_mkdir(path.c_str());
#else
fail = ::mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH);
#endif
return !(bool)fail;
}
bool DFHack::Filesystem::rmdir (std::string path)
{
int fail;
#ifdef _WIN32
fail = ::_rmdir(path.c_str());
#else
fail = ::rmdir(path.c_str());
#endif
return !(bool)fail;
}
#ifdef _WIN32
_filetype mode2type (unsigned short mode) {
#else
_filetype mode2type (mode_t mode) {
#endif
if (S_ISREG(mode))
return FILETYPE_FILE;
else if (S_ISDIR(mode))
return FILETYPE_DIRECTORY;
else if (S_ISLNK(mode))
return FILETYPE_LINK;
else if (S_ISSOCK(mode))
return FILETYPE_SOCKET;
else if (S_ISFIFO(mode))
return FILETYPE_NAMEDPIPE;
else if (S_ISCHR(mode))
return FILETYPE_CHAR_DEVICE;
else if (S_ISBLK(mode))
return FILETYPE_BLOCK_DEVICE;
else
return FILETYPE_UNKNOWN;
}
bool DFHack::Filesystem::stat (std::string path, STAT_STRUCT &info)
{
return !(bool)(STAT_FUNC(path.c_str(), &info));
}
bool DFHack::Filesystem::exists (std::string path)
{
STAT_STRUCT info;
return (bool)DFHack::Filesystem::stat(path.c_str(), info);
}
#include <iostream>
_filetype DFHack::Filesystem::filetype (std::string path)
{
STAT_STRUCT info;
DFHack::Filesystem::stat(path, info);
std::cout << info.st_mode << std::endl;
return mode2type(info.st_mode);
}
bool DFHack::Filesystem::isfile (std::string path)
{
return DFHack::Filesystem::filetype(path) == FILETYPE_FILE;
}
bool DFHack::Filesystem::isdir (std::string path)
{
return DFHack::Filesystem::filetype(path) == FILETYPE_DIRECTORY;
}

@ -69,6 +69,7 @@ using namespace std;
#include "df/unit_misc_trait.h" #include "df/unit_misc_trait.h"
#include "df/unit_skill.h" #include "df/unit_skill.h"
#include "df/curse_attr_change.h" #include "df/curse_attr_change.h"
#include "df/squad.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
@ -1552,3 +1553,15 @@ int8_t DFHack::Units::getCasteProfessionColor(int race, int casteid, df::profess
// default to dwarven peasant color // default to dwarven peasant color
return 3; return 3;
} }
std::string DFHack::Units::getSquadName(df::unit *unit)
{
if (unit->military.squad_id == -1)
return "";
df::squad *squad = df::squad::find(unit->military.squad_id);
if (!squad)
return "";
if (squad->alias.size() > 0)
return squad->alias;
return Translation::TranslateName(&squad->name, true);
}

@ -12,6 +12,6 @@ fi
old_tty_settings=$(stty -g) old_tty_settings=$(stty -g)
cd "${PWD}" cd "${PWD}"
DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe "$@"
stty "$old_tty_settings" stty "$old_tty_settings"
echo "" echo ""

@ -167,6 +167,8 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(command-prompt command-prompt.cpp) DFHACK_PLUGIN(command-prompt command-prompt.cpp)
DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader) DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader)
DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
endif() endif()
# this is the skeleton plugin. If you want to make your own, make a copy and then change it # this is the skeleton plugin. If you want to make your own, make a copy and then change it

@ -12,11 +12,13 @@
#include <set> #include <set>
#include <list> #include <list>
#include <utility> #include <utility>
#include <vector>
#include "df/interface_key.h" #include "df/interface_key.h"
#include "df/ui.h" #include "df/ui.h"
#include "df/graphic.h" #include "df/graphic.h"
#include "df/enabler.h" #include "df/enabler.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
@ -24,6 +26,8 @@ using df::global::ui;
using df::global::gps; using df::global::gps;
using df::global::enabler; using df::global::enabler;
std::vector<std::string> command_history;
class viewscreen_commandpromptst; class viewscreen_commandpromptst;
class prompt_ostream:public buffered_color_ostream class prompt_ostream:public buffered_color_ostream
{ {
@ -44,12 +48,25 @@ public:
void render(); void render();
void help() { } void help() { }
int8_t movies_okay() { return 0; }
std::string getFocusString() { return "commandprompt"; } std::string getFocusString() { return "commandprompt"; }
viewscreen_commandpromptst(std::string entry):is_response(false),entry(entry) viewscreen_commandpromptst(std::string entry):is_response(false)
{ {
show_fps=df::global::gps->display_frames; show_fps=df::global::gps->display_frames;
df::global::gps->display_frames=0; df::global::gps->display_frames=0;
cursor_pos = 0;
frame = 0;
history_idx = command_history.size();
if (history_idx > 0)
{
if (command_history[history_idx - 1] == "")
{
command_history.pop_back();
history_idx--;
}
}
command_history.push_back(entry);
} }
~viewscreen_commandpromptst() ~viewscreen_commandpromptst()
{ {
@ -65,12 +82,50 @@ public:
responses.push_back(std::make_pair(v, part + '\n')); responses.push_back(std::make_pair(v, part + '\n'));
} }
} }
std::string get_entry()
{
return command_history[history_idx];
}
void set_entry(std::string entry)
{
command_history[history_idx] = entry;
}
void back_word()
{
std::string entry = get_entry();
if (cursor_pos == 0)
return;
cursor_pos--;
while (cursor_pos > 0 && !isalnum(entry[cursor_pos]))
cursor_pos--;
while (cursor_pos > 0 && isalnum(entry[cursor_pos]))
cursor_pos--;
if (!isalnum(entry[cursor_pos]) && cursor_pos != 0)
cursor_pos++;
}
void forward_word()
{
std::string entry = get_entry();
int len = entry.size();
if (cursor_pos == len)
return;
cursor_pos++;
while (cursor_pos <= len && !isalnum(entry[cursor_pos]))
cursor_pos++;
while (cursor_pos <= len && isalnum(entry[cursor_pos]))
cursor_pos++;
if (cursor_pos > len)
cursor_pos = len;
}
protected: protected:
std::list<std::pair<color_value,std::string> > responses; std::list<std::pair<color_value,std::string> > responses;
int cursor_pos;
int history_idx;
bool is_response; bool is_response;
bool show_fps; bool show_fps;
int frame;
void submit(); void submit();
std::string entry;
}; };
void prompt_ostream::flush_proxy() void prompt_ostream::flush_proxy()
{ {
@ -82,6 +137,9 @@ void prompt_ostream::flush_proxy()
} }
void viewscreen_commandpromptst::render() void viewscreen_commandpromptst::render()
{ {
++frame;
if (frame >= df::global::enabler->gfps)
frame = 0;
if (Screen::isDismissed(this)) if (Screen::isDismissed(this))
return; return;
@ -101,14 +159,25 @@ void viewscreen_commandpromptst::render()
} }
else else
{ {
std::string entry = get_entry();
Screen::fillRect(Screen::Pen(' ', 7, 0),0,0,dim.x,0); Screen::fillRect(Screen::Pen(' ', 7, 0),0,0,dim.x,0);
Screen::paintString(Screen::Pen(' ', 7, 0), 0, 0,"[DFHack]#"); Screen::paintString(Screen::Pen(' ', 7, 0), 0, 0,"[DFHack]#");
if(entry.size()<dim.x) std::string cursor = (frame < df::global::enabler->gfps / 2) ? "_" : " ";
if(cursor_pos < (dim.x - 10))
{
Screen::paintString(Screen::Pen(' ', 7, 0), 10,0 , entry); Screen::paintString(Screen::Pen(' ', 7, 0), 10,0 , entry);
if (entry.size() > dim.x - 10)
Screen::paintTile(Screen::Pen('\032', 7, 0), dim.x - 1, 0);
if (cursor != " ")
Screen::paintString(Screen::Pen(' ', 10, 0), 10 + cursor_pos, 0, cursor);
}
else else
{ {
Screen::paintTile(Screen::Pen('>', 7, 0), 9, 0); size_t start = cursor_pos - dim.x + 10 + 1;
Screen::paintString(Screen::Pen(' ', 7, 0), 10, 0, entry.substr(entry.size()-dim.x)); Screen::paintTile(Screen::Pen('\033', 7, 0), 9, 0);
Screen::paintString(Screen::Pen(' ', 7, 0), 10, 0, entry.substr(start));
if (cursor != " ")
Screen::paintString(Screen::Pen(' ', 10, 0), dim.x - 1, 0, cursor);
} }
} }
} }
@ -120,9 +189,8 @@ void viewscreen_commandpromptst::submit()
Screen::dismiss(this); Screen::dismiss(this);
return; return;
} }
//color_ostream_proxy out(Core::getInstance().getConsole());
prompt_ostream out(this); prompt_ostream out(this);
Core::getInstance().runCommand(out, entry); Core::getInstance().runCommand(out, get_entry());
if(out.empty() && responses.empty()) if(out.empty() && responses.empty())
Screen::dismiss(this); Screen::dismiss(this);
else else
@ -132,7 +200,8 @@ void viewscreen_commandpromptst::submit()
} }
void viewscreen_commandpromptst::feed(std::set<df::interface_key> *events) void viewscreen_commandpromptst::feed(std::set<df::interface_key> *events)
{ {
int old_pos = cursor_pos;
std::string entry = get_entry();
bool leave_all = events->count(interface_key::LEAVESCREEN_ALL); bool leave_all = events->count(interface_key::LEAVESCREEN_ALL);
if (leave_all || events->count(interface_key::LEAVESCREEN)) if (leave_all || events->count(interface_key::LEAVESCREEN))
{ {
@ -144,30 +213,91 @@ void viewscreen_commandpromptst::feed(std::set<df::interface_key> *events)
parent->feed(events); parent->feed(events);
events->clear(); events->clear();
} }
//if (command_history.size() && !entry.size())
// command_history.pop_back();
return; return;
} }
if(events->count(interface_key::SELECT)) if (events->count(interface_key::SELECT))
{ {
submit(); submit();
return; return;
} }
if(is_response) if (is_response)
return; return;
for (auto it = events->begin(); it != events->end(); ++it) for (auto it = events->begin(); it != events->end(); ++it)
{ {
auto key = *it; auto key = *it;
if (key==interface_key::STRING_A000) //delete? if (key==interface_key::STRING_A000) //delete?
{ {
if(entry.size()) if(entry.size() && cursor_pos > 0)
entry.resize(entry.size()-1); {
entry.erase(cursor_pos - 1, 1);
cursor_pos--;
}
if(cursor_pos > entry.size())
cursor_pos = entry.size();
continue; continue;
} }
if (key >= interface_key::STRING_A000 && if (key >= interface_key::STRING_A000 &&
key <= interface_key::STRING_A255) key <= interface_key::STRING_A255)
{ {
entry.push_back(char(key - interface_key::STRING_A000)); entry.insert(cursor_pos, 1, char(key - interface_key::STRING_A000));
cursor_pos++;
set_entry(entry);
return;
}
}
// Prevent number keys from moving cursor
if(events->count(interface_key::CURSOR_RIGHT))
{
cursor_pos++;
if (cursor_pos > entry.size())
cursor_pos = entry.size();
} }
else if(events->count(interface_key::CURSOR_LEFT))
{
cursor_pos--;
if (cursor_pos < 0) cursor_pos = 0;
} }
else if(events->count(interface_key::CURSOR_RIGHT_FAST))
{
forward_word();
}
else if(events->count(interface_key::CURSOR_LEFT_FAST))
{
back_word();
}
else if(events->count(interface_key::CUSTOM_CTRL_A))
{
cursor_pos = 0;
}
else if(events->count(interface_key::CUSTOM_CTRL_E))
{
cursor_pos = entry.size();
}
else if(events->count(interface_key::CURSOR_UP))
{
history_idx--;
if (history_idx < 0)
history_idx = 0;
entry = get_entry();
cursor_pos = entry.size();
}
else if(events->count(interface_key::CURSOR_DOWN))
{
if (history_idx < command_history.size() - 1)
{
history_idx++;
if (history_idx >= command_history.size())
history_idx = command_history.size() - 1;
entry = get_entry();
cursor_pos = entry.size();
}
}
set_entry(entry);
if (old_pos != cursor_pos)
frame = 0;
} }
DFHACK_PLUGIN("command-prompt"); DFHACK_PLUGIN("command-prompt");
command_result show_prompt(color_ostream &out, std::vector <std::string> & parameters) command_result show_prompt(color_ostream &out, std::vector <std::string> & parameters)

@ -0,0 +1,513 @@
#include "Console.h"
#include "Core.h"
#include "DataDefs.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Screen.h"
#include "modules/Gui.h"
#include <algorithm>
#include <set>
#include <VTableInterpose.h>
#include "ColorText.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/interface_key.h"
using namespace DFHack;
struct EmbarkTool
{
std::string id;
std::string name;
std::string desc;
bool enabled;
df::interface_key toggle_key;
};
EmbarkTool embark_tools[] = {
{"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map",
false, df::interface_key::CUSTOM_A},
{"nano", "Nano embark", "Allows the embark size to be decreased below 2x2",
false, df::interface_key::CUSTOM_N},
{"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site",
false, df::interface_key::CUSTOM_S},
{"sticky", "Stable position", "Maintains the selected local area while navigating the world map",
false, df::interface_key::CUSTOM_P},
};
#define NUM_TOOLS int(sizeof(embark_tools) / sizeof(EmbarkTool))
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
void OutputString (int8_t color, int &x, int y, const std::string &text);
bool tool_exists (std::string tool_name);
bool tool_enabled (std::string tool_name);
bool tool_enable (std::string tool_name, bool enable_state);
void tool_update (std::string tool_name);
class embark_tools_settings : public dfhack_viewscreen
{
public:
embark_tools_settings () { };
~embark_tools_settings () { };
void help () { };
std::string getFocusString () { return "embark-tools/options"; };
void render ()
{
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)
{
bool is_top = false;
if (screen->embark_pos_min.y == 0)
is_top = true;
std::set<df::interface_key> keys;
keys.insert(df::interface_key::SETUP_LOCAL_Y_MUP);
screen->feed(&keys);
if (!is_top)
{
keys.insert(df::interface_key::SETUP_LOCAL_Y_MDOWN);
screen->feed(&keys);
}
}
void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
{
/* Reproduces DF's embark resizing functionality
* Local area resizes up and to the right, unless it's already touching the edge
*/
int x1 = screen->embark_pos_min.x,
x2 = screen->embark_pos_max.x,
y1 = screen->embark_pos_min.y,
y2 = screen->embark_pos_max.y,
width = x2 - x1 + dx,
height = y2 - y1 + dy;
if (x1 == x2 && dx == -1)
dx = 0;
if (y1 == y2 && dy == -1)
dy = 0;
x2 += dx; // Resize right
while (x2 > 15)
{
x2--;
x1--;
}
x1 = std::max(0, x1);
y1 -= dy; // Resize up
while (y1 < 0)
{
y1++;
y2++;
}
y2 = std::min(15, y2);
screen->embark_pos_min.x = x1;
screen->embark_pos_max.x = x2;
screen->embark_pos_min.y = y1;
screen->embark_pos_max.y = y2;
update_embark_sidebar(screen);
}
std::string sand_indicator = "";
bool sand_dirty = true; // Flag set when update is needed
void sand_update (df::viewscreen_choose_start_sitest * screen)
{
CoreSuspendClaimer suspend;
buffered_color_ostream out;
Core::getInstance().runCommand(out, "prospect");
auto fragments = out.fragments();
sand_indicator = "";
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
std::string fragment = iter->second;
if (fragment.find("SAND_") != std::string::npos)
{
sand_indicator = "Sand";
break;
}
}
sand_dirty = false;
}
int sticky_pos[] = {0, 0, 3, 3};
bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen)
{
sticky_pos[0] = screen->embark_pos_min.x;
sticky_pos[1] = screen->embark_pos_max.x;
sticky_pos[2] = screen->embark_pos_min.y;
sticky_pos[3] = screen->embark_pos_max.y;
}
void sticky_apply (df::viewscreen_choose_start_sitest * screen)
{
screen->embark_pos_min.x = sticky_pos[0];
screen->embark_pos_max.x = sticky_pos[1];
screen->embark_pos_min.y = sticky_pos[2];
screen->embark_pos_max.y = sticky_pos[3];
update_embark_sidebar(screen);
}
/*
* Viewscreen hooks
*/
void OutputString (int8_t color, int &x, int y, const std::string &text)
{
Screen::paintString(Screen::Pen(' ', color, 0), x, y, text);
x += text.length();
}
struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
typedef df::viewscreen_choose_start_sitest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
{
bool prevent_default = false;
if (tool_enabled("anywhere"))
{
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
if (key == df::interface_key::SETUP_EMBARK)
{
prevent_default = true;
this->in_embark_normal = 1;
}
}
}
if (input->count(df::interface_key::CUSTOM_S))
{
Screen::show(new embark_tools_settings);
return;
}
if (tool_enabled("nano"))
{
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
bool is_resize = true;
int dx = 0, dy = 0;
switch (key)
{
case df::interface_key::SETUP_LOCAL_Y_UP:
dy = 1;
break;
case df::interface_key::SETUP_LOCAL_Y_DOWN:
dy = -1;
break;
case df::interface_key::SETUP_LOCAL_X_UP:
dx = 1;
break;
case df::interface_key::SETUP_LOCAL_X_DOWN:
dx = -1;
break;
default:
is_resize = false;
}
if (is_resize)
{
prevent_default = true;
resize_embark(this, dx, dy);
}
}
}
if (tool_enabled("sticky"))
{
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
bool is_motion = false;
int dx = 0, dy = 0;
switch (key)
{
case df::interface_key::CURSOR_UP:
case df::interface_key::CURSOR_DOWN:
case df::interface_key::CURSOR_LEFT:
case df::interface_key::CURSOR_RIGHT:
case df::interface_key::CURSOR_UPLEFT:
case df::interface_key::CURSOR_UPRIGHT:
case df::interface_key::CURSOR_DOWNLEFT:
case df::interface_key::CURSOR_DOWNRIGHT:
case df::interface_key::CURSOR_UP_FAST:
case df::interface_key::CURSOR_DOWN_FAST:
case df::interface_key::CURSOR_LEFT_FAST:
case df::interface_key::CURSOR_RIGHT_FAST:
case df::interface_key::CURSOR_UPLEFT_FAST:
case df::interface_key::CURSOR_UPRIGHT_FAST:
case df::interface_key::CURSOR_DOWNLEFT_FAST:
case df::interface_key::CURSOR_DOWNRIGHT_FAST:
is_motion = true;
break;
default:
is_motion = false;
}
if (is_motion && !sticky_moved)
{
sticky_save(this);
sticky_moved = true;
}
}
}
if (tool_enabled("sand"))
{
sand_dirty = true;
}
if (!prevent_default)
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
if (tool_enabled("sticky") && sticky_moved)
{
sticky_apply(this);
sticky_moved = false;
}
INTERPOSE_NEXT(render)();
auto dim = Screen::getWindowSize();
int x = 1,
y = dim.y - 5;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
OutputString(COLOR_WHITE, x, y, ": Enabled: ");
std::list<std::string> parts;
for (int i = 0; i < NUM_TOOLS; i++)
{
if (embark_tools[i].enabled)
{
parts.push_back(embark_tools[i].name);
parts.push_back(", ");
}
}
if (parts.size())
{
parts.pop_back(); // Remove trailing comma
for (auto iter = parts.begin(); iter != parts.end(); iter++)
{
OutputString(COLOR_LIGHTMAGENTA, x, y, *iter);
}
}
else
{
OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
}
if (tool_enabled("anywhere"))
{
x = 20; y = dim.y - 2;
if (this->page >= 0 && this->page <= 4)
{
// Only display on five map pages, not on site finder or notes
OutputString(COLOR_WHITE, x, y, ": Embark!");
}
}
if (tool_enabled("sand"))
{
if (sand_dirty)
{
sand_update(this);
}
x = dim.x - 28; y = 13;
if (this->page == 0)
{
OutputString(COLOR_YELLOW, x, y, sand_indicator);
}
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
/*
* Tool management
*/
bool tool_exists (std::string tool_name)
{
for (int i = 0; i < NUM_TOOLS; i++)
{
if (embark_tools[i].id == tool_name)
return true;
}
return false;
}
bool tool_enabled (std::string tool_name)
{
for (int i = 0; i < NUM_TOOLS; i++)
{
if (embark_tools[i].id == tool_name)
return embark_tools[i].enabled;
}
return false;
}
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")
{
embark_tools[i].enabled = enable_state;
tool_update(tool_name);
n++;
}
}
return (bool)n;
}
void tool_update (std::string tool_name)
{
// Called whenever a tool is enabled/disabled
if (tool_name == "sand")
{
sand_dirty = true;
}
}
/*
* Plugin management
*/
DFHACK_PLUGIN("embark-tools");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
std::string help = "";
help += "embark-tools (enable/disable) tool [tool...]\n"
"Tools:\n";
for (int i = 0; i < NUM_TOOLS; i++)
{
help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n");
}
commands.push_back(PluginCommand(
"embark-tools",
"A collection of embark tools",
embark_tools_cmd,
false,
help.c_str()
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown (color_ostream &out)
{
INTERPOSE_HOOK(choose_start_site_hook, feed).remove();
INTERPOSE_HOOK(choose_start_site_hook, render).remove();
return CR_OK;
}
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
{
if (is_enabled != enable)
{
if (!INTERPOSE_HOOK(choose_start_site_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(choose_start_site_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters)
{
CoreSuspender suspend;
if (parameters.size())
{
// Set by "enable"/"disable" - allows for multiple commands, e.g. "enable nano disable anywhere"
bool enable_state = true;
for (size_t i = 0; i < parameters.size(); i++)
{
if (parameters[i] == "enable")
{
enable_state = true;
plugin_enable(out, true); // Enable plugin
}
else if (parameters[i] == "disable")
enable_state = false;
else if (tool_exists(parameters[i]) || parameters[i] == "all")
{
tool_enable(parameters[i], enable_state);
}
else
return CR_WRONG_USAGE;
}
}
else
{
if (is_enabled)
{
out << "Tool status:" << std::endl;
for (int i = 0; i < NUM_TOOLS; i++)
{
EmbarkTool t = embark_tools[i];
out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl;
}
}
else
{
out << "Plugin not enabled" << std::endl;
}
}
return CR_OK;
}

@ -248,11 +248,13 @@ struct UnitInfo
string profession; string profession;
int8_t color; int8_t color;
int active_index; int active_index;
string squad_effective_name;
string squad_info;
}; };
enum altsort_mode { enum altsort_mode {
ALTSORT_NAME, ALTSORT_NAME,
ALTSORT_PROFESSION, ALTSORT_PROFESSION_OR_SQUAD,
ALTSORT_HAPPINESS, ALTSORT_HAPPINESS,
ALTSORT_ARRIVAL, ALTSORT_ARRIVAL,
ALTSORT_MAX ALTSORT_MAX
@ -278,6 +280,22 @@ bool sortByProfession (const UnitInfo *d1, const UnitInfo *d2)
return (d1->profession < d2->profession); return (d1->profession < d2->profession);
} }
bool sortBySquad (const UnitInfo *d1, const UnitInfo *d2)
{
bool gt = false;
if (d1->unit->military.squad_id == -1 && d2->unit->military.squad_id == -1)
gt = d1->name > d2->name;
else if (d1->unit->military.squad_id == -1)
gt = true;
else if (d2->unit->military.squad_id == -1)
gt = false;
else if (d1->unit->military.squad_id != d2->unit->military.squad_id)
gt = d1->squad_effective_name > d2->squad_effective_name;
else
gt = d1->unit->military.squad_position > d2->unit->military.squad_position;
return descending ? gt : !gt;
}
bool sortByHappiness (const UnitInfo *d1, const UnitInfo *d2) bool sortByHappiness (const UnitInfo *d1, const UnitInfo *d2)
{ {
if (descending) if (descending)
@ -336,7 +354,7 @@ bool sortBySkill (const UnitInfo *d1, const UnitInfo *d2)
enum display_columns { enum display_columns {
DISP_COLUMN_HAPPINESS, DISP_COLUMN_HAPPINESS,
DISP_COLUMN_NAME, DISP_COLUMN_NAME,
DISP_COLUMN_PROFESSION, DISP_COLUMN_PROFESSION_OR_SQUAD,
DISP_COLUMN_LABORS, DISP_COLUMN_LABORS,
DISP_COLUMN_MAX, DISP_COLUMN_MAX,
}; };
@ -366,6 +384,7 @@ public:
protected: protected:
vector<UnitInfo *> units; vector<UnitInfo *> units;
altsort_mode altsort; altsort_mode altsort;
bool show_squad;
bool do_refresh_names; bool do_refresh_names;
int first_row, sel_row, num_rows; int first_row, sel_row, num_rows;
@ -418,6 +437,7 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector<df::unit*> &src, int cur
units.push_back(cur); units.push_back(cur);
} }
altsort = ALTSORT_NAME; altsort = ALTSORT_NAME;
show_squad = false;
first_column = sel_column = 0; first_column = sel_column = 0;
refreshNames(); refreshNames();
@ -450,6 +470,13 @@ void viewscreen_unitlaborsst::refreshNames()
cur->name = Translation::TranslateName(Units::getVisibleName(unit), false); cur->name = Translation::TranslateName(Units::getVisibleName(unit), false);
cur->transname = Translation::TranslateName(Units::getVisibleName(unit), true); cur->transname = Translation::TranslateName(Units::getVisibleName(unit), true);
cur->profession = Units::getProfessionName(unit); cur->profession = Units::getProfessionName(unit);
if (unit->military.squad_id > -1) {
cur->squad_effective_name = Units::getSquadName(unit);
cur->squad_info = stl_sprintf("%i", unit->military.squad_position + 1) + "." + cur->squad_effective_name;
} else {
cur->squad_effective_name = "";
cur->squad_info = "";
}
} }
calcSize(); calcSize();
} }
@ -458,7 +485,7 @@ void viewscreen_unitlaborsst::calcSize()
{ {
auto dim = Screen::getWindowSize(); auto dim = Screen::getWindowSize();
num_rows = dim.y - 10; num_rows = dim.y - 11;
if (num_rows > units.size()) if (num_rows > units.size())
num_rows = units.size(); num_rows = units.size();
@ -471,8 +498,8 @@ void viewscreen_unitlaborsst::calcSize()
col_maxwidth[DISP_COLUMN_HAPPINESS] = 4; col_maxwidth[DISP_COLUMN_HAPPINESS] = 4;
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] = 10; col_minwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = 10;
col_maxwidth[DISP_COLUMN_PROFESSION] = 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] = num_columns*3/5; // 60%
col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS; col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS;
@ -481,8 +508,13 @@ void viewscreen_unitlaborsst::calcSize()
{ {
if (col_maxwidth[DISP_COLUMN_NAME] < units[i]->name.size()) if (col_maxwidth[DISP_COLUMN_NAME] < units[i]->name.size())
col_maxwidth[DISP_COLUMN_NAME] = units[i]->name.size(); col_maxwidth[DISP_COLUMN_NAME] = units[i]->name.size();
if (col_maxwidth[DISP_COLUMN_PROFESSION] < units[i]->profession.size()) if (show_squad) {
col_maxwidth[DISP_COLUMN_PROFESSION] = units[i]->profession.size(); if (col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] < units[i]->squad_info.size())
col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = units[i]->squad_info.size();
} else {
if (col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] < units[i]->profession.size())
col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = units[i]->profession.size();
}
} }
// check how much room we have // check how much room we have
@ -753,10 +785,10 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
} }
break; break;
case DISP_COLUMN_PROFESSION: case DISP_COLUMN_PROFESSION_OR_SQUAD:
if (enabler->mouse_lbut || enabler->mouse_rbut) if (enabler->mouse_lbut || enabler->mouse_rbut)
{ {
input_sort = ALTSORT_PROFESSION; input_sort = ALTSORT_PROFESSION_OR_SQUAD;
if (enabler->mouse_lbut) if (enabler->mouse_lbut)
events->insert(interface_key::SECONDSCROLL_PAGEDOWN); events->insert(interface_key::SECONDSCROLL_PAGEDOWN);
if (enabler->mouse_rbut) if (enabler->mouse_rbut)
@ -783,7 +815,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
break; break;
case DISP_COLUMN_NAME: case DISP_COLUMN_NAME:
case DISP_COLUMN_PROFESSION: case DISP_COLUMN_PROFESSION_OR_SQUAD:
// left-click to view, right-click to zoom // left-click to view, right-click to zoom
if (enabler->mouse_lbut) if (enabler->mouse_lbut)
{ {
@ -879,8 +911,8 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
case ALTSORT_NAME: case ALTSORT_NAME:
std::sort(units.begin(), units.end(), sortByName); std::sort(units.begin(), units.end(), sortByName);
break; break;
case ALTSORT_PROFESSION: case ALTSORT_PROFESSION_OR_SQUAD:
std::sort(units.begin(), units.end(), sortByProfession); std::sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession);
break; break;
case ALTSORT_HAPPINESS: case ALTSORT_HAPPINESS:
std::sort(units.begin(), units.end(), sortByHappiness); std::sort(units.begin(), units.end(), sortByHappiness);
@ -895,9 +927,9 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
switch (altsort) switch (altsort)
{ {
case ALTSORT_NAME: case ALTSORT_NAME:
altsort = ALTSORT_PROFESSION; altsort = ALTSORT_PROFESSION_OR_SQUAD;
break; break;
case ALTSORT_PROFESSION: case ALTSORT_PROFESSION_OR_SQUAD:
altsort = ALTSORT_HAPPINESS; altsort = ALTSORT_HAPPINESS;
break; break;
case ALTSORT_HAPPINESS: case ALTSORT_HAPPINESS:
@ -908,6 +940,10 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
break; break;
} }
} }
if (events->count(interface_key::OPTION20))
{
show_squad = !show_squad;
}
if (VIRTUAL_CAST_VAR(unitlist, df::viewscreen_unitlistst, parent)) if (VIRTUAL_CAST_VAR(unitlist, df::viewscreen_unitlistst, parent))
{ {
@ -949,7 +985,7 @@ void viewscreen_unitlaborsst::render()
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_HAPPINESS], 2, "Hap."); Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_HAPPINESS], 2, "Hap.");
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], 2, "Profession"); Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_PROFESSION_OR_SQUAD], 2, show_squad ? "Squad" : "Profession");
for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++) for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++)
{ {
@ -1019,12 +1055,17 @@ void viewscreen_unitlaborsst::render()
name.resize(col_widths[DISP_COLUMN_NAME]); name.resize(col_widths[DISP_COLUMN_NAME]);
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_NAME], 4 + row, name); Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_NAME], 4 + row, name);
string profession = cur->profession;
profession.resize(col_widths[DISP_COLUMN_PROFESSION]);
fg = cur->color;
bg = 0; bg = 0;
string profession_or_squad;
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_PROFESSION], 4 + row, profession); if (show_squad) {
fg = 11;
profession_or_squad = cur->squad_info;
} else {
fg = cur->color;
profession_or_squad = cur->profession;
}
profession_or_squad.resize(col_widths[DISP_COLUMN_PROFESSION_OR_SQUAD]);
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_PROFESSION_OR_SQUAD], 4 + row, profession_or_squad);
// Print unit's skills and labor assignments // Print unit's skills and labor assignments
for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++) for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++)
@ -1114,7 +1155,25 @@ void viewscreen_unitlaborsst::render()
else else
str = stl_sprintf("Not %s (0/500)", ENUM_ATTR_STR(job_skill, caption_noun, columns[sel_column].skill)); str = stl_sprintf("Not %s (0/500)", ENUM_ATTR_STR(job_skill, caption_noun, columns[sel_column].skill));
} }
Screen::paintString(Screen::Pen(' ', 9, 0), x, 3 + num_rows + 2, str); Screen::paintString(Screen::Pen(' ', 9, 0), x, y, str);
if (cur->unit->military.squad_id > -1) {
x = 1;
y++;
string squadLabel = "Squad: ";
Screen::paintString(white_pen, x, y, squadLabel);
x += squadLabel.size();
string squad = cur->squad_effective_name;
Screen::paintString(Screen::Pen(' ', 11, 0), x, y, squad);
x += squad.size();
string pos = stl_sprintf(" Pos %i", cur->unit->military.squad_position + 1);
Screen::paintString(Screen::Pen(' ', 9, 0), x, y, pos);
}
canToggle = (cur->allowEdit) && (columns[sel_column].labor != unit_labor::NONE); canToggle = (cur->allowEdit) && (columns[sel_column].labor != unit_labor::NONE);
} }
@ -1136,6 +1195,9 @@ void viewscreen_unitlaborsst::render()
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::LEAVESCREEN)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::LEAVESCREEN));
OutputString(15, x, y, ": Done, "); OutputString(15, x, y, ": Done, ");
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::OPTION20));
OutputString(15, x, y, ": Toggle View, ");
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SECONDSCROLL_DOWN)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SECONDSCROLL_DOWN));
OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SECONDSCROLL_UP)); OutputString(10, x, y, Screen::getKeyDisplay(interface_key::SECONDSCROLL_UP));
OutputString(15, x, y, ": Sort by Skill, "); OutputString(15, x, y, ": Sort by Skill, ");
@ -1150,8 +1212,8 @@ void viewscreen_unitlaborsst::render()
case ALTSORT_NAME: case ALTSORT_NAME:
OutputString(15, x, y, "Name"); OutputString(15, x, y, "Name");
break; break;
case ALTSORT_PROFESSION: case ALTSORT_PROFESSION_OR_SQUAD:
OutputString(15, x, y, "Profession"); OutputString(15, x, y, show_squad ? "Squad" : "Profession");
break; break;
case ALTSORT_HAPPINESS: case ALTSORT_HAPPINESS:
OutputString(15, x, y, "Happiness"); OutputString(15, x, y, "Happiness");

@ -0,0 +1,213 @@
#include "Console.h"
#include "Core.h"
#include "DataDefs.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/EventManager.h"
#include "modules/Maps.h"
#include "df/caste_raw.h"
#include "df/caste_raw_flags.h"
#include "df/creature_raw.h"
#include "df/profession.h"
#include "df/unit.h"
#include "df/world.h"
#include <map>
#include <vector>
using namespace DFHack;
using namespace std;
static int32_t howOften = 10000;
static int32_t popcap = 100;
static int32_t pregtime = 200000;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
command_result petcapRemover (color_ostream &out, std::vector <std::string> & parameters);
DFHACK_PLUGIN("petcapRemover");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"petcapRemover",
"Removes the pet population cap by causing pregnancies.",
petcapRemover,
false, //allow non-interactive use
"petcapRemover\n"
" does pregnancies now and schedules the next check\n"
"petcapRemover every n\n"
" set how often in ticks the plugin checks for possible pregnancies\n"
"petcapRemover cap n\n"
" sets the new cap to n. if n = 0, no cap. Caps between 1 and 50 effectively don't do anything because normal DF pregnancies will continue to happen below that cap.\n"
"petcapRemover pregtime n\n"
" sets the pregnancy duration to n ticks. Natural pregnancies are 300000 ticks for the current race and 200000 ticks for everyone else.\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
bool impregnate(df::unit* female, df::unit* male);
void impregnateMany() {
map<int32_t, vector<int32_t> > males;
map<int32_t, vector<int32_t> > females;
map<int32_t, int32_t> popcount;
auto units = df::global::world->units.all;
for ( size_t a = 0; a < units.size(); a++ ) {
df::unit* unit = units[a];
if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor )
continue;
popcount[unit->race]++;
if ( unit->relations.pregnancy_genes ) {
//already pregnant
//for player convenience and population stability, count the fetus toward the population cap
popcount[unit->race]++;
continue;
}
if ( unit->flags1.bits.caged )
continue;
int32_t race = unit->race;
int16_t caste = unit->caste;
df::creature_raw* creatureRaw = df::global::world->raws.creatures.all[race];
df::caste_raw* casteRaw = creatureRaw->caste[caste];
//must have PET or PET_EXOTIC
if ( !(casteRaw->flags.is_set(df::enums::caste_raw_flags::PET) || casteRaw->flags.is_set(df::enums::caste_raw_flags::PET_EXOTIC) ) )
continue;
//check for adulthood
if ( unit->profession == df::enums::profession::CHILD || unit->profession == df::enums::profession::BABY )
continue;
if ( unit->sex == 1 )
males[unit->race].push_back(a);
else
females[unit->race].push_back(a);
}
for ( auto i = females.begin(); i != females.end(); i++ ) {
int32_t race = i->first;
vector<int32_t>& femalesList = i->second;
for ( size_t a = 0; a < femalesList.size(); a++ ) {
if ( popcap > 0 && popcount[race] >= popcap )
break;
vector<int32_t> compatibles;
df::coord pos1 = units[femalesList[a]]->pos;
if ( males.find(i->first) == males.end() )
continue;
vector<int32_t>& malesList = males[i->first];
for ( size_t b = 0; b < malesList.size(); b++ ) {
df::coord pos2 = units[malesList[b]]->pos;
if ( Maps::canWalkBetween(pos1,pos2) )
compatibles.push_back(malesList[b]);
}
if ( compatibles.empty() )
continue;
size_t maleIndex = (size_t)(compatibles.size()*((float)rand() / (1+(float)RAND_MAX)));
if ( impregnate(units[femalesList[a]], units[compatibles[maleIndex]]) )
popcount[race]++;
}
}
}
bool impregnate(df::unit* female, df::unit* male) {
if ( !female || !male )
return false;
if ( female->relations.pregnancy_genes )
return false;
df::unit_genes* preg = new df::unit_genes;
*preg = male->appearance.genes;
female->relations.pregnancy_genes = preg;
female->relations.pregnancy_timer = pregtime; //300000 for dwarves
female->relations.pregnancy_caste = male->caste;
return true;
}
void tickHandler(color_ostream& out, void* data) {
if ( !is_enabled )
return;
CoreSuspender suspend;
impregnateMany();
EventManager::unregisterAll(plugin_self);
EventManager::EventHandler handle(tickHandler, howOften);
EventManager::registerTick(handle, howOften, plugin_self);
}
command_result petcapRemover (color_ostream &out, std::vector <std::string> & parameters)
{
CoreSuspender suspend;
for ( size_t a = 0; a < parameters.size(); a++ ) {
if ( parameters[a] == "every" ) {
if ( a+1 >= parameters.size() )
return CR_WRONG_USAGE;
int32_t old = howOften;
howOften = atoi(parameters[a+1].c_str());
if (howOften < -1) {
howOften = old;
return CR_WRONG_USAGE;
}
a++;
continue;
} else if ( parameters[a] == "cap" ) {
if ( a+1 >= parameters.size() )
return CR_WRONG_USAGE;
int32_t old = popcap;
popcap = atoi(parameters[a+1].c_str());
if ( popcap < 0 ) {
popcap = old;
return CR_WRONG_USAGE;
}
a++;
continue;
} else if ( parameters[a] == "pregtime" ) {
if ( a+1 >= parameters.size() )
return CR_WRONG_USAGE;
int32_t old = pregtime;
pregtime = atoi(parameters[a+1].c_str());
if ( pregtime <= 0 ) {
pregtime = old;
return CR_WRONG_USAGE;
}
a++;
continue;
}
out.print("%s, line %d: invalid argument: %s\n", __FILE__, __LINE__, parameters[a].c_str());
return CR_WRONG_USAGE;
}
if ( howOften < 0 ) {
is_enabled = false;
return CR_OK;
}
is_enabled = true;
EventManager::unregisterAll(plugin_self);
EventManager::EventHandler handle(tickHandler, howOften);
EventManager::registerTick(handle, howOften, plugin_self);
out.print("petcapRemover: howOften = every %d ticks, popcap per species = %d, preg time = %d ticks.\n", howOften, popcap, pregtime);
return CR_OK;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enable != is_enabled)
{
is_enabled = enable;
if ( !is_enabled ) {
EventManager::unregisterAll(plugin_self);
}
}
return CR_OK;
}

@ -52,6 +52,7 @@
#include "df/reaction.h" #include "df/reaction.h"
#include "df/reaction_reagent_itemst.h" #include "df/reaction_reagent_itemst.h"
#include "df/reaction_reagent_flags.h" #include "df/reaction_reagent_flags.h"
#include "df/viewscreen_setupdwarfgamest.h"
#include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_layer_assigntradest.h"
#include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_layer_militaryst.h" #include "df/viewscreen_layer_militaryst.h"
@ -120,6 +121,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" when soldiers go off-duty (i.e. civilian).\n" " when soldiers go off-duty (i.e. civilian).\n"
" tweak readable-build-plate [disable]\n" " tweak readable-build-plate [disable]\n"
" Fixes rendering of creature weight limits in pressure plate build menu.\n" " Fixes rendering of creature weight limits in pressure plate build menu.\n"
" tweak confirm-embark [disable]\n"
" Asks for confirmation on the embark setup screen before embarking\n"
" tweak stable-temp [disable]\n" " tweak stable-temp [disable]\n"
" Fixes performance bug 6012 by squashing jitter in temperature updates.\n" " Fixes performance bug 6012 by squashing jitter in temperature updates.\n"
" tweak fast-heat <max-ticks>\n" " tweak fast-heat <max-ticks>\n"
@ -331,6 +334,97 @@ struct readable_build_plate_hook : df::viewscreen_dwarfmodest
IMPLEMENT_VMETHOD_INTERPOSE(readable_build_plate_hook, render); IMPLEMENT_VMETHOD_INTERPOSE(readable_build_plate_hook, render);
enum confirm_embark_states
{
ECS_INACTIVE = 0,
ECS_CONFIRM,
ECS_ACCEPTED
};
static confirm_embark_states confirm_embark_state = ECS_INACTIVE;
struct confirm_embark_hook : df::viewscreen_setupdwarfgamest
{
typedef df::viewscreen_setupdwarfgamest interpose_base;
void OutputString(int8_t fg, int &x, int y, std::string text)
{
Screen::paintString(Screen::Pen(' ', fg, COLOR_BLACK), x, y, text);
x += text.length();
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
bool intercept = false;
if (this->show_play_now == 0)
{
if (confirm_embark_state == ECS_INACTIVE)
{
if (input->count(df::interface_key::SETUP_EMBARK))
{
confirm_embark_state = ECS_CONFIRM;
intercept = true;
}
}
else if (confirm_embark_state == ECS_CONFIRM)
{
intercept = true;
if (input->count(df::interface_key::MENU_CONFIRM))
confirm_embark_state = ECS_ACCEPTED;
else if (input->size())
confirm_embark_state = ECS_INACTIVE;
}
}
if (!intercept)
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key))
{
if (confirm_embark_state == ECS_CONFIRM)
{
if (key == df::interface_key::OPTIONS)
return true;
}
return INTERPOSE_NEXT(key_conflict)(key);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
auto dim = Screen::getWindowSize();
int x = 0, y = 0;
if (confirm_embark_state != ECS_INACTIVE)
{
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), 0, 0, dim.x - 1, dim.y - 1);
}
if (confirm_embark_state == ECS_CONFIRM)
{
x = 2, y = 2;
OutputString(COLOR_WHITE, x, y, "Really embark? (");
OutputString(COLOR_LIGHTGREEN, x, y, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM));
OutputString(COLOR_WHITE, x, y, " = yes, other = no)");
x = 2, y = 4;
int32_t points = this->points_remaining;
OutputString(COLOR_WHITE, x, y, "Points left: ");
OutputString((points ? COLOR_YELLOW : COLOR_LIGHTGREEN), x, y, std::to_string((unsigned long long/*won't compile on windows otherwise*/)points));
x = dim.x - 10, y = dim.y - 1;
OutputString(COLOR_WHITE, x, y, "DFHack");
}
else if (confirm_embark_state == ECS_ACCEPTED)
{
std::set<df::interface_key> input;
input.insert(df::interface_key::SETUP_EMBARK);
this->feed(&input);
confirm_embark_state = ECS_INACTIVE;
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, key_conflict);
IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, render);
struct stable_temp_hook : df::item_actual { struct stable_temp_hook : df::item_actual {
typedef df::item_actual interpose_base; typedef df::item_actual interpose_base;
@ -1216,6 +1310,12 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
enable_hook(out, INTERPOSE_HOOK(readable_build_plate_hook, render), parameters); enable_hook(out, INTERPOSE_HOOK(readable_build_plate_hook, render), parameters);
} }
else if (cmd == "confirm-embark")
{
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, feed), parameters);
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, key_conflict), parameters);
enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, render), parameters);
}
else if (cmd == "stable-temp") else if (cmd == "stable-temp")
{ {
enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, adjustTemperature), parameters); enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, adjustTemperature), parameters);