Conflicts:
	library/xml
	plugins/isoworld
	plugins/rendermax/CMakeLists.txt
	plugins/rendermax/renderer_light.cpp
	plugins/rendermax/renderer_light.hpp
	plugins/rendermax/renderer_opengl.hpp
	plugins/rendermax/rendermax.cpp
	plugins/rendermax/rendermax.lua
develop
Japa 2014-12-11 17:53:19 +05:30
commit 22edb5a033
256 changed files with 26084 additions and 10881 deletions

@ -58,8 +58,8 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif()
# set up versioning.
set(DF_VERSION "0.34.11")
SET(DFHACK_RELEASE "r4" CACHE STRING "Current release revision.")
set(DF_VERSION "0.40.19")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
@ -168,7 +168,7 @@ add_subdirectory(depends)
IF(BUILD_LIBRARY)
add_subdirectory (library)
## install the default documentation files
install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
install(FILES LICENSE NEWS "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
install(DIRECTORY images DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif()
@ -182,7 +182,7 @@ IF(UNIX)
if(APPLE)
SET(CPACK_GENERATOR "ZIP")
else()
SET(CPACK_GENERATOR "TGZ")
SET(CPACK_GENERATOR "TBZ2")
endif()
ELSEIF(WIN32)
SET(CPACK_GENERATOR "ZIP")

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.11: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" />
<title>Building DFHACK</title>
<style type="text/css">
@ -382,10 +382,10 @@ ul.auto-toc {
<h2><a class="toc-backref" href="#id5">How to get the code</a></h2>
<p>DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.</p>
<p>The code resides here: <a class="reference external" href="https://github.com/peterix/dfhack">https://github.com/peterix/dfhack</a></p>
<p>The code resides here: <a class="reference external" href="https://github.com/DFHack/dfhack">https://github.com/DFHack/dfhack</a></p>
<p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p>
<pre class="literal-block">
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -396,11 +396,14 @@ git submodule update
<h2><a class="toc-backref" href="#id6">Dependencies</a></h2>
<p>DFHack is meant to be installed into an existing DF folder, so get one ready.</p>
<p>For building, you need a 32-bit version of GCC. For example, to build DFHack on
a 64-bit distribution like Arch, you'll need the multilib development tools and libraries.</p>
a 64-bit distribution like Arch, you'll need the multilib development tools and libraries.
Alternatively, you might be able to use <tt class="docutils literal">lxc</tt> to
<a class="reference external" href="http://www.bay12forums.com/smf/index.php?topic=139553.msg5435310#msg5435310">create a virtual 32-bit environment</a>.</p>
<p>Before you can build anything, you'll also need <tt class="docutils literal">cmake</tt>. It is advisable to also get
<tt class="docutils literal">ccmake</tt> on distributions that split the cmake package into multiple parts.</p>
<p>For the code generation parts, you need perl and the XML::LibXML and XML::LibXSLT perl packages.
You should be able to find them in your distro repositories (on Arch linux 'perl-xml-libxml' and 'perl-xml-libxslt').</p>
<p>To build Stonesense, you'll also need OpenGL headers.</p>
</div>
<div class="section" id="build">
<h2><a class="toc-backref" href="#id7">Build</a></h2>
@ -489,7 +492,7 @@ process. Just stick with the defaults for everything and you'll be fine.</p>
</li>
<li><p class="first">Get the dfhack source:</p>
<pre class="literal-block">
git clone https://github.com/danaris/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -553,14 +556,14 @@ make install
You will need some sort of Windows port of git, or a GUI. Some examples:</p>
<blockquote>
<ul class="simple">
<li><a class="reference external" href="http://code.google.com/p/msysgit/">http://code.google.com/p/msysgit/</a> - this is a command line version of git for windows. Most tutorials on git usage will apply.</li>
<li><a class="reference external" href="http://msysgit.github.io/">http://msysgit.github.io/</a> - this is a command line version of git for windows. Most tutorials on git usage will apply.</li>
<li><a class="reference external" href="http://code.google.com/p/tortoisegit/">http://code.google.com/p/tortoisegit/</a> - this puts a pretty, graphical face on top of msysgit :)</li>
</ul>
</blockquote>
<p>The code resides here: <a class="reference external" href="https://github.com/peterix/dfhack">https://github.com/peterix/dfhack</a></p>
<p>The code resides here: <a class="reference external" href="https://github.com/DFHack/dfhack">https://github.com/DFHack/dfhack</a></p>
<p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p>
<pre class="literal-block">
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -577,13 +580,7 @@ to your binary search PATH so the tool can be later run from anywhere.</p>
<p>You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient.
Grab it from Microsoft's site.</p>
<p>You'll also need the Visual Studio 2010 SP1 update.</p>
<p>For the code generation parts, you'll need perl and XML::LibXML. You can install them like this:</p>
<ul class="simple">
<li>download and install strawberry perl from <a class="reference external" href="http://strawberryperl.com/">http://strawberryperl.com/</a></li>
<li>reboot so that the system can pick up the new binary path</li>
<li>open a cmd.exe window and run &quot;cpan XML::LibXML&quot; (obviously without the quotes). This can take a while to complete.</li>
<li>Same with &quot;cpan XML::LibXSLT&quot;.</li>
</ul>
<p>For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. Strawberry Perl works nicely for this: http://strawberryperl.com/</p>
<p>If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p>
</div>
<div class="section" id="id3">
@ -666,7 +663,7 @@ I'll make <em>you</em> fix it ;)</p>
the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.</p>
<p>Fixes are higher in priority. If you want to work on something, but
don't know what, check out <a class="reference external" href="http://github.com/peterix/dfhack/issues">http://github.com/peterix/dfhack/issues</a> --
don't know what, check out <a class="reference external" href="http://github.com/DFHack/dfhack/issues">http://github.com/DFHack/dfhack/issues</a> --
this is also a good place to dump new ideas and/or bugs that need
fixing.</p>
</div>
@ -678,13 +675,13 @@ to look at machine code without getting crazy :)</p>
<p>Good windows tools include:</p>
<ul class="simple">
<li>Cheat Engine</li>
<li>IDA Pro (the free version)</li>
<li>IDA Pro 5.0 (freely available for non-commercial use)</li>
</ul>
<p>Good linux tools:</p>
<ul class="simple">
<li>angavrilov's df-structures gui (visit us on IRC for details).</li>
<li>edb (Evan's Debugger)</li>
<li>IDA Pro running under wine.</li>
<li>IDA Pro 5.0 running under Wine</li>
<li>Some of the tools residing in the <tt class="docutils literal">legacy</tt> dfhack branch.</li>
</ul>
<p>Using publicly known information and analyzing the game's data is preferred.</p>

@ -16,11 +16,11 @@ How to get the code
DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.
The code resides here: https://github.com/peterix/dfhack
The code resides here: https://github.com/DFHack/dfhack
If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address::
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -33,6 +33,8 @@ DFHack is meant to be installed into an existing DF folder, so get one ready.
For building, you need a 32-bit version of GCC. For example, to build DFHack on
a 64-bit distribution like Arch, you'll need the multilib development tools and libraries.
Alternatively, you might be able to use ``lxc`` to
`create a virtual 32-bit environment <http://www.bay12forums.com/smf/index.php?topic=139553.msg5435310#msg5435310>`_.
Before you can build anything, you'll also need ``cmake``. It is advisable to also get
``ccmake`` on distributions that split the cmake package into multiple parts.
@ -40,6 +42,8 @@ Before you can build anything, you'll also need ``cmake``. It is advisable to al
For the code generation parts, you need perl and the XML::LibXML and XML::LibXSLT perl packages.
You should be able to find them in your distro repositories (on Arch linux 'perl-xml-libxml' and 'perl-xml-libxslt').
To build Stonesense, you'll also need OpenGL headers.
Build
=====
Building is fairly straightforward. Enter the ``build`` folder and start the build like this::
@ -118,7 +122,7 @@ If you are building on 10.6, please read the subsection below titled "Snow Leopa
6. Get the dfhack source::
git clone https://github.com/danaris/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -165,14 +169,14 @@ How to get the code
DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
You will need some sort of Windows port of git, or a GUI. Some examples:
* http://code.google.com/p/msysgit/ - this is a command line version of git for windows. Most tutorials on git usage will apply.
* http://msysgit.github.io/ - this is a command line version of git for windows. Most tutorials on git usage will apply.
* http://code.google.com/p/tortoisegit/ - this puts a pretty, graphical face on top of msysgit :)
The code resides here: https://github.com/peterix/dfhack
The code resides here: https://github.com/DFHack/dfhack
If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address::
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -194,12 +198,7 @@ Grab it from Microsoft's site.
You'll also need the Visual Studio 2010 SP1 update.
For the code generation parts, you'll need perl and XML::LibXML. You can install them like this:
* download and install strawberry perl from http://strawberryperl.com/
* reboot so that the system can pick up the new binary path
* open a cmd.exe window and run "cpan XML::LibXML" (obviously without the quotes). This can take a while to complete.
* Same with "cpan XML::LibXSLT".
For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. Strawberry Perl works nicely for this: http://strawberryperl.com/
If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.
@ -307,7 +306,7 @@ the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.
Fixes are higher in priority. If you want to work on something, but
don't know what, check out http://github.com/peterix/dfhack/issues --
don't know what, check out http://github.com/DFHack/dfhack/issues --
this is also a good place to dump new ideas and/or bugs that need
fixing.
@ -321,13 +320,13 @@ to look at machine code without getting crazy :)
Good windows tools include:
* Cheat Engine
* IDA Pro (the free version)
* IDA Pro 5.0 (freely available for non-commercial use)
Good linux tools:
* angavrilov's df-structures gui (visit us on IRC for details).
* edb (Evan's Debugger)
* IDA Pro running under wine.
* IDA Pro 5.0 running under Wine
* Some of the tools residing in the ``legacy`` dfhack branch.
Using publicly known information and analyzing the game's data is preferred.

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.11: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" />
<title>Contributors</title>
<style type="text/css">
@ -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>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>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>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>
@ -401,6 +401,8 @@ ul.auto-toc {
<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>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>
<li>Putnam</li>
</ul>
<p>And those are the cool people who made <strong>stonesense</strong>.</p>
<ul class="simple">

@ -24,7 +24,7 @@ The following is a list of people who have contributed to **DFHack**.
- RossM <Ross@Gnome>
- Tom Prince <tom.prince@ualberta.net>
- Jared Adams <jaxad0127@gmail.com>
- expwnent <q309185@gmail.com>
- expwnent
- Erik Youngren <artanis.00@gmail.com>
- Espen Wiborg <espen.wiborg@telio.no>
- Tim Walberg <twalberg@comcast.net>
@ -61,6 +61,8 @@ The following is a list of people who have contributed to **DFHack**.
- Caldfir <caldfir@hotmail.com>
- Antalia <tamarakorr@gmail.com>
- Angus Mezick <amezick@gmail.com>
- PeridexisErrant <PeridexisErrant@gmail.com>
- Putnam
And those are the cool people who made **stonesense**.

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.11: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" />
<title>DFHack Lua API</title>
<style type="text/css">
@ -1246,6 +1246,11 @@ Returns <em>true</em> on success.</p>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_item_equal(job_item1,job_item2)</tt></p>
<p>Compares important fields in the job item structures.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.linkIntoWorld(job,new_id)</tt></p>
<p>Adds job into <tt class="docutils literal">df.global.job_list</tt>, and if new_id
is true, then also sets it's id and increases
<tt class="docutils literal">df.global.job_next_id</tt></p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.listNewlyCreated(first_id)</tt></p>
<p>Returns the current value of <tt class="docutils literal">df.global.job_next_id</tt>, and
if there are any jobs with <tt class="docutils literal">first_id &lt;= id &lt; job_next_id</tt>,
@ -1586,6 +1591,10 @@ are removed from extents. If <tt class="docutils literal">allow_occupied</tt>, t
<p>Checks if a bridge constructed at specified position would have
support from terrain, and thus won't collapse if retracted.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getStockpileContents(stockpile)</tt></p>
<p>Returns a list of items stored on the given stockpile.
Ignores empty bins, barrels, and wheelbarrows assigned as storage and transport for that stockpile.</p>
</li>
</ul>
<p>Low-level building creation functions;</p>
<ul>
@ -1757,6 +1766,15 @@ functions in this section, this may be used at any time.</p>
<p>Returns the string that should be used to represent the given
logical keybinding on the screen in texts like &quot;press Key to ...&quot;.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.screen.keyToChar(key)</tt></p>
<p>Returns the integer character code of the string input
character represented by the given logical keybinding,
or <em>nil</em> if not a string input key.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.screen.charToKey(charcode)</tt></p>
<p>Returns the keybinding representing the given string input
character, or <em>nil</em> if impossible.</p>
</li>
</ul>
<p>The &quot;pen&quot; argument used by functions above may be represented by
a table with the following possible fields:</p>
@ -3230,6 +3248,18 @@ tweaking (e.g. adding custom reactions)</p>
<li><p class="first"><tt class="docutils literal">onInventoryChange(unit_id,item_id,old_equip,new_equip)</tt></p>
<p>Gets called when someone picks up an item, puts one down, or changes the way they are holding it. If an item is picked up, old_equip will be null. If an item is dropped, new_equip will be null. If an item is re-equipped in a new way, then neither will be null. You absolutely must NOT alter either old_equip or new_equip or you might break other plugins.</p>
</li>
<li><p class="first"><tt class="docutils literal">onReport(reportId)</tt></p>
<p>Gets called when a report happens. This happens more often than you probably think, even if it doesn't show up in the announcements.</p>
</li>
<li><p class="first"><tt class="docutils literal">onUnitAttack(attackerId, defenderId, woundId)</tt></p>
<p>Called when a unit wounds another with a weapon. Is NOT called if blocked, dodged, deflected, or parried.</p>
</li>
<li><p class="first"><tt class="docutils literal">onUnload()</tt></p>
<p>A convenience event in case you don't want to register for every onStateChange event.</p>
</li>
<li><p class="first"><tt class="docutils literal">onInteraction(attackVerb, defendVerb, attackerId, defenderId, attackReportId, defendReportId)</tt></p>
<p>Called when a unit uses an interaction on another.</p>
</li>
</ol>
</div>
<div class="section" id="functions">
@ -3248,7 +3278,8 @@ tweaking (e.g. adding custom reactions)</p>
<p>Enable event checking for EventManager events. For event types use <tt class="docutils literal">eventType</tt> table. Note that different types of events require different frequencies to be effective. The frequency is how many ticks EventManager will wait before checking if that type of event has happened. If multiple scripts or plugins use the same event type, the smallest frequency is the one that is used, so you might get events triggered more often than the frequency you use here.</p>
</li>
<li><p class="first"><tt class="docutils literal">registerSidebar(shop_name,callback)</tt></p>
<p>Enable callback when sidebar for <tt class="docutils literal">shop_name</tt> is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib.</p>
<p>Enable callback when sidebar for <tt class="docutils literal">shop_name</tt> is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib. Also accepts a <tt class="docutils literal">class</tt> instead of function
as callback. Best used with <tt class="docutils literal">gui.dwarfmode</tt> class <tt class="docutils literal">WorkshopOverlay</tt>.</p>
</li>
</ol>
</div>
@ -3309,6 +3340,7 @@ plugin export a function it's recommended to use lua decorated function.</p>
<li><tt class="docutils literal"><span class="pre">{x=&lt;number&gt;</span> <span class="pre">y=&lt;number&gt;</span> + 4 numbers like in first case}</tt>, this generates full frame useful for animations that change little (1-2 tiles)</li>
</ol>
</li>
<li>canBeRoomSubset -- a flag if this building can be counted in room. 1 means it can, 0 means it can't and -1 default building behaviour</li>
</ol>
</dd>
<dt>Animate table also might contain:</dt>

@ -965,6 +965,12 @@ Job module
Compares important fields in the job item structures.
* ``dfhack.job.linkIntoWorld(job,new_id)``
Adds job into ``df.global.job_list``, and if new_id
is true, then also sets it's id and increases
``df.global.job_next_id``
* ``dfhack.job.listNewlyCreated(first_id)``
Returns the current value of ``df.global.job_next_id``, and
@ -1388,6 +1394,11 @@ Buildings module
Checks if a bridge constructed at specified position would have
support from terrain, and thus won't collapse if retracted.
* ``dfhack.buildings.getStockpileContents(stockpile)``
Returns a list of items stored on the given stockpile.
Ignores empty bins, barrels, and wheelbarrows assigned as storage and transport for that stockpile.
Low-level building creation functions;
* ``dfhack.buildings.allocInstance(pos, type, subtype, custom)``
@ -1591,6 +1602,17 @@ Basic painting functions:
Returns the string that should be used to represent the given
logical keybinding on the screen in texts like "press Key to ...".
* ``dfhack.screen.keyToChar(key)``
Returns the integer character code of the string input
character represented by the given logical keybinding,
or *nil* if not a string input key.
* ``dfhack.screen.charToKey(charcode)``
Returns the keybinding representing the given string input
character, or *nil* if impossible.
The "pen" argument used by functions above may be represented by
a table with the following possible fields:
@ -3161,6 +3183,22 @@ These events are straight from EventManager module. Each of them first needs to
Gets called when someone picks up an item, puts one down, or changes the way they are holding it. If an item is picked up, old_equip will be null. If an item is dropped, new_equip will be null. If an item is re-equipped in a new way, then neither will be null. You absolutely must NOT alter either old_equip or new_equip or you might break other plugins.
10. ``onReport(reportId)``
Gets called when a report happens. This happens more often than you probably think, even if it doesn't show up in the announcements.
11. ``onUnitAttack(attackerId, defenderId, woundId)``
Called when a unit wounds another with a weapon. Is NOT called if blocked, dodged, deflected, or parried.
12. ``onUnload()``
A convenience event in case you don't want to register for every onStateChange event.
13. ``onInteraction(attackVerb, defendVerb, attackerId, defenderId, attackReportId, defendReportId)``
Called when a unit uses an interaction on another.
Functions
---------
@ -3182,7 +3220,8 @@ Functions
5. ``registerSidebar(shop_name,callback)``
Enable callback when sidebar for ``shop_name`` is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib.
Enable callback when sidebar for ``shop_name`` is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib. Also accepts a ``class`` instead of function
as callback. Best used with ``gui.dwarfmode`` class ``WorkshopOverlay``.
Examples
--------
@ -3239,7 +3278,8 @@ Functions
a. tables of 4 numbers ``{tile,fore,back,bright}`` OR
b. empty table (tile not modified) OR
c. ``{x=<number> y=<number> + 4 numbers like in first case}``, this generates full frame useful for animations that change little (1-2 tiles)
8. canBeRoomSubset -- a flag if this building can be counted in room. 1 means it can, 0 means it can't and -1 default building behaviour
Animate table also might contain:
1. frameLenght -- how many ticks does one frame take OR
2. isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning)

279
NEWS

@ -1,28 +1,301 @@
DFHack future
DFHack Future
Internals
Fixes
New Plugins
New Scripts
Misc Improvements
DFHack 0.40.19-r1
Internals:
Fixes:
typo fix in modtools/reaction-trigger
modtools/item-trigger should now work with item types
New plugins:
savestock and loadstock: two commands for saving and loading
stockpile settings to a file. They can be used to migrate stockpile
settings between worlds and saves.
New scripts:
remove-stress [all]: set selected or all units unit to -1,000,000 stress
this script replaces removebadthoughts.rb
Misc improvements:
cmd-prompt can now access selected items, units, and buildings
autolabor plugin: add an optional talent pool parameter
DFHack 0.40.16-r1
Internals:
EventManager should handle INTERACTION triggers a little better. It still can get confused about who did what but only rarely.
EventManager should no longer trigger REPORT events for old reports after loading a save.
lua/persist-table.lua: a convenient way of using persistent tables of arbitrary structure and dimension in Lua
Fixes:
mousequery: Disabled when linking levers
stocks: Melting should work now
scripts/full-heal: Updated with proper argument handling.
scripts/modtools/reaction-trigger-transition.lua
should produce the correct syntax now
scripts/superdwarf.rb should work better now
scripts/forum-dwarves.lua
update for new df-structures changes
New scripts:
scripts/
adaptation.rb
view or set the cavern adaptation level of your citizens
add-thought.lua
allows the user to add thoughts to creatures.
gaydar.lua
detect the sexual orientation of units on the map
markdown.lua
Save a copy of a text screen in markdown (for reddit among others).
devel/
all-bob.lua: renames everyone Bob to help test interaction-trigger
Misc improvements:
autodump:
Can now mark a stockpile for auto-dumping (similar to
automelt and autotrade)
buildingplan:
Can now auto-allocate rooms to dwarves with specific positions
(e.g. expedition leader, mayor)
dwarfmonitor
Now displays a weather indicator and date
lua/syndrome-util.lua
now you can remove syndromes by SYN_CLASS
scripts/modtools/add-syndrome.lua
now you can remove syndromes by SYN_CLASS
No longer writes empty .history files
DFHack 0.40.15-r1
Fixes:
- mousequery: Fixed behavior when selecting a tile on the lowest z-level
Internals:
- EventManager: deals with frame_counter getting reset properly now.
- modtools/item-trigger: fixed equip/unequip bug and corrected minor documentation error
- teleport: Updated with proper argument handling and proper unit-at-destination handling.
- autotrade: Removed the newly obsolete "Mark all" functionality.
- search: Adapts to the new trade screen column width
- tweak fast-trade: Switching the fast-trade keybinding to Shift-Up/Shift-Down, due to Select All conflict
DFHack 0.40.14-r1
Internals:
- The DFHack console can now be disabled by setting the DFHACK_DISABLE_CONSOLE
environment variable: "DFHACK_DISABLE_CONSOLE=1 ./dfhack"
Fixes:
- Stopped duplicate load/unload events when unloading a world
- Stopped "-e" from being echoed when DFHack quits on Linux
- automelt now uses a faster method to locate items
- autotrade: "Mark all" no longer double-marks bin contents
- drainaquifer.rb: replaced with a faster less buggy drain-aquifer.lua
- embark-tools no longer conflicts with keys on the notes screen
- fastdwarf: Fixed problems with combat/attacks
- forum-dwarves should work now
- manipulator now uses a stable sort, allowing sorting by multiple categories
- rendermax updated to work with 0.40
New plugins:
- trackstop: Shows track stop friction and dump direction in its 'q' menu
New tweaks:
- farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus
- import-priority-category: Allows changing the priority of all goods in a
category when discussing an import agreement with the liaison
- manager-quantity: Removes the limit of 30 jobs per manager order
- civ-view-agreement: Fixes overlapping text on the "view agreement" screen
- nestbox-color: Fixes the color of built nestboxes
Misc Improvements:
- exportlegends.lua can now handle site maps
DFHack 0.40.13-r1
Internals:
- unified spatter structs
- added ruby df.print_color(color, string) method for dfhack console
Fixes:
- no more -e after terminating
- fixed superdwarf
DFHack 0.40.12-r1
Fixes:
- possible crash fixed for hack-wish
- updated search to not conflict with BUILDJOB_SUSPEND
- workflow: job_material_category -> dfhack_material_category
New plugins:
- hotkeys (by Falconne): Shows ingame viewscreen with all dfhack keybindings active in current mode.
- automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting)
Misc Improvements:
- now you can use @ to print things in interactive Lua with subtley different semantics
- optimizations for stockpiles for autotrade and stockflow
- updated exportlegends.lua to work with new maps, dfhack 40.11 r1+
Internals:
- support for global onLoadWorld.init and onUnloadWorld.init files,
called when loading and unloading a world
- Close file after loading a binary patch.
DFHack 0.40.11-r1
Internals:
- Plugins on OS X now use ".plug.dylib" as an extension instead of ".plug.so"
Fixes:
- 3dveins should no longer hang/crash on specific maps
- Fixed some autotrade and search layout issues
- Updated deathcause
- hack-wish should work now
- reveal no longer allocates data for nonexistent map blocks
- Various documentation fixes and updates
DFHack v0.40.10-r1
A few bugfixes.
DFHack v0.40.08-r2
Internals:
supported per save script folders
Items module: added createItem function
Sorted CMakeList for plugins and plugins/devel
diggingInvaders no longer builds if plugin building is disabled
EventManager:
EQUIPMENT_CHANGE now triggers for new units
new events:
ON_REPORT
UNIT_ATTACK
UNLOAD
INTERACTION
New scripts:
lua/
repeat-util.lua
makes it easier to make things repeat indefinitely
syndrome-util.lua
makes it easier to deal with unit syndromes
scripts/
forum-dwarves.lua
helps copy df viewscreens to a file
full-heal.lua
fully heal a unit
remove-wear.lua
removes wear from all items in the fort
repeat.lua
repeatedly calls a script or a plugin
ShowUnitSyndromes.rb
shows syndromes affecting units and other relevant info
teleport.lua
teleports units
scripts/devel/
print-args.lua
scripts/fix/
blood-del.lua
makes it so civs don't bring barrels full of blood ichor or goo
feeding-timers.lua
reset the feeding timers of all units
scripts/gui/
hack-wish.lua
creates items out of any material
unit-info-viewer.lua
displays information about units
scripts/modtools/
add-syndrome.lua
add a syndrome to a unit or remove one
anonymous-script.lua
execute an lua script defined by a string. For example, 'scripts/modtools/anonymous-script "print(args[2] .. args[1])" one two' will print 'twoone'. Useful for the *-trigger scripts.
force.lua
forces events: caravan, migrants, diplomat, megabeast, curiousbeast, mischievousbeast, flier, siege, nightcreature
item-trigger.lua
triggers commands based on equipping, unequipping, and wounding units with items
interaction-trigger.lua
triggers commands when interactions happen
invader-item-destroyer.lua
destroys invaders' items when they die
moddable-gods.lua
standardized version of Putnam's moddable gods script
outside-only.lua
register buildings as outside only or inside only
replaces outsideOnly plugin
projectile-trigger.lua
standardized version of projectileExpansion
reaction-trigger.lua
trigger commands when custom reactions complete
replaces autoSyndrome
reaction-trigger-transition.lua
a tool for converting mods from autoSyndrome to reaction-trigger
random-trigger.lua
triggers random scripts that you register
skill-change.lua
for incrementing and setting skills
spawn-flow.lua
creates flows, like mist or dragonfire
syndrome-trigger.lua
trigger commands when syndromes happen
transform-unit.lua
shapeshifts a unit, possibly permanently
New commands:
New tweaks:
New plugins:
Misc improvements:
new function in utils.lua for standardized argument processing
Removed
digmat.rb: digFlood does the same functionality with less FPS impact
scripts/invasionNow: scripts/modtools/force.lua does it better
autoSyndrome replaced with scripts/modtools/reaction-trigger.lua
syndromeTrigger replaced with scripts/modtools/syndrome-trigger.lua
devel/printArgs plugin converted to scripts/devel/print-args.lua
DFHack v0.40.08-r1
Was a mistake. Don't use it.
DFHack v0.34.11-r5
Internals:
- 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 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.
- 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.
New scripts:
- gui/mod-manager: allows installing/uninstalling mods into df from df/mods directory.
- gui/clone-uniform: duplicates the currently selected uniform in the military screen.
- fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it)
- undump-buildings: removes dump designation from materials used in buildings.
- exportlegends: exports data from legends mode, allowing a set-and-forget export of large worlds.
- log-region: each time a fort is loaded identifying information will be written to the gamelog.
- dfstatus: show an overview of critical stock quantities, including food, drinks, wood, and bars.
New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- 'plant create' - spawn a new shrub under the cursor
- command-prompt: a dfhack command prompt in df.
New tweaks:
- 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)
- confirm-embark: adds a prompt before embarking (on the "prepare carefully" screen)
New plugins:
- rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df.
- automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting)
- 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:
- plant: move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- digfort: improved csv parsing, add start() comment handling
- exterminate: allow specifying a caste (exterminate gob:male)
- createitem: in adventure mode it now defaults to the controlled unit as maker.
- autotrade: adds "(Un)mark All" options to both panes of trade screen.
- mousequery: several usability improvements.
- mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor.
- search: workshop profile search added.
- dwarfmonitor: add screen to summarise preferences of fortress dwarfs.
- getplants: add autochop function to automate woodcutting.
- stocks: added more filtering and display options.
Siege engine plugin:
- engine quality and distance to target now affect accuracy

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2
build/.gitignore vendored

@ -1,2 +1,4 @@
VC2010
DF_PATH.txt
_CPack_Packages
*.tar.*

@ -3,4 +3,3 @@ call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
msbuild /m /p:Platform=Win32 /p:Configuration=Release PACKAGE.vcxproj
cd ..
exit %ERRORLEVEL%

@ -22,6 +22,7 @@
//----------------------------------------------------------------------
//basic includes
#include <algorithm>
#include <fstream>
#include <iostream>
#include <errno.h>

@ -47,6 +47,10 @@
#elif !defined(_MSC_VER)
#include <stdint.h>
#endif
#include <algorithm>
using std::min;
using std::max;
// make MSVC shut up about some things
#ifdef _MSC_VER

@ -2,6 +2,10 @@
# Generic dwarfmode bindings #
##############################
# show all current key bindings
keybinding add Ctrl-F1 hotkeys
keybinding add Alt-F1 hotkeys
# toggle the display of water level as 1-7 tiles
keybinding add Ctrl-W twaterlvl
@ -17,6 +21,9 @@ keybinding add Ctrl-C spotclean
# destroy items designated for dump in the selected tile
keybinding add Ctrl-Shift-K autodump-destroy-here
# set the zone or cage under the cursor as the default
keybinding add Alt-Shift-I@dwarfmode/Zones "zone set"
# with an item selected:
# destroy the selected item
@ -34,6 +41,13 @@ keybinding add Ctrl-Shift-T "gui/rename unit-profession"
# a dfhack prompt in df. Sublime text like.
keybinding add Ctrl-Shift-P command-prompt
# show information collected by dwarfmonitor
keybinding add Alt-M@dwarfmode/Default "dwarfmonitor prefs"
keybinding add Ctrl-F@dwarfmode/Default "dwarfmonitor stats"
# export a Dwarf's preferences screen in BBCode to post ot a forum
keybinding add Ctrl-Shift-F@dwarfmode forum-dwarves
##############################
# Generic adv mode bindings #
##############################
@ -41,6 +55,13 @@ keybinding add Ctrl-Shift-P command-prompt
keybinding add Ctrl-B adv-bodyswap
keybinding add Ctrl-Shift-B "adv-bodyswap force"
##############################
# Generic legends bindings #
##############################
# export all information, or just the detailed maps (doesn't handle site maps)
keybinding add Ctrl-A@legends "exportlegends all"
#############################
# Context-specific bindings #
#############################
@ -48,9 +69,8 @@ keybinding add Ctrl-Shift-B "adv-bodyswap force"
# Stocks plugin
keybinding add Ctrl-Shift-Z@dwarfmode/Default "stocks show"
# Workflow
keybinding add Ctrl-W@dwarfmode/QueryBuilding/Some "gui/workflow"
keybinding add Ctrl-I "gui/workflow status"
# open an overview window summarising some stocks (dfstatus)
keybinding add Ctrl-Shift-I@dwarfmode/Default "gui/dfstatus"
# q->stockpile; p - copy & paste stockpiles
keybinding add Alt-P copystock
@ -119,23 +139,10 @@ keybinding add P@dwarfmode/QueryBuilding/Some/Weaponrack gui/assign-rack
# stabilize the cursor of dwarfmode when switching menus
tweak stable-cursor
# stop military from considering training as 'patrol duty'
tweak patrol-duty
# display creature weight in build plate menu as ??K, instead of (???df: Max
tweak readable-build-plate
# improve FPS by squashing endless item temperature update loops
tweak stable-temp
# speed up items reaching temp equilibrium with environment by
# capping the rate to no less than 1 degree change per 500 frames
# Note: will also cause stuff to melt faster in magma etc
tweak fast-heat 500
# stop stacked liquid/bar/thread/cloth items from lasting forever
# if used in reactions that use only a fraction of the dimension.
tweak fix-dimensions
# might be fixed by DF
# tweak fix-dimensions
# make reactions requiring containers usable in advmode - the issue is
# that the screen asks for those reagents to be selected directly
@ -151,10 +158,22 @@ tweak military-stable-assign
tweak military-color-assigned
# remove inverse dependency of squad training speed on unit list size and use more sparring
tweak military-training
# tweak military-training
# make crafted cloth items wear out with time like in old versions (bug 6003)
tweak craft-age-wear
# stop adamantine clothing from wearing out (bug 6481)
#tweak adamantine-cloth-wear
# Add "Select all" and "Deselect all" options to farm plot menus
tweak farm-plot-select
# Add Shift-Left/Right controls to import agreement screen
tweak import-priority-category
# prevent crash if bees die in a hive with ungathered products by insta-gathering them
tweak hive-crash
# Misc. UI tweaks
tweak civ-view-agreement
###########################
# Globally acting plugins #
@ -170,10 +189,10 @@ enable search
enable automaterial
# Other interface improvement tools
#enable dwarfmonitor mousequery autotrade buildingplan resume zone
enable dwarfmonitor mousequery automelt autotrade buildingplan resume trackstop zone stocks autochop
# Auto Syndrome
#autoSyndrome enable
# allow the fortress bookkeeper to queue jobs through the manager
stockflow enable
###########
# Scripts #
@ -182,26 +201,9 @@ enable automaterial
# write the correct season to gamelog on world load
soundsense-season
# patch the material objects in memory to fix cloth stockpiles
fix/cloth-stockpile enable
# write identifying information about the fort to gamelog on world load
log-region
#######################################################
# Apply binary patches at runtime #
#######################################################
# Bug 5994 - items teleported when removing a construction
binpatch apply deconstruct-teleport
binpatch apply deconstruct-heapfall
# Bug 4406 - hospital overstocking on all items
binpatch apply hospital-overstocking
# Bug 808 - custom reactions completely using up all of their reagents
binpatch apply custom-reagent-size
# Bug 4530 - marksdwarves not training when quiver full of combat-only ammo
binpatch apply training-ammo
# Bug 1445 - weapon racks broken, armor stand capacity too low
binpatch apply weaponrack-unassign
binpatch apply armorstand-capacity

@ -1,11 +1,30 @@
#!/bin/bash
# regenerate documentation after editing the .rst files. Requires python and docutils.
rst2html=$(which rst2html || which rst2html.py)
if [[ -z "$rst2html" ]]; then
echo "Docutils not found: See http://docutils.sourceforge.net/"
exit 1
fi
rst2html_version=$("$rst2html" --version | cut -d' ' -f3)
if [[ $(echo $rst2html_version | cut -d. -f2) -lt 12 ]]; then
echo "You are using docutils $rst2html_version. Docutils 0.12+ is recommended
to build these documents."
read -r -n1 -p "Continue? [y/N] " reply
echo
if [[ ! $reply =~ ^[Yy]$ ]]; then
exit
fi
fi
cd `dirname $0`
function process() {
if [ "$1" -nt "$2" ]; then
rst2html --no-generator --no-datestamp "$1" "$2"
echo -n "Updating $2... "
"$rst2html" --no-generator --no-datestamp "$1" "$2"
echo "Done"
else
echo "$2 - up to date."
fi

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@ -85,14 +85,14 @@ IF(WIN32)
ENDIF()
SET(MAIN_SOURCES_LINUX
Console-linux.cpp
Console-posix.cpp
Hooks-linux.cpp
PlugLoad-linux.cpp
Process-linux.cpp
)
SET(MAIN_SOURCES_DARWIN
Console-darwin.cpp
Console-posix.cpp
PlugLoad-darwin.cpp
Process-darwin.cpp
Hooks-darwin.cpp
@ -127,6 +127,7 @@ include/modules/Vermin.h
include/modules/World.h
include/modules/Graphic.h
include/modules/Once.h
include/modules/Filesystem.h
)
SET( MODULE_SOURCES
@ -152,6 +153,7 @@ modules/World.cpp
modules/Graphic.cpp
modules/Windows.cpp
modules/Once.cpp
modules/Filesystem.cpp
)
IF(WIN32)

@ -47,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "ColorText.h"
#include "MiscUtils.h"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <sstream>

@ -1,773 +0,0 @@
/*
https://github.com/peterix/dfhack
A thread-safe logging console with a line editor.
Based on linenoise:
linenoise -- guerrilla line editing library against the idea that a
line editing lib needs to be 20,000 lines of C code.
You can find the latest source code at:
http://github.com/antirez/linenoise
Does a number of crazy assumptions that happen to be true in 99.9999% of
the 2010 UNIX computers around.
------------------------------------------------------------------------
Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
Copyright (c) 2011, Petr Mrázek <peterix@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string.h>
#include <string>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <errno.h>
#include <deque>
// George Vulov for MacOSX
#ifndef __LINUX__
#define TEMP_FAILURE_RETRY(expr) \
({ long int _res; \
do _res = (long int) (expr); \
while (_res == -1L && errno == EINTR); \
_res; })
#endif
#include "Console.h"
#include "Hooks.h"
using namespace DFHack;
#include "tinythread.h"
using namespace tthread;
static int isUnsupportedTerm(void)
{
static const char *unsupported_term[] = {"dumb","cons25",NULL};
char *term = getenv("TERM");
int j;
if (term == NULL) return 0;
for (j = 0; unsupported_term[j]; j++)
if (!strcasecmp(term,unsupported_term[j])) return 1;
return 0;
}
const char * ANSI_CLS = "\033[2J";
const char * ANSI_BLACK = "\033[22;30m";
const char * ANSI_RED = "\033[22;31m";
const char * ANSI_GREEN = "\033[22;32m";
const char * ANSI_BROWN = "\033[22;33m";
const char * ANSI_BLUE = "\033[22;34m";
const char * ANSI_MAGENTA = "\033[22;35m";
const char * ANSI_CYAN = "\033[22;36m";
const char * ANSI_GREY = "\033[22;37m";
const char * ANSI_DARKGREY = "\033[01;30m";
const char * ANSI_LIGHTRED = "\033[01;31m";
const char * ANSI_LIGHTGREEN = "\033[01;32m";
const char * ANSI_YELLOW = "\033[01;33m";
const char * ANSI_LIGHTBLUE = "\033[01;34m";
const char * ANSI_LIGHTMAGENTA = "\033[01;35m";
const char * ANSI_LIGHTCYAN = "\033[01;36m";
const char * ANSI_WHITE = "\033[01;37m";
const char * RESETCOLOR = "\033[0m";
const char * getANSIColor(const int c)
{
switch (c)
{
case -1: return RESETCOLOR; // HACK! :P
case 0 : return ANSI_BLACK;
case 1 : return ANSI_BLUE; // non-ANSI
case 2 : return ANSI_GREEN;
case 3 : return ANSI_CYAN; // non-ANSI
case 4 : return ANSI_RED; // non-ANSI
case 5 : return ANSI_MAGENTA;
case 6 : return ANSI_BROWN;
case 7 : return ANSI_GREY;
case 8 : return ANSI_DARKGREY;
case 9 : return ANSI_LIGHTBLUE; // non-ANSI
case 10: return ANSI_LIGHTGREEN;
case 11: return ANSI_LIGHTCYAN; // non-ANSI;
case 12: return ANSI_LIGHTRED; // non-ANSI;
case 13: return ANSI_LIGHTMAGENTA;
case 14: return ANSI_YELLOW; // non-ANSI
case 15: return ANSI_WHITE;
default: return "";
}
}
namespace DFHack
{
class Private
{
public:
Private()
{
dfout_C = NULL;
rawmode = false;
in_batch = false;
supported_terminal = false;
state = con_unclaimed;
};
virtual ~Private()
{
//sync();
}
private:
bool read_char(unsigned char & out)
{
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(exit_pipe[0], &descriptor_set);
int ret = TEMP_FAILURE_RETRY(
select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL)
);
if(ret == -1)
return false;
if (FD_ISSET(exit_pipe[0], &descriptor_set))
return false;
if (FD_ISSET(STDIN_FILENO, &descriptor_set))
{
// read byte from stdin
ret = TEMP_FAILURE_RETRY(
read(STDIN_FILENO, &out, 1)
);
if(ret == -1)
return false;
return true;
}
return false;
}
public:
void print(const char *data)
{
fputs(data, dfout_C);
}
void print_text(color_ostream::color_value clr, const std::string &chunk)
{
if(!in_batch && state == con_lineedit)
{
disable_raw();
fprintf(dfout_C,"\x1b[1G");
fprintf(dfout_C,"\x1b[0K");
color(clr);
print(chunk.c_str());
reset_color();
enable_raw();
prompt_refresh();
}
else
{
color(clr);
print(chunk.c_str());
}
}
void begin_batch()
{
assert(!in_batch);
in_batch = true;
if (state == con_lineedit)
{
disable_raw();
fprintf(dfout_C,"\x1b[1G");
fprintf(dfout_C,"\x1b[0K");
}
}
void end_batch()
{
assert(in_batch);
flush();
in_batch = false;
if (state == con_lineedit)
{
reset_color();
enable_raw();
prompt_refresh();
}
}
void flush()
{
if (!rawmode)
fflush(dfout_C);
}
/// Clear the console, along with its scrollback
void clear()
{
if(rawmode)
{
const char * clr = "\033c\033[3J\033[H";
::write(STDIN_FILENO,clr,strlen(clr));
}
else
{
print("\033c\033[3J\033[H");
fflush(dfout_C);
}
}
/// Position cursor at x,y. 1,1 = top left corner
void gotoxy(int x, int y)
{
char tmp[64];
sprintf(tmp,"\033[%d;%dH", y,x);
print(tmp);
}
/// Set color (ANSI color number)
void color(Console::color_value index)
{
if(!rawmode)
fprintf(dfout_C,getANSIColor(index));
else
{
const char * colstr = getANSIColor(index);
int lstr = strlen(colstr);
::write(STDIN_FILENO,colstr,lstr);
}
}
/// Reset color to default
void reset_color(void)
{
color(COLOR_RESET);
if(!rawmode)
fflush(dfout_C);
}
/// Enable or disable the caret/cursor
void cursor(bool enable = true)
{
if(enable)
print("\033[?25h");
else
print("\033[?25l");
}
/// Waits given number of milliseconds before continuing.
void msleep(unsigned int msec);
/// get the current number of columns
int get_columns(void)
{
winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80;
return ws.ws_col;
}
/// get the current number of rows
int get_rows(void)
{
winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25;
return ws.ws_row;
}
/// beep. maybe?
//void beep (void);
/// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch)
{
output.clear();
reset_color();
this->prompt = prompt;
if (!supported_terminal)
{
print(prompt.c_str());
fflush(dfout_C);
// FIXME: what do we do here???
//SDL_recursive_mutexV(lock);
std::getline(std::cin, output);
//SDL_recursive_mutexP(lock);
return output.size();
}
else
{
int count;
if (enable_raw() == -1) return 0;
if(state == con_lineedit)
return -1;
state = con_lineedit;
count = prompt_loop(lock,ch);
state = con_unclaimed;
disable_raw();
print("\n");
if(count != -1)
{
output = raw_buffer;
}
return count;
}
}
int enable_raw()
{
struct termios raw;
if (!supported_terminal)
return -1;
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
return -1;
raw = orig_termios; //modify the original mode
// input modes: no break, no CR to NL, no parity check, no strip char,
// no start/stop output control.
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// output modes - disable post processing
raw.c_oflag &= ~(OPOST);
// control modes - set 8 bit chars
raw.c_cflag |= (CS8);
// local modes - choing off, canonical off, no extended functions,
// no signal chars (^Z,^C)
#ifdef CONSOLE_NO_CATCH
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN );
#else
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG );
#endif
// control chars - set return condition: min number of bytes and timer.
// We want read to return every single byte, without timeout.
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
// put terminal in raw mode after flushing
if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0)
return -1;
rawmode = 1;
return 0;
}
void disable_raw()
{
/* Don't even check the return value as it's too late. */
if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig_termios) != -1)
rawmode = 0;
}
void prompt_refresh()
{
char seq[64];
int cols = get_columns();
int plen = prompt.size();
const char * buf = raw_buffer.c_str();
int len = raw_buffer.size();
int cooked_cursor = raw_cursor;
// Use math! This is silly.
while((plen+cooked_cursor) >= cols)
{
buf++;
len--;
cooked_cursor--;
}
while (plen+len > cols)
{
len--;
}
/* Cursor to left edge */
snprintf(seq,64,"\x1b[1G");
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
/* Write the prompt and the current buffer content */
if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return;
if (::write(STDIN_FILENO,buf,len) == -1) return;
/* Erase to right */
snprintf(seq,64,"\x1b[0K");
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
/* Move cursor to original position. */
snprintf(seq,64,"\x1b[1G\x1b[%dC", (int)(cooked_cursor+plen));
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
}
int prompt_loop(recursive_mutex * lock, CommandHistory & history)
{
int fd = STDIN_FILENO;
size_t plen = prompt.size();
int history_index = 0;
raw_buffer.clear();
raw_cursor = 0;
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
const std::string empty;
history.add(empty);
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1;
while(1)
{
unsigned char c;
int isok;
unsigned char seq[2], seq2;
lock->unlock();
if(!read_char(c))
{
lock->lock();
return -2;
}
lock->lock();
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == 9)
{
/*
if( completionCallback != NULL) {
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
// Return on errors
if (c < 0) return len;
// Read next character when 0
if (c == 0) continue;
}
else
{
// ignore tab
continue;
}
*/
// just ignore tabs
continue;
}
switch(c)
{
case 13: // enter
history.remove();
return raw_buffer.size();
case 3: // ctrl-c
errno = EAGAIN;
return -1;
case 127: // backspace
case 8: // ctrl-h
if (raw_cursor > 0 && raw_buffer.size() > 0)
{
raw_buffer.erase(raw_cursor-1,1);
raw_cursor--;
prompt_refresh();
}
break;
case 27: // escape sequence
lock->unlock();
if(!read_char(seq[0]) || !read_char(seq[1]))
{
lock->lock();
return -2;
}
lock->lock();
if(seq[0] == '[')
{
if (seq[1] == 'D')
{
left_arrow:
if (raw_cursor > 0)
{
raw_cursor--;
prompt_refresh();
}
}
else if ( seq[1] == 'C')
{
right_arrow:
/* right arrow */
if (size_t(raw_cursor) != raw_buffer.size())
{
raw_cursor++;
prompt_refresh();
}
}
else if (seq[1] == 'A' || seq[1] == 'B')
{
/* up and down arrow: history */
if (history.size() > 1)
{
/* Update the current history entry before to
* overwrite it with tne next one. */
history[history_index] = raw_buffer;
/* Show the new entry */
history_index += (seq[1] == 'A') ? 1 : -1;
if (history_index < 0)
{
history_index = 0;
break;
}
else if (size_t(history_index) >= history.size())
{
history_index = history.size()-1;
break;
}
raw_buffer = history[history_index];
raw_cursor = raw_buffer.size();
prompt_refresh();
}
}
else if(seq[1] == 'H')
{
// home
raw_cursor = 0;
prompt_refresh();
}
else if(seq[1] == 'F')
{
// end
raw_cursor = raw_buffer.size();
prompt_refresh();
}
else if (seq[1] > '0' && seq[1] < '7')
{
// extended escape
lock->unlock();
if(!read_char(seq2))
{
lock->lock();
return -2;
}
lock->lock();
if (seq[1] == '3' && seq2 == '~' )
{
// delete
if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size())
{
raw_buffer.erase(raw_cursor,1);
prompt_refresh();
}
}
}
}
break;
default:
if (raw_buffer.size() == size_t(raw_cursor))
{
raw_buffer.append(1,c);
raw_cursor++;
if (plen+raw_buffer.size() < size_t(get_columns()))
{
/* Avoid a full update of the line in the
* trivial case. */
if (::write(fd,&c,1) == -1) return -1;
}
else
{
prompt_refresh();
}
}
else
{
raw_buffer.insert(raw_cursor,1,c);
raw_cursor++;
prompt_refresh();
}
break;
case 21: // Ctrl+u, delete the whole line.
raw_buffer.clear();
raw_cursor = 0;
prompt_refresh();
break;
case 11: // Ctrl+k, delete from current to end of line.
raw_buffer.erase(raw_cursor);
prompt_refresh();
break;
case 1: // Ctrl+a, go to the start of the line
raw_cursor = 0;
prompt_refresh();
break;
case 5: // ctrl+e, go to the end of the line
raw_cursor = raw_buffer.size();
prompt_refresh();
break;
case 12: // ctrl+l, clear screen
clear();
prompt_refresh();
}
}
return raw_buffer.size();
}
FILE * dfout_C;
bool supported_terminal;
// state variables
bool rawmode; // is raw mode active?
termios orig_termios; // saved/restored by raw mode
// current state
enum console_state
{
con_unclaimed,
con_lineedit
} state;
bool in_batch;
std::string prompt; // current prompt string
std::string raw_buffer; // current raw mode buffer
int raw_cursor; // cursor position in the buffer
// thread exit mechanism
int exit_pipe[2];
fd_set descriptor_set;
};
}
Console::Console()
{
d = 0;
inited = false;
// we can't create the mutex at this time. the SDL functions aren't hooked yet.
wlock = new recursive_mutex();
}
Console::~Console()
{
if(inited)
shutdown();
if(wlock)
delete wlock;
if(d)
delete d;
}
bool Console::init(bool sharing)
{
if(sharing)
{
inited = false;
return false;
}
freopen("stdout.log", "w", stdout);
d = new Private();
// make our own weird streams so our IO isn't redirected
d->dfout_C = fopen("/dev/tty", "w");
std::cin.tie(this);
clear();
d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO);
// init the exit mechanism
pipe(d->exit_pipe);
FD_ZERO(&d->descriptor_set);
FD_SET(STDIN_FILENO, &d->descriptor_set);
FD_SET(d->exit_pipe[0], &d->descriptor_set);
inited = true;
return true;
}
bool Console::shutdown(void)
{
if(!d)
return true;
lock_guard <recursive_mutex> g(*wlock);
if(d->rawmode)
d->disable_raw();
d->print("\n");
inited = false;
// kill the thing
close(d->exit_pipe[1]);
return true;
}
void Console::begin_batch()
{
//color_ostream::begin_batch();
wlock->lock();
if (inited)
d->begin_batch();
}
void Console::end_batch()
{
if (inited)
d->end_batch();
wlock->unlock();
}
void Console::flush_proxy()
{
lock_guard <recursive_mutex> g(*wlock);
if (inited)
d->flush();
}
void Console::add_text(color_value color, const std::string &text)
{
lock_guard <recursive_mutex> g(*wlock);
if (inited)
d->print_text(color, text);
}
int Console::get_columns(void)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -1;
if(inited)
ret = d->get_columns();
return ret;
}
int Console::get_rows(void)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -1;
if(inited)
ret = d->get_rows();
return ret;
}
void Console::clear()
{
lock_guard <recursive_mutex> g(*wlock);
if(inited)
d->clear();
}
void Console::gotoxy(int x, int y)
{
lock_guard <recursive_mutex> g(*wlock);
if(inited)
d->gotoxy(x,y);
}
void Console::cursor(bool enable)
{
lock_guard <recursive_mutex> g(*wlock);
if(inited)
d->cursor(enable);
}
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -2;
if(inited)
ret = d->lineedit(prompt,output,wlock,ch);
return ret;
}
void Console::msleep (unsigned int msec)
{
if (msec > 1000) sleep(msec/1000000);
usleep((msec % 1000000) * 1000);
}

@ -46,6 +46,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <iostream>
@ -85,7 +86,7 @@ static int isUnsupportedTerm(void)
if (term == NULL) return 0;
for (j = 0; unsupported_term[j]; j++)
if (!strcasecmp(term,unsupported_term[j])) return 1;
return 0;
return 0;
}
const char * ANSI_CLS = "\033[2J";
@ -307,6 +308,33 @@ namespace DFHack
}
/// beep. maybe?
//void beep (void);
void back_word()
{
if (raw_cursor == 0)
return;
raw_cursor--;
while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor]))
raw_cursor--;
while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor]))
raw_cursor--;
if (!isalnum(raw_buffer[raw_cursor]) && raw_cursor != 0)
raw_cursor++;
prompt_refresh();
}
void forward_word()
{
int len = raw_buffer.size();
if (raw_cursor == len)
return;
raw_cursor++;
while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor]))
raw_cursor++;
while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor]))
raw_cursor++;
if (raw_cursor > len)
raw_cursor = len;
prompt_refresh();
}
/// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch)
{
@ -480,14 +508,27 @@ namespace DFHack
break;
case 27: // escape sequence
lock->unlock();
if(!read_char(seq[0]) || !read_char(seq[1]))
if (!read_char(seq[0]))
{
lock->lock();
return -2;
}
lock->lock();
if(seq[0] == '[')
if (seq[0] == 'b')
{
back_word();
}
else if (seq[0] == 'f')
{
forward_word();
}
else if(seq[0] == '[')
{
if (!read_char(seq[1]))
{
lock->lock();
return -2;
}
if (seq[1] == 'D')
{
left_arrow:
@ -547,6 +588,7 @@ namespace DFHack
else if (seq[1] > '0' && seq[1] < '7')
{
// extended escape
unsigned char seq3[3];
lock->unlock();
if(!read_char(seq2))
{
@ -563,41 +605,58 @@ namespace DFHack
prompt_refresh();
}
}
if (!read_char(seq3[0]) || !read_char(seq3[1]))
{
lock->lock();
return -2;
}
if (seq2 == ';')
{
// Format: esc [ n ; n DIRECTION
// Ignore first character (second "n")
if (seq3[1] == 'C')
{
forward_word();
}
else if (seq3[1] == 'D')
{
back_word();
}
}
}
}
break;
default:
if (raw_buffer.size() == size_t(raw_cursor))
{
raw_buffer.append(1,c);
raw_cursor++;
if (plen+raw_buffer.size() < size_t(get_columns()))
{
/* Avoid a full update of the line in the
* trivial case. */
if (::write(fd,&c,1) == -1) return -1;
}
else
{
prompt_refresh();
}
}
else
{
raw_buffer.insert(raw_cursor,1,c);
raw_cursor++;
prompt_refresh();
}
break;
case 21: // Ctrl+u, delete the whole line.
raw_buffer.clear();
case 21: // Ctrl+u, delete from current to beginning of line.
if (raw_cursor > 0)
yank_buffer = raw_buffer.substr(0, raw_cursor);
raw_buffer.erase(0, raw_cursor);
raw_cursor = 0;
prompt_refresh();
break;
case 11: // Ctrl+k, delete from current to end of line.
if (raw_cursor < raw_buffer.size())
yank_buffer = raw_buffer.substr(raw_cursor);
raw_buffer.erase(raw_cursor);
prompt_refresh();
break;
case 25: // Ctrl+y, paste last text deleted with Ctrl+u/k
if (yank_buffer.size())
{
raw_buffer.insert(raw_cursor, yank_buffer);
raw_cursor += yank_buffer.size();
prompt_refresh();
}
break;
case 20: // Ctrl+t, transpose current and previous characters
if (raw_buffer.size() >= 2 && raw_cursor > 0)
{
if (raw_cursor == raw_buffer.size())
raw_cursor--;
std::swap(raw_buffer[raw_cursor - 1], raw_buffer[raw_cursor]);
raw_cursor++;
prompt_refresh();
}
break;
case 1: // Ctrl+a, go to the start of the line
raw_cursor = 0;
prompt_refresh();
@ -609,6 +668,32 @@ namespace DFHack
case 12: // ctrl+l, clear screen
clear();
prompt_refresh();
default:
if (c >= 32) // Space
{
if (raw_buffer.size() == size_t(raw_cursor))
{
raw_buffer.append(1,c);
raw_cursor++;
if (plen+raw_buffer.size() < size_t(get_columns()))
{
/* Avoid a full update of the line in the
* trivial case. */
if (::write(fd,&c,1) == -1) return -1;
}
else
{
prompt_refresh();
}
}
else
{
raw_buffer.insert(raw_cursor,1,c);
raw_cursor++;
prompt_refresh();
}
break;
}
}
}
return raw_buffer.size();
@ -625,9 +710,10 @@ namespace DFHack
con_lineedit
} state;
bool in_batch;
std::string prompt; // current prompt string
std::string raw_buffer; // current raw mode buffer
int raw_cursor; // cursor position in the buffer
std::string prompt; // current prompt string
std::string raw_buffer; // current raw mode buffer
std::string yank_buffer; // last text deleted with Ctrl-K/Ctrl-U
int raw_cursor; // cursor position in the buffer
// thread exit mechanism
int exit_pipe[2];
fd_set descriptor_set;

@ -61,6 +61,9 @@ using namespace DFHack;
#include "df/world_data.h"
#include "df/interfacest.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_game_cleanerst.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include <df/graphic.h>
#include <stdio.h>
@ -393,6 +396,28 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
return false;
}
string findScript(string path, string name) {
if (df::global::world) {
//first try the save folder if it exists
string save = World::ReadWorldFolder();
if ( save != "" ) {
string file = path + "/data/save/" + save + "/raw/scripts/" + name;
if (fileExists(file)) {
return file;
}
}
}
string file = path + "/raw/scripts/" + name;
if (fileExists(file)) {
return file;
}
file = path + "/hack/scripts/" + name;
if (fileExists(file)) {
return file;
}
return "";
}
command_result Core::runCommand(color_ostream &con, const std::string &first, vector<string> &parts)
{
if (!first.empty())
@ -446,18 +471,20 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
return CR_OK;
}
}
auto filename = getHackPath() + "scripts/" + parts[0];
if (fileExists(filename + ".lua"))
{
string help = getScriptHelp(filename + ".lua", "-- ");
string path = this->p->getPath();
string file = findScript(path, parts[0] + ".lua");
if ( file != "" ) {
string help = getScriptHelp(file, "-- ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK;
}
if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb"))
{
string help = getScriptHelp(filename + ".rb", "# ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK;
if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) {
file = findScript(path, parts[0] + ".rb");
if ( file != "" ) {
string help = getScriptHelp(file, "# ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK;
}
}
con.printerr("Unknown command: %s\n", parts[0].c_str());
}
@ -765,15 +792,19 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
command_result res = plug_mgr->InvokeCommand(con, first, parts);
if(res == CR_NOT_IMPLEMENTED)
{
auto filename = getHackPath() + "scripts/" + first;
std::string completed;
if (fileExists(filename + ".lua"))
string completed;
string path = this->p->getPath();
string filename = findScript(path, first + ".lua");
bool lua = filename != "";
if ( !lua ) {
filename = findScript(path, first + ".rb");
}
if ( lua )
res = runLuaScript(con, first, parts);
else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb"))
else if ( filename != "" && plug_mgr->ruby && plug_mgr->ruby->is_enabled() )
res = runRubyScript(con, plug_mgr, first, parts);
else if (try_autocomplete(con, first, completed))
return CR_NOT_IMPLEMENTED;// runCommand(con, completed, parts);
else if ( try_autocomplete(con, first, completed) )
return CR_NOT_IMPLEMENTED;
else
con.printerr("%s is not a recognized command.\n", first.c_str());
}
@ -811,13 +842,29 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
}
}
static void run_dfhack_init(color_ostream &out, Core *core)
{
if (!df::global::world || !df::global::ui || !df::global::gview)
{
out.printerr("Key globals are missing, skipping loading dfhack.init.\n");
return;
}
if (!core->loadScriptFile(out, "dfhack.init", true))
{
core->runCommand(out, "gui/no-dfhack-init");
core->loadScriptFile(out, "dfhack.init-example", true);
}
}
// Load dfhack.init in a dedicated thread (non-interactive console mode)
void fInitthread(void * iodata)
{
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
color_ostream_proxy out(core->getConsole());
core->loadScriptFile(out, "dfhack.init", true);
run_dfhack_init(out, core);
}
// A thread function... for the interactive console.
@ -837,7 +884,7 @@ void fIOthread(void * iodata)
return;
}
core->loadScriptFile(con, "dfhack.init", true);
run_dfhack_init(con, core);
con.print("DFHack is ready. Have a nice day!\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
@ -919,6 +966,8 @@ void Core::fatal (std::string output, bool deactivate)
#ifndef LINUX_BUILD
out << "Check file stderr.log for details\n";
MessageBox(0,out.str().c_str(),"DFHack error!", MB_OK | MB_ICONERROR);
#else
cout << "DFHack fatal error: " << out.str() << std::endl;
#endif
}
@ -981,12 +1030,15 @@ bool Core::Init()
cerr << "Initializing Console.\n";
// init the console.
bool is_text_mode = false;
if(init && init->display.flag.is_set(init_display_flags::TEXT))
bool is_text_mode = (init && init->display.flag.is_set(init_display_flags::TEXT));
if (is_text_mode || getenv("DFHACK_DISABLE_CONSOLE"))
{
is_text_mode = true;
con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n";
if (!is_text_mode)
{
cout << "Console disabled.\n";
}
}
else if(con.init(false))
cerr << "Console is running.\n";
@ -1224,10 +1276,24 @@ void Core::doUpdate(color_ostream &out, bool first_update)
if (first_update)
onStateChange(out, SC_CORE_INITIALIZED);
// find the current viewscreen
df::viewscreen *screen = NULL;
if (df::global::gview)
{
screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
}
bool is_load_save =
strict_virtual_cast<df::viewscreen_game_cleanerst>(screen) ||
strict_virtual_cast<df::viewscreen_loadgamest>(screen) ||
strict_virtual_cast<df::viewscreen_savegamest>(screen);
// detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL;
void *new_mapdata = NULL;
if (df::global::world)
if (df::global::world && !is_load_save)
{
df::world_data *wdata = df::global::world->world_data;
// when the game is unloaded, world_data isn't deleted, but its contents are
@ -1269,16 +1335,10 @@ void Core::doUpdate(color_ostream &out, bool first_update)
}
// detect if the viewscreen changed
if (df::global::gview)
if (screen != top_viewscreen)
{
df::viewscreen *screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
if (screen != top_viewscreen)
{
top_viewscreen = screen;
onStateChange(out, SC_VIEWSCREEN_CHANGED);
}
top_viewscreen = screen;
onStateChange(out, SC_VIEWSCREEN_CHANGED);
}
if (df::global::pause_state)
@ -1371,7 +1431,9 @@ void Core::onUpdate(color_ostream &out)
}
static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_change_event event) {
//TODO: use different separators for windows
if (!df::global::world)
return;
//TODO: use different separators for windows
#ifdef _WIN32
static const std::string separator = "\\";
#else
@ -1380,9 +1442,11 @@ static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_cha
std::string rawFolder = "data" + separator + "save" + separator + (df::global::world->cur_savegame.save_dir) + separator + "raw" + separator;
switch(event) {
case SC_WORLD_LOADED:
core->loadScriptFile(out, "onLoadWorld.init", true);
core->loadScriptFile(out, rawFolder + "onLoad.init", true);
break;
case SC_WORLD_UNLOADED:
core->loadScriptFile(out, "onUnloadWorld.init", true);
core->loadScriptFile(out, rawFolder + "onUnload.init", true);
break;
default:

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

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

@ -64,6 +64,15 @@ DYLD_INTERPOSE(DFH_SDL_Init,SDL_Init);
DYLD_INTERPOSE(DFH_SDL_PollEvent,SDL_PollEvent);
DYLD_INTERPOSE(DFH_SDL_Quit,SDL_Quit);
DYLD_INTERPOSE(DFH_SDL_NumJoysticks,SDL_NumJoysticks);
DYLD_INTERPOSE(DFH_SDL_CreateRGBSurface,SDL_CreateRGBSurface);
DYLD_INTERPOSE(DFH_SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom);
DYLD_INTERPOSE(DFH_SDL_UnlockSurface,SDL_UnlockSurface);
DYLD_INTERPOSE(DFH_SDL_LockSurface,SDL_LockSurface);
DYLD_INTERPOSE(DFH_SDL_ConvertSurface,SDL_ConvertSurface);
DYLD_INTERPOSE(DFH_SDL_FreeSurface,SDL_FreeSurface);
DYLD_INTERPOSE(DFH_SDL_GetMouseState,SDL_GetMouseState);
DYLD_INTERPOSE(DFH_SDL_GetVideoSurface,SDL_GetVideoSurface);
DYLD_INTERPOSE(DFH_SDL_UpperBlit,SDL_UpperBlit);
/*******************************************************************************
* SDL part starts here *
@ -168,4 +177,97 @@ DFhackCExport int DFH_SDL_Init(uint32_t flags)
//int ret = _SDL_Init(flags);
int ret = SDL_Init(flags);
return ret;
}
// New SDL functions starting in r5
static int (*_SDL_CreateRGBSurface)(uint32_t flags, int width, int height, int depth,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0;
DFhackCExport vPtr DFH_SDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask)
{
return SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask);
}
static vPtr (*_SDL_CreateRGBSurfaceFrom)(vPtr pixels, int width, int height, int depth, int pitch,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0;
DFhackCExport vPtr DFH_SDL_CreateRGBSurfaceFrom(vPtr pixels, int width, int height, int depth, int pitch,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask)
{
return SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask);
}
static void (*_SDL_FreeSurface)(vPtr surface) = 0;
DFhackCExport void DFH_SDL_FreeSurface(vPtr surface)
{
SDL_FreeSurface(surface);
}
static vPtr (*_SDL_ConvertSurface)(vPtr surface, vPtr format, uint32_t flags) = 0;
DFhackCExport vPtr DFH_SDL_ConvertSurface(vPtr surface, vPtr format, uint32_t flags)
{
return SDL_ConvertSurface(surface, format, flags);
}
static int (*_SDL_LockSurface)(vPtr surface) = 0;
DFhackCExport int DFH_SDL_LockSurface(vPtr surface)
{
return SDL_LockSurface(surface);
}
static void (*_SDL_UnlockSurface)(vPtr surface) = 0;
DFhackCExport void DFH_SDL_UnlockSurface(vPtr surface)
{
SDL_UnlockSurface(surface);
}
static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0;
DFhackCExport uint8_t DFH_SDL_GetMouseState(int *x, int *y)
{
return SDL_GetMouseState(x,y);
}
static void * (*_SDL_GetVideoSurface)( void ) = 0;
DFhackCExport void * DFH_SDL_GetVideoSurface(void)
{
return SDL_GetVideoSurface();
}
static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0;
DFhackCExport int DFH_SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect)
{
if ( dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 )
{
DFHack::Core & c = DFHack::Core::getInstance();
DFHack::Graphic* g = c.getGraphic();
DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h);
if ( ov != NULL )
{
if ( ov->paintOver )
{
SDL_UpperBlit(src, srcrect, dst, dstrect);
}
DFHack::DFSDL_Rect* dstrect2 = new DFHack::DFSDL_Rect;
dstrect2->x = dstrect->x;
dstrect2->y = dstrect->y;
dstrect2->w = dstrect->w;
dstrect2->h = dstrect->h;
if ( ov->dstResize != NULL )
{
DFHack::DFSDL_Rect* r = (DFHack::DFSDL_Rect*)ov->dstResize;
dstrect2->x += r->x;
dstrect2->y += r->y;
dstrect2->w += r->w;
dstrect2->h += r->h;
}
int result = SDL_UpperBlit(ov->surface, ov->rect, dst, dstrect2);
delete dstrect2;
return result;
}
}
return SDL_UpperBlit(src, srcrect, dst, dstrect);
}

@ -52,6 +52,7 @@ distribution.
#include "modules/Buildings.h"
#include "modules/Constructions.h"
#include "modules/Random.h"
#include "modules/Filesystem.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
@ -65,7 +66,7 @@ distribution.
#include "df/item.h"
#include "df/material.h"
#include "df/viewscreen.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/nemesis_record.h"
#include "df/historical_figure.h"
#include "df/historical_entity.h"
@ -1338,6 +1339,7 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,isSuitableItem),
WRAPM(Job,isSuitableMaterial),
WRAPM(Job,getName),
WRAPM(Job,linkIntoWorld),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
@ -1464,6 +1466,20 @@ static df::proj_itemst *items_makeProjectile(df::item *item)
return Items::makeProjectile(mc, item);
}
static int16_t items_findType(std::string token)
{
DFHack::ItemTypeInfo result;
result.find(token);
return result.type;
}
static int32_t items_findSubtype(std::string token)
{
DFHack::ItemTypeInfo result;
result.find(token);
return result.subtype;
}
static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getGeneralRef),
WRAPM(Items, getSpecificRef),
@ -1478,12 +1494,15 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getSubtypeDef),
WRAPM(Items, getItemBaseValue),
WRAPM(Items, getValue),
WRAPM(Items, createItem),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToBuilding, items_moveToBuilding),
WRAPN(moveToInventory, items_moveToInventory),
WRAPN(makeProjectile, items_makeProjectile),
WRAPN(remove, items_remove),
WRAPN(findType, items_findType),
WRAPN(findSubtype, items_findSubtype),
{ NULL, NULL }
};
@ -1725,11 +1744,20 @@ int buildings_setSize(lua_State *state)
}
static int buildings_getStockpileContents(lua_State *state)
{
std::vector<df::item*> pvec;
Buildings::getStockpileContents(Lua::CheckDFObject<df::building_stockpilest>(state,1),&pvec);
Lua::PushVector(state, pvec);
return 1;
}
static const luaL_Reg dfhack_buildings_funcs[] = {
{ "findAtTile", buildings_findAtTile },
{ "findCivzonesAt", buildings_findCivzonesAt },
{ "getCorrectSize", buildings_getCorrectSize },
{ "setSize", &Lua::CallWithCatchWrapper<buildings_setSize> },
{ "getStockpileContents", buildings_getStockpileContents},
{ NULL, NULL }
};
@ -1906,6 +1934,28 @@ static int screen_doSimulateInput(lua_State *L)
return 0;
}
static int screen_keyToChar(lua_State *L)
{
auto keycode = (df::interface_key)luaL_checkint(L, 1);
int charcode = Screen::keyToChar(keycode);
if (charcode >= 0)
lua_pushinteger(L, charcode);
else
lua_pushnil(L);
return 1;
}
static int screen_charToKey(lua_State *L)
{
auto charcode = (char)luaL_checkint(L, 1);
df::interface_key keycode = Screen::charToKey(charcode);
if (keycode)
lua_pushinteger(L, keycode);
else
lua_pushnil(L);
return 1;
}
}
static const luaL_Reg dfhack_screen_funcs[] = {
@ -1920,9 +1970,25 @@ static const luaL_Reg dfhack_screen_funcs[] = {
{ "dismiss", screen_dismiss },
{ "isDismissed", screen_isDismissed },
{ "_doSimulateInput", screen_doSimulateInput },
{ "keyToChar", screen_keyToChar },
{ "charToKey", screen_charToKey },
{ 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 *****/
static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
@ -2227,6 +2293,64 @@ static int internal_getDir(lua_State *L)
}
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[] = {
{ "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress },
@ -2240,6 +2364,7 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "memscan", internal_memscan },
{ "diffscan", internal_diffscan },
{ "getDir", internal_getDir },
{ "runCommand", internal_runCommand },
{ NULL, NULL }
};
@ -2265,5 +2390,6 @@ void OpenDFHackApi(lua_State *state)
OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs);
OpenModule(state, "constructions", dfhack_constructions_module);
OpenModule(state, "screen", dfhack_screen_module, dfhack_screen_funcs);
OpenModule(state, "filesystem", dfhack_filesystem_module);
OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs);
}

@ -225,7 +225,7 @@ static int lua_dfhack_print(lua_State *S)
{
std::string str = lua_print_fmt(S);
if (color_ostream *out = Lua::GetOutput(S))
*out << str;
out->print("%s", str.c_str());//*out << str;
else
Core::print("%s", str.c_str());
return 0;

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

@ -696,7 +696,11 @@ void PluginManager::init(Core * core)
{
#ifdef LINUX_BUILD
string path = core->getHackPath() + "plugins/";
#ifdef _DARWIN
const string searchstr = ".plug.dylib";
#else
const string searchstr = ".plug.so";
#endif
#else
string path = core->getHackPath() + "plugins\\";
const string searchstr = ".plug.dll";

@ -222,7 +222,7 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
uintptr_t Process::getBase()
{
return 0x1000000;
return 0x1000;
}
int Process::adjustOffset(int offset, bool /*to_file*/)

@ -76,7 +76,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/historical_entity.h"
#include "df/squad.h"
#include "df/squad_position.h"
#include "df/death_info.h"
#include "df/incident.h"
#include "BasicApi.pb.h"
@ -283,7 +283,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (unit->counters.death_id >= 0)
{
info->set_death_id(unit->counters.death_id);
if (auto death = df::death_info::find(unit->counters.death_id))
if (auto death = df::incident::find(unit->counters.death_id))
info->set_death_flags(death->flags.whole);
}
@ -455,7 +455,7 @@ static command_result ListEnums(color_ostream &stream,
BITFIELD(cie_add_tag_mask1);
BITFIELD(cie_add_tag_mask2);
describe_bitfield<df::death_info::T_flags>(out->mutable_death_info_flags());
describe_bitfield<df::incident::T_flags>(out->mutable_death_info_flags());
ENUM(profession);

@ -109,7 +109,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
{
mem->setOS(OS_APPLE);
// this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x1000000);
mem->setBase(0x1000);
}
else
{

@ -64,6 +64,8 @@ namespace DFHack
}
bool save (const char * filename)
{
if (!history.size())
return true;
std::ofstream outfile (filename);
//fprintf(stderr,"Save: Initialized stream\n");
if(outfile.bad())

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

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

@ -33,6 +33,8 @@ distribution.
#include <string>
#include <stdint.h>
#include "modules/Graphic.h"
// function and variable pointer... we don't try to understand what SDL does here
typedef void * fPtr;
typedef void * vPtr;
@ -49,12 +51,38 @@ DFhackCExport int DFH_SDL_NumJoysticks(void);
DFhackCExport void DFH_SDL_Quit(void);
DFhackCExport int DFH_SDL_PollEvent(SDL::Event* event);
DFhackCExport int DFH_SDL_Init(uint32_t flags);
DFhackCExport uint8_t DFH_SDL_GetMouseState(int *x, int *y);
DFhackCExport void * DFH_SDL_GetVideoSurface(void);
DFhackCExport int DFH_SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect);
DFhackCExport vPtr DFH_SDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask);
DFhackCExport vPtr DFH_SDL_CreateRGBSurfaceFrom(vPtr pixels, int width, int height, int depth, int pitch,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask);
DFhackCExport void DFH_SDL_FreeSurface(vPtr surface);
DFhackCExport vPtr DFH_SDL_ConvertSurface(vPtr surface, vPtr format, uint32_t flags);
DFhackCExport int DFH_SDL_LockSurface(vPtr surface);
DFhackCExport void DFH_SDL_UnlockSurface(vPtr surface);
DFhackCExport uint8_t DFH_SDL_GetMouseState(int *x, int *y);
DFhackCExport void * DFH_SDL_GetVideoSurface(void);
#endif
DFhackCExport int SDL_NumJoysticks(void);
DFhackCExport void SDL_Quit(void);
DFhackCExport int SDL_PollEvent(SDL::Event* event);
DFhackCExport int SDL_Init(uint32_t flags);
DFhackCExport int wgetch(WINDOW * win);
DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y);
DFhackCExport void * SDL_GetVideoSurface(void);
DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect);
DFhackCExport vPtr SDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask);
DFhackCExport vPtr SDL_CreateRGBSurfaceFrom(vPtr pixels, int width, int height, int depth, int pitch,
uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask);
DFhackCExport void SDL_FreeSurface(vPtr surface);
DFhackCExport vPtr SDL_ConvertSurface(vPtr surface, vPtr format, uint32_t flags);
DFhackCExport int SDL_LockSurface(vPtr surface);
DFhackCExport void SDL_UnlockSurface(vPtr surface);
DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y);
DFhackCExport void * SDL_GetVideoSurface(void);
// hook - called early from DF's main()
DFhackCExport int egg_init(void);

@ -38,6 +38,19 @@ namespace DFHack
{
int16_t type;
int32_t index;
bool operator<(const t_matglossPair &b) const
{
if (type != b.type) return (type < b.type);
return (index < b.index);
}
bool operator==(const t_matglossPair &b) const
{
return (type == b.type) && (index == b.index);
}
bool operator!=(const t_matglossPair &b) const
{
return (type != b.type) || (index != b.index);
}
};
template <int SIZE>

@ -42,12 +42,16 @@ namespace DFHack
struct my_hack : df::someclass {
typedef df::someclass interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, foo, (int arg)) {
// You may define additional methods here, but NOT non-static fields
DEFINE_VMETHOD_INTERPOSE(int, foo, (int arg)) {
// If needed by the code, claim the suspend lock.
// DO NOT USE THE USUAL CoreSuspender, OR IT WILL DEADLOCK!
// CoreSuspendClaimer suspend;
...
INTERPOSE_NEXT(foo)(arg) // call the original
... this->field ... // access fields of the df::someclass object
...
int orig_retval = INTERPOSE_NEXT(foo)(arg); // call the original method
...
}
};

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

@ -27,14 +27,18 @@ distribution.
#include "DataDefs.h"
#include "Types.h"
#include "df/building.h"
#include "df/building_stockpilest.h"
#include "df/building_type.h"
#include "df/civzone_type.h"
#include "df/furnace_type.h"
#include "df/item.h"
#include "df/workshop_type.h"
#include "df/construction_type.h"
#include "df/shop_type.h"
#include "df/siegeengine_type.h"
#include "df/trap_type.h"
#include "modules/Items.h"
#include "modules/Maps.h"
namespace df
{
@ -186,5 +190,104 @@ DFHACK_EXPORT bool deconstruct(df::building *bld);
void updateBuildings(color_ostream& out, void* ptr);
void clearBuildings(color_ostream& out);
/**
* Iterates over the items stored on a stockpile.
* (For stockpiles with containers, yields the containers, not their contents.)
*
* Usage:
*
* Buildings::StockpileIterator stored;
* for (stored.begin(stockpile); !stored.done(); ++stored) {
* df::item *item = *stored;
* }
*
* Implementation detail: Uses tile blocks for speed.
* For each tile block that contains at least part of the stockpile,
* starting at the top left and moving right, row by row,
* the block's items are checked for anything on the ground within that stockpile.
*/
class DFHACK_EXPORT StockpileIterator : public std::iterator<std::input_iterator_tag, df::item>
{
df::building_stockpilest* stockpile;
df::map_block* block;
size_t current;
df::item *item;
public:
StockpileIterator() {
stockpile = NULL;
block = NULL;
item = NULL;
}
StockpileIterator& operator++() {
while (stockpile) {
if (block) {
// Check the next item in the current block.
++current;
} else {
// Start with the top-left block covering the stockpile.
block = Maps::getTileBlock(stockpile->x1, stockpile->y1, stockpile->z);
current = 0;
}
while (current >= block->items.size()) {
// Out of items in this block; find the next block to search.
if (block->map_pos.x + 16 < stockpile->x2) {
block = Maps::getTileBlock(block->map_pos.x + 16, block->map_pos.y, stockpile->z);
current = 0;
} else if (block->map_pos.y + 16 < stockpile->y2) {
block = Maps::getTileBlock(stockpile->x1, block->map_pos.y + 16, stockpile->z);
current = 0;
} else {
// All items in all blocks have been checked.
block = NULL;
item = NULL;
return *this;
}
}
// If the current item isn't properly stored, move on to the next.
item = df::item::find(block->items[current]);
if (!item->flags.bits.on_ground) {
continue;
}
if (!Buildings::containsTile(stockpile, item->pos, false)) {
continue;
}
// Ignore empty bins, barrels, and wheelbarrows assigned here.
if (item->isAssignedToThisStockpile(stockpile->id)) {
auto ref = Items::getGeneralRef(item, df::general_ref_type::CONTAINS_ITEM);
if (!ref) continue;
}
// Found a valid item; yield it.
break;
}
return *this;
}
void begin(df::building_stockpilest* sp) {
stockpile = sp;
operator++();
}
df::item* operator*() {
return item;
}
bool done() {
return block == NULL;
}
};
/**
* Collects items stored on a stockpile into a vector.
*/
DFHACK_EXPORT void getStockpileContents(df::building_stockpilest *stockpile, std::vector<df::item*> *items);
}
}

@ -9,8 +9,10 @@
#include "Console.h"
#include "DataDefs.h"
#include <df/coord.h>
#include <df/unit_inventory_item.h>
#include "df/coord.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/unit_wound.h"
namespace DFHack {
namespace EventManager {
@ -26,6 +28,10 @@ namespace DFHack {
SYNDROME,
INVASION,
INVENTORY_CHANGE,
REPORT,
UNIT_ATTACK,
UNLOAD,
INTERACTION,
EVENT_MAX
};
}
@ -69,6 +75,21 @@ namespace DFHack {
InventoryChangeData(int32_t id_in, InventoryItem* old_in, InventoryItem* new_in): unitId(id_in), item_old(old_in), item_new(new_in) {}
};
struct UnitAttackData {
int32_t attacker;
int32_t defender;
int32_t wound;
};
struct InteractionData {
std::string attackVerb;
std::string defendVerb;
int32_t attacker;
int32_t defender;
int32_t attackReport;
int32_t defendReport;
};
DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin);
DFHACK_EXPORT int32_t registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false);
DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin);

@ -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);
}
}

@ -86,6 +86,7 @@ namespace DFHack
// A unit is selected via 'v', 'k', unitjobs, or
// a full-screen item view of a cage or suchlike
DFHACK_EXPORT bool any_unit_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::unit *getAnyUnit(df::viewscreen *top);
DFHACK_EXPORT df::unit *getSelectedUnit(color_ostream &out, bool quiet = false);
// An item is selected via 'v'->inventory, 'k', 't', or
@ -93,10 +94,12 @@ namespace DFHack
// last case, the highlighted contained item is returned, not
// the container itself.
DFHACK_EXPORT bool any_item_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::item *getAnyItem(df::viewscreen *top);
DFHACK_EXPORT df::item *getSelectedItem(color_ostream &out, bool quiet = false);
// A building is selected via 'q', 't' or 'i' (civzone)
DFHACK_EXPORT bool any_building_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::building *getAnyBuilding(df::viewscreen *top);
DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false);
// Low-level API that gives full control over announcements and reports

@ -179,5 +179,8 @@ DFHACK_EXPORT int getItemBaseValue(int16_t item_type, int16_t item_subtype, int1
/// Gets the value of a specific item, ignoring civ values and trade agreements
DFHACK_EXPORT int getValue(df::item *item);
DFHACK_EXPORT int32_t createItem(df::item_type type, int16_t item_subtype, int16_t mat_type, int32_t mat_index, df::unit* creator);
}
}

@ -31,6 +31,7 @@ distribution.
#include <stdint.h>
#include <cstring>
#include "df/map_block.h"
#include "df/map_block_column.h"
#include "df/tile_bitmask.h"
#include "df/block_square_event_mineralst.h"
#include "df/construction.h"
@ -71,6 +72,7 @@ class BlockInfo
Block *mblock;
MapCache *parent;
df::map_block *block;
df::map_block_column *column; //for plants
public:
enum GroundType {

@ -45,6 +45,8 @@ distribution.
#include "df/block_square_event_world_constructionst.h"
#include "df/block_square_event_material_spatterst.h"
#include "df/block_square_event_grassst.h"
#include "df/block_square_event_spoorst.h"
#include "df/block_square_event_item_spatterst.h"
#include "df/tile_liquid.h"
#include "df/tile_dig_designation.h"
#include "df/tile_traffic.h"
@ -257,6 +259,8 @@ extern DFHACK_EXPORT df::map_block * getBlock (int32_t blockx, int32_t blocky, i
extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::map_block * ensureTileBlock (int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::map_block_column * getBlockColumn(int32_t blockx, int32_t blocky);
inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); }
inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); }
inline df::map_block * ensureTileBlock (df::coord pos) { return ensureTileBlock(pos.x, pos.y, pos.z); }
@ -299,9 +303,11 @@ DFHACK_EXPORT df::flow_info *spawnFlow(df::coord pos, df::flow_type type, int ma
extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block,
std::vector<df::block_square_event_mineralst *>* veins,
std::vector<df::block_square_event_frozen_liquidst *>* ices = 0,
std::vector<df::block_square_event_material_spatterst *>* splatter = 0,
std::vector<df::block_square_event_material_spatterst *>* materials = 0,
std::vector<df::block_square_event_grassst *>* grass = 0,
std::vector<df::block_square_event_world_constructionst *>* constructions = 0
std::vector<df::block_square_event_world_constructionst *>* constructions = 0,
std::vector<df::block_square_event_spoorst *>* spoors = 0,
std::vector<df::block_square_event_item_spatterst *>* items = 0
);
/// remove a block event from the block by address

@ -195,6 +195,11 @@ namespace DFHack
/// Retrieve the string representation of the bound key.
DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key);
/// Return the character represented by this key, or -1
DFHACK_EXPORT int keyToChar(df::interface_key key);
/// Return the key code matching this character, or NONE
DFHACK_EXPORT df::interface_key charToKey(char code);
/// A painter class that implements a clipping area and cursor/pen state
struct DFHACK_EXPORT Painter : ViewRect {
df::coord2d gcursor;

@ -41,7 +41,7 @@ namespace df
{
struct nemesis_record;
struct burrow;
struct assumed_identity;
struct identity;
struct historical_entity;
struct entity_position_assignment;
struct entity_position;
@ -213,7 +213,7 @@ DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit);
DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit);
DFHACK_EXPORT df::identity *getIdentity(df::unit *unit);
DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isHidingCurse(df::unit *unit);
@ -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 getCasteProfessionColor(int race, int caste, df::profession pid);
DFHACK_EXPORT std::string getSquadName(df::unit *unit);
}
}
#endif

@ -37,6 +37,7 @@ local function load_patch(name)
end
end
file:close()
return { name = name, old_bytes = old_bytes, new_bytes = new_bytes }
end

@ -10,6 +10,14 @@ local dfhack = dfhack
local base_env = dfhack.BASE_G
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
COLOR_RESET = -1
@ -75,7 +83,27 @@ dfhack.exception.__index = dfhack.exception
-- Module loading
local function find_required_module_arg()
-- require -> module code -> mkmodule -> find_...
if debug.getinfo(4,'f').func == require then
return debug.getlocal(4, 1)
end
-- reload -> dofile -> module code -> mkmodule -> find_...
if debug.getinfo(5,'f').func == reload then
return debug.getlocal(5, 1)
end
end
function mkmodule(module,env)
-- Verify that the module name is correct
local _, rq_modname = find_required_module_arg()
if not rq_modname then
error('The mkmodule function must be used at the start of a module')
end
if rq_modname ~= module then
error('Found module '..module..' during require '..rq_modname)
end
-- Reuse the already loaded module table
local pkg = package.loaded[module]
if pkg == nil then
pkg = {}
@ -84,6 +112,7 @@ function mkmodule(module,env)
error("Not a table in package.loaded["..module.."]")
end
end
-- Inject the plugin-exported functions when appropriate
local plugname = string.match(module,'^plugins%.([%w%-]+)$')
if plugname then
dfhack.open_plugin(pkg,plugname)
@ -138,6 +167,15 @@ function printall(table)
end
end
function printall_ipairs(table)
local ok,f,t,k = pcall(ipairs,table)
if ok then
for k,v in f,t,k do
print(string.format("%-23s\t = %s",tostring(k),tostring(v)))
end
end
end
function copyall(table)
local rv = {}
for k,v in pairs(table) do rv[k] = v end
@ -256,7 +294,9 @@ function dfhack.interpreter(prompt,hfile,env)
print("Shortcuts:\n"..
" '= foo' => '_1,_2,... = foo'\n"..
" '! foo' => 'print(foo)'\n"..
"Both save the first result as '_'.")
" '~ foo' => 'printall(foo)'\n"..
" '@ foo' => 'printall_ipairs(foo)'\n"..
"All of these save the first result as '_'.")
print_banner = false
end
@ -275,6 +315,10 @@ function dfhack.interpreter(prompt,hfile,env)
print(table.unpack(data,2,data.n))
printall(data[2])
end,
['@'] = function(data)
print(table.unpack(data,2,data.n))
printall_ipairs(data[2])
end,
['='] = function(data)
for i=2,data.n do
local varname = '_'..vcnt
@ -341,20 +385,81 @@ internal.scripts = internal.scripts or {}
local scripts = internal.scripts
local hack_path = dfhack.getHackPath()
function dfhack.run_script(name,...)
local key = string.lower(name)
local function findScript(name)
local file = hack_path..'scripts/'..name..'.lua'
local env = scripts[key]
if dfhack.filesystem.exists(file) then
return file
end
file = dfhack.getSavePath()
if file then
file = file .. '/raw/scripts/' .. name .. '.lua'
if dfhack.filesystem.exists(file) then
return file
end
end
file = hack_path..'../raw/scripts/' .. name .. '.lua'
if dfhack.filesystem.exists(file) then
return file
end
return nil
end
function dfhack.run_script(name,...)
local file = findScript(name)
if not file then
error('Could not find script ' .. name)
end
local env = scripts[file]
if env == nil then
env = {}
setmetatable(env, { __index = base_env })
end
local f,perr = loadfile(file, 't', env)
if f == nil then
error(perr)
if f then
scripts[file] = env
return f(...)
end
error(perr)
end
local function _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
return internal.runCommand(command)
end
function dfhack.run_command_silent(...)
local result = _run_command(...)
local output = ""
for i, f in pairs(result) do
if type(f) == 'table' then
output = output .. f[2]
end
end
return output, result.status
end
function dfhack.run_command(...)
local output, status = _run_command(...)
for i, fragment in pairs(output) do
if type(fragment) == 'table' then
dfhack.color(fragment[1])
dfhack.print(fragment[2])
end
end
scripts[key] = env
return f(...)
dfhack.color(COLOR_RESET)
end
-- Per-save init file

@ -383,7 +383,7 @@ MenuOverlay = defclass(MenuOverlay, DwarfOverlay)
MenuOverlay.ATTRS {
frame_inset = 0,
frame_background = CLEAR_PEN,
frame_background = gui.CLEAR_PEN,
}
function MenuOverlay:computeFrame(parent_rect)
@ -417,5 +417,35 @@ function MenuOverlay:render(dc)
MenuOverlay.super.render(self, dc)
end
end
--fakes a "real" workshop sidebar menu, but on exactly selected workshop
WorkshopOverlay = defclass(WorkshopOverlay, MenuOverlay)
WorkshopOverlay.ATTRS={
workshop=DEFAULT_NIL,
}
function WorkshopOverlay:onInput(keys)
local allowedKeys={ --TODO add options: job management, profile, etc...
"CURSOR_RIGHT","CURSOR_LEFT","CURSOR_UP","CURSOR_DOWN",
"CURSOR_UPRIGHT","CURSOR_UPLEFT","CURSOR_DOWNRIGHT","CURSOR_DOWNLEFT",
"CURSOR_UP_Z","CURSOR_DOWN_Z","DESTROYBUILDING","CHANGETAB"}
if keys.LEAVESCREEN then
self:dismiss()
self:sendInputToParent('LEAVESCREEN')
elseif keys.CHANGETAB then
self:sendInputToParent("CHANGETAB")
self:inputToSubviews(keys)
self:updateLayout()
else
for _,name in ipairs(allowedKeys) do
if keys[name] then
self:sendInputToParent(name)
break
end
end
self:inputToSubviews(keys)
end
if df.global.world.selected_building ~= self.workshop then
self:dismiss()
return
end
end
return _ENV

@ -268,7 +268,7 @@ function found_offset(name,val)
if cval then
if cval ~= val then
error(string.format('Mismatch with the current value: %x',val))
error(string.format('Mismatch with the current value: %x != %x',val,cval))
end
else
dfhack.internal.setAddress(name, val)

@ -0,0 +1,230 @@
local _ENV = mkmodule('persist-table')
--[[
persist-table.lua
author expwnent
This module is intended to help facilitate persistent table lookups.
It is a wrapper over dfhack.persistent calls.
It supports tables of arbitrary dimension and shape.
It stores information about each table and subtable's size and children.
For convenience, all stored information is itself persistent.
It would be more efficient to cache stored information in global variables and update it on save/load but this is not yet implemented.
Ask expwnent to try this if performance is bad.
Usage:
local persistTable = require 'persist-table'
persistTable.GlobalTable.doomitude = 'doom!' -- will be stored persistently
print(persistTable.GlobalTable.doomitude) --doom!
persistTable.GlobalTable.doomitude = nil --delete the persistent record
print(persistTable.GlobalTable.doomitude) --nil
persistTable.GlobalTable.mana = {} --allocate a subtable for mana
local mana = persistTable.GlobalTable.mana --setting elements in this table will be persistent too
mana['1'] = '3' --slightly faster than persistTable.GlobalTable.mana['1'] = '3'
--be aware that if you don't change the local variable mana when the game exits and reloads a different save you will run into mysterious problems so don't do that
mana['2'] = '100'
mana.maximum = '1000' --tables can be any arbitrary shape
local globalTable = persistTable.GlobalTable --this is safe too
globalTable.mana = nil --this is safe: it will deallocate all subtables cleanly
globalTable.revengeDesire = {}
--globalTable.revengeDseire.foo = {} -- error: tables must be allocated first with parentTable.subtableName = {}
--you can check if it exists with globalTable.tableName != nil
local revengeTable = globalTable.revengeDesire
revengeTable['2'] = revengeTable['2'] or {} --this is fine too
revengeTable['2']['3'] = 'totally a lot, man'
revengeTable['3'] = {}
revengeTable['3']['2'] = 'maybe a little bit'
revengeTable['2'] = nil
-------------
And so on.
Be careful not to name your tables in a way that will conflict with other scripts! The easiest way is to just put all your tables in one giant table named based on your script.
All stored values MUST be strings. Numbers can be supported later but are not yet supported. Keep in mind there is a significant overhead for each table element so don't go totally crazy storing massive amounts of information or the game will run out of RAM.
--table._children returns a list of child keys
for _,childKey in ipairs(table._children) do
local child = table[childKey]
--blah
end
--]]
local prefix = 'persist-table'
local function ensure(name)
return dfhack.persistent.save({key=name})
end
local function gensym()
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
local available = nil
local smallest = nil
for _,candidate in pairs(availables) do
--TODO: it would be great if we could iterate over these in order but we can't
local score = tonumber(candidate.value)
--print('gensym', candidate, score, available, smallest)
if (score and (not available or score < smallest)) then
smallest = score
available = candidate
end
end
if available then
local value = available.value
available:delete()
--print('gensym: allocate ' .. value)
return value
end
--none explicitly available, so smallest unused is the next available number
local smallestUnused = ensure(prefix .. '$smallest_unused')
if smallestUnused.value == '' then
smallestUnused.value = '0'
end
local result = smallestUnused.value
smallestUnused.value = tostring(1+tonumber(result))
smallestUnused:save()
--print('gensym: allocate ' .. result)
return result
end
local function releasesym(symb)
local availables = dfhack.persistent.get_all(prefix .. '$available') or {}
for _,available in pairs(availables) do
--print('releasesym: ', symb, available.value, available)
if available.value == symb then
print('error: persist-table.releasesym(' .. symb .. '): available.value = ' .. available.value)
return
end
end
dfhack.persistent.save({key=prefix .. '$available', value=symb}, true)
--print('releasesym: unallocate ' .. symb)
end
local intCount = 7
local existIndex = intCount-0
local existValue = 1
local pointerIndex = intCount-1
local pointerValue = 1
local defaultValue = -1
local function isEmpty(table)
return next(table) == nil
end
local function deletePersistent(name)
if name == '' then
return
end
local children = dfhack.persistent.get_all(prefix .. name) or {}
for _,childKey in ipairs(children) do
local childEntry = ensure(prefix .. name .. '$$' .. childKey.value)
if childEntry.ints[existIndex] == existValue and childEntry.ints[pointerIndex] == pointerValue then
deletePersistent(childEntry.value)
end
childEntry:delete()
childKey:delete()
end
releasesym(name)
end
GlobalTable = GlobalTable or {key = 'mastertable'}
GlobalTable.mt = GlobalTable.mt or {}
GlobalTable.mt.__index = GlobalTable.mt.__index or function(theTable, key)
if key == '_children' then
return rawget(theTable,key)
end
--print(rawget(theTable,'key') .. '[' .. key .. ']')
local entry = ensure(prefix .. rawget(theTable,'key') .. '$$' .. key)
if entry.ints[existIndex] == existValue and entry.ints[pointerIndex] == defaultValue then
--print('string: ' .. entry.value)
return entry.value
end
if entry.ints[pointerIndex] == pointerValue then
--pre-existing pointer
local result = {key = entry.value}
result.mt = rawget(GlobalTable,'mt')
local childArgs = dfhack.persistent.get_all(prefix .. entry.value)
result._children = {}
for _,childArg in ipairs(childArgs or {}) do
--print(result._children, childArg.value)
table.insert(result._children, childArg.value)
end
setmetatable(result,rawget(GlobalTable,'mt'))
--print('theTable: ' .. entry.value)
return result
end
entry:delete()
--print 'theTable[key] does not exist.'
return nil
end
GlobalTable.mt.__newindex = GlobalTable.mt.__newindex or function(theTable, key, value)
--print(rawget(theTable,'key') .. '[' .. key .. '] = ' .. tostring(value))
local entry = ensure(prefix .. rawget(theTable,'key') .. '$$' .. key)
local old = entry.value
local isNew = entry.ints[existIndex] == defaultValue
if entry.ints[existIndex] == existValue and entry.ints[pointerIndex] == pointerValue then
if type(value) == 'table' and rawget(value,'mt') == rawget(GlobalTable,mt) and entry.value == rawget(value,'key') then
--if setting it to the same table it already is, then don't do anything
return
end
deletePersistent(entry.value)
end
if not value then
--print('__newindesx: delete')
--delete
for i,child in ipairs(dfhack.persistent.get_all(prefix .. rawget(theTable,'key')) or {}) do
if child.value == key then
child:delete()
end
end
entry:delete()
return
elseif type(value) == 'string' then
--print('__newindesx: string')
entry.value = value
entry.ints[pointerIndex] = defaultValue
entry.ints[existIndex] = existValue
entry:save()
if isNew then
--print('new child!')
dfhack.persistent.save({key=prefix .. rawget(theTable,'key'), value=key}, true)
end
return
elseif type(value) == 'table' then
--print('__newindesx: table')
if rawget(value,'mt') ~= rawget(GlobalTable,'mt') then
if not isEmpty(value) then
error('setting value to an invalid table')
end
--print('__newindesx: empty table')
--empty table: allocate a thing
entry.ints[pointerIndex] = pointerValue
entry.ints[existIndex] = existValue
entry.value = gensym()
entry:save()
if isNew then
--print('new child!')
dfhack.persistent.save({key=prefix .. rawget(theTable,'key'), value=key}, true)
end
return
end
--print('__newindesx: table assignment')
entry.value = rawget(value,'key')
entry.ints[pointerIndex] = pointerValue
entry.ints[existIndex] = existValue
entry:save()
if isNew then
--print('new child!')
dfhack.persistent.save({key=prefix .. rawget(theTable,'key'), value=key}, true)
end
return
else
error('type(value) = ' .. type(value))
end
end
setmetatable(GlobalTable, GlobalTable.mt)
return _ENV

@ -0,0 +1,42 @@
-- lua/plugins/repeatUtil.lua
-- author expwnent
-- tools for registering callbacks periodically
-- vaguely based on a script by Putnam
local _ENV = mkmodule("repeat-util")
repeating = repeating or {}
dfhack.onStateChange.repeatUtilStateChange = function(code)
if code == SC_WORLD_UNLOADED then
repeating = {}
end
end
function cancel(name)
if not repeating[name] then
return false
end
dfhack.timeout_active(repeating[name],nil)
repeating[name] = nil
return true
end
function scheduleEvery(name,time,timeUnits,func)
cancel(name)
local function helper()
func()
repeating[name] = dfhack.timeout(time,timeUnits,helper)
end
helper()
end
function scheduleUnlessAlreadyScheduled(name,time,timeUnits,func)
if repeating[name] then
return
end
scheduleEvery(name,time,timeUnits,func)
end
return _ENV

@ -0,0 +1,175 @@
--lua/syndrome-util.lua
--author expwnent
--some utilities for adding syndromes to units
local _ENV = mkmodule("syndrome-util")
local utils = require("utils")
function findUnitSyndrome(unit,syn_id)
for index,syndrome in ipairs(unit.syndromes.active) do
if syndrome['type'] == syn_id then
return syndrome
end
end
return nil
end
--usage: syndromeUtil.ResetPolicy.DoNothing, syndromeUtil.ResetPolicy.ResetDuration, etc
ResetPolicy = ResetPolicy or utils.invert({
"DoNothing",
"ResetDuration",
"AddDuration",
"NewInstance"
})
function eraseSyndrome(unit,syndromeId,oldestFirst)
local i1
local iN
local d
if oldestFirst then
i1 = 0
iN = #unit.syndromes.active-1
d = 1
else
i1 = #unit.syndromes.active-1
iN = 0
d = -1
end
local syndromes = unit.syndromes.active
for i=i1,iN,d do
if syndromes[i]['type'] == syndromeId then
syndromes:erase(i)
return true
end
end
return false
end
local function eraseSyndromeClassHelper(unit,synclass)
for i,unitSyndrome in ipairs(unit.syndromes.active) do
local syndrome = df.syndrome.find(unitSyndrome.type)
for _,class in ipairs(syndrome.syn_class) do
if class.value == synclass then
unit.syndromes.active:erase(i)
return true
end
end
end
return false
end
function eraseSyndromeClass(unit,synclass)
local count=0
while eraseSyndromeClassHelper(unit,synclass) do
count = count+1
end
return count
end
function eraseSyndromes(unit,syndromeId)
local count=0
while eraseSyndrome(unit,syndromeId,true) do
count = count+1
end
return count
end
--target is a df.unit, syndrome is a df.syndrome, resetPolicy is one of syndromeUtil.ResetPolicy
--if the target has an instance of the syndrome already, the reset policy takes effect
--returns true if the unit did not have the syndrome before calling and false otherwise
function infectWithSyndrome(target,syndrome,resetPolicy)
local oldSyndrome = findUnitSyndrome(target,syndrome.id)
if oldSyndrome == nil or resetPolicy == nil or resetPolicy == ResetPolicy.NewInstance then
local unitSyndrome = df.unit_syndrome:new()
unitSyndrome.type = syndrome.id
unitSyndrome.year = df.global.cur_year
unitSyndrome.year_time = df.global.cur_year_tick
unitSyndrome.ticks = 0
unitSyndrome.wound_id = -1
for k,v in ipairs(syndrome.ce) do
local symptom = df.unit_syndrome.T_symptoms:new()
symptom.quantity = 0
symptom.delay = 0
symptom.ticks = 0
symptom.flags.active = true
unitSyndrome.symptoms:insert("#",symptom)
end
target.syndromes.active:insert("#",unitSyndrome)
elseif resetPolicy == ResetPolicy.DoNothing then
elseif resetPolicy == ResetPolicy.ResetDuration then
for k,symptom in ipairs(oldSyndrome.symptoms) do
symptom.ticks = 0
end
oldSyndrome.ticks = 0
elseif resetPolicy == ResetPolicy.AddDuration then
for k,symptom in ipairs(oldSyndrome.symptoms) do
--really it's syndrome.ce[k].end, but lua doesn't like that because keywords
if syndrome.ce[k]["end"] ~= -1 then
symptom.ticks = symptom.ticks - syndrome.ce[k]["end"]
end
end
else qerror("Bad reset policy: " .. resetPolicy)
end
return (oldSyndrome == nil)
end
function isValidTarget(unit,syndrome)
--mostly copied from itemsyndrome, which is based on autoSyndrome
if
#syndrome.syn_affected_class==0
and #syndrome.syn_affected_creature==0
and #syndrome.syn_affected_caste==0
and #syndrome.syn_immune_class==0
and #syndrome.syn_immune_creature==0
and #syndrome.syn_immune_caste==0
then
return true
end
local affected = false
local unitRaws = df.creature_raw.find(unit.race)
local casteRaws = unitRaws.caste[unit.caste]
local unitRaceName = unitRaws.creature_id
local unitCasteName = casteRaws.caste_id
local unitClasses = casteRaws.creature_class
for _,unitClass in ipairs(unitClasses) do
for _,syndromeClass in ipairs(syndrome.syn_affected_class) do
if unitClass.value==syndromeClass.value then
affected = true
end
end
end
for caste,creature in ipairs(syndrome.syn_affected_creature) do
local affectedCreature = creature.value
local affectedCaste = syndrome.syn_affected_caste[caste].value
if affectedCreature == unitRaceName and (affectedCaste == unitCasteName or affectedCaste == "ALL") then
affected = true
end
end
for _,unitClass in ipairs(unitClasses) do
for _,syndromeClass in ipairs(syndrome.syn_immune_class) do
if unitClass.value == syndromeClass.value then
affected = false
end
end
end
for caste,creature in ipairs(syndrome.syn_immune_creature) do
local immuneCreature = creature.value
local immuneCaste = syndrome.syn_immune_caste[caste].value
if immuneCreature == unitRaceName and (immuneCaste == unitCasteName or immuneCaste == "ALL") then
affected = false
end
end
return affected
end
function infectWithSyndromeIfValidTarget(target,syndrome,resetPolicy)
if isValidTarget(target,syndrome) then
infectWithSyndrome(target,syndrome,resetPolicy)
return true
else
return false
end
end
return _ENV

@ -540,4 +540,86 @@ function check_number(text)
return nv ~= nil, nv
end
return _ENV
function invert(tab)
local result = {}
for k,v in pairs(tab) do
result[v]=k
end
return result
end
function processArgs(args, validArgs)
--[[
standardized argument processing for scripts
-argName value
-argName [list of values]
-argName [list of [nested values] -that can be [whatever] format of matched square brackets]
-arg1 \-arg3
escape sequences
--]]
local result = {}
local argName
local bracketDepth = 0
for i,arg in ipairs(args) do
if argName then
if arg == '[' then
if bracketDepth > 0 then
table.insert(result[argName], arg)
end
bracketDepth = bracketDepth+1
elseif arg == ']' then
bracketDepth = bracketDepth-1
if bracketDepth > 0 then
table.insert(result[argName], arg)
else
argName = nil
end
elseif string.sub(arg,1,1) == '\\' then
if bracketDepth == 0 then
result[argName] = string.sub(arg,2)
argName = nil
else
table.insert(result[argName], string.sub(arg,2))
end
else
if bracketDepth == 0 then
result[argName] = arg
argName = nil
else
table.insert(result[argName], arg)
end
end
elseif string.sub(arg,1,1) == '-' then
argName = string.sub(arg,2)
if validArgs and not validArgs[argName] then
error('error: invalid arg: ' .. i .. ': ' .. argName)
end
if result[argName] then
error('duplicate arg: ' .. i .. ': ' .. argName)
end
if i+1 > #args or string.sub(args[i+1],1,1) == '-' then
result[argName] = ''
argName = nil
else
result[argName] = {}
end
else
error('error parsing arg ' .. i .. ': ' .. arg)
end
end
return result
end
function fillTable(table1,table2)
for k,v in pairs(table2) do
table1[k] = v
end
end
function unfillTable(table1,table2)
for k,v in pairs(table2) do
table1[k] = nil
end
end
return _ENV

@ -1072,7 +1072,7 @@ bool Buildings::deconstruct(df::building *bld)
// Assume: no parties.
unlinkRooms(bld);
// Assume: not unit destroy target
vector_erase_at(ui->tax_collection.rooms, linear_index(ui->tax_collection.rooms, bld));
vector_erase_at(ui->tax_collection.rooms, linear_index(ui->tax_collection.rooms, bld->id));
// Assume: not used in punishment
// Assume: not used in non-own jobs
// Assume: does not affect pathfinding
@ -1163,3 +1163,16 @@ void Buildings::updateBuildings(color_ostream& out, void* ptr)
corner2.erase(id);
}
}
void Buildings::getStockpileContents(df::building_stockpilest *stockpile, std::vector<df::item*> *items)
{
CHECK_NULL_POINTER(stockpile);
items->clear();
Buildings::StockpileIterator stored;
for (stored.begin(stockpile); !stored.done(); ++stored) {
df::item *item = *stored;
items->push_back(item);
}
}

@ -1,29 +1,43 @@
#include "Core.h"
#include "Console.h"
#include "VTableInterpose.h"
#include "modules/Buildings.h"
#include "modules/Constructions.h"
#include "modules/EventManager.h"
#include "modules/Once.h"
#include "modules/Job.h"
#include "modules/Units.h"
#include "modules/World.h"
#include "df/announcement_type.h"
#include "df/building.h"
#include "df/construction.h"
#include "df/general_ref.h"
#include "df/general_ref_type.h"
#include "df/general_ref_unit_workerst.h"
#include "df/global_objects.h"
#include "df/interaction.h"
#include "df/item.h"
#include "df/item_actual.h"
#include "df/item_constructed.h"
#include "df/item_crafted.h"
#include "df/item_weaponst.h"
#include "df/job.h"
#include "df/job_list_link.h"
#include "df/report.h"
#include "df/ui.h"
#include "df/unit.h"
#include "df/unit_flags1.h"
#include "df/unit_inventory_item.h"
#include "df/unit_report_type.h"
#include "df/unit_syndrome.h"
#include "df/unit_wound.h"
#include "df/world.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
@ -116,6 +130,10 @@ static void manageConstructionEvent(color_ostream& out);
static void manageSyndromeEvent(color_ostream& out);
static void manageInvasionEvent(color_ostream& out);
static void manageEquipmentEvent(color_ostream& out);
static void manageReportEvent(color_ostream& out);
static void manageUnitAttackEvent(color_ostream& out);
static void manageUnloadEvent(color_ostream& out){};
static void manageInteractionEvent(color_ostream& out);
typedef void (*eventManager_t)(color_ostream&);
@ -130,6 +148,10 @@ static const eventManager_t eventManager[] = {
manageSyndromeEvent,
manageInvasionEvent,
manageEquipmentEvent,
manageReportEvent,
manageUnitAttackEvent,
manageUnloadEvent,
manageInteractionEvent,
};
//job initiated
@ -162,6 +184,17 @@ static int32_t nextInvasion;
//static unordered_map<int32_t, vector<df::unit_inventory_item> > equipmentLog;
static unordered_map<int32_t, vector<InventoryItem> > equipmentLog;
//report
static int32_t lastReport;
//unit attack
static int32_t lastReportUnitAttack;
static std::map<int32_t,std::vector<int32_t> > reportToRelevantUnits;
static int32_t reportToRelevantUnitsTime = -1;
//interaction
static int32_t lastReportInteraction;
void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) {
static bool doOnce = false;
// const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"};
@ -186,7 +219,14 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
equipmentLog.clear();
Buildings::clearBuildings(out);
lastReport = -1;
lastReportUnitAttack = -1;
gameLoaded = false;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNLOAD].begin(), handlers[EventType::UNLOAD].end());
for (auto a = copy.begin(); a != copy.end(); a++ ) {
(*a).second.eventHandler(out, NULL);
}
} else if ( event == DFHack::SC_MAP_LOADED ) {
/*
int32_t tick = df::global::world->frame_counter;
@ -198,6 +238,16 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
//out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick);
*/
//tickQueue.clear();
if (!df::global::item_next_id)
return;
if (!df::global::building_next_id)
return;
if (!df::global::job_next_id)
return;
if (!df::global::ui)
return;
if (!df::global::world)
return;
nextItem = *df::global::item_next_id;
nextBuilding = *df::global::building_next_id;
@ -235,6 +285,14 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
lastSyndromeTime = startTime;
}
}
lastReport = -1;
if ( df::global::world->status.reports.size() > 0 ) {
lastReport = df::global::world->status.reports[df::global::world->status.reports.size()-1]->id;
}
lastReportUnitAttack = -1;
lastReportInteraction = -1;
reportToRelevantUnitsTime = -1;
reportToRelevantUnits.clear();
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
eventLastTick[a] = -1;//-1000000;
}
@ -247,6 +305,9 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
if ( !gameLoaded ) {
return;
}
if (!df::global::world)
return;
CoreSuspender suspender;
int32_t tick = df::global::world->frame_counter;
@ -263,7 +324,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
}
else eventFrequency = 1;
if ( tick - eventLastTick[a] < eventFrequency )
if ( tick >= eventLastTick[a] && tick - eventLastTick[a] < eventFrequency )
continue;
eventManager[a](out);
@ -272,6 +333,8 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
}
static void manageTickEvent(color_ostream& out) {
if (!df::global::world)
return;
unordered_set<EventHandler> toRemove;
int32_t tick = df::global::world->frame_counter;
while ( !tickQueue.empty() ) {
@ -298,6 +361,10 @@ static void manageTickEvent(color_ostream& out) {
}
static void manageJobInitiatedEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::job_next_id)
return;
if ( lastJobId == -1 ) {
lastJobId = *df::global::job_next_id - 1;
return;
@ -331,6 +398,8 @@ static int32_t getWorkerID(df::job* job) {
TODO: consider checking item creation / experience gain just in case
*/
static void manageJobCompletedEvent(color_ostream& out) {
if (!df::global::world)
return;
int32_t tick0 = eventLastTick[EventType::JOB_COMPLETED];
int32_t tick1 = df::global::world->frame_counter;
@ -456,6 +525,8 @@ static void manageJobCompletedEvent(color_ostream& out) {
}
static void manageUnitDeathEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end());
for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) {
df::unit* unit = df::global::world->units.all[a];
@ -476,6 +547,10 @@ static void manageUnitDeathEvent(color_ostream& out) {
}
static void manageItemCreationEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::item_next_id)
return;
if ( nextItem >= *df::global::item_next_id ) {
return;
}
@ -508,6 +583,10 @@ static void manageItemCreationEvent(color_ostream& out) {
}
static void manageBuildingEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::building_next_id)
return;
/*
* TODO: could be faster
* consider looking at jobs: building creation / destruction
@ -547,6 +626,8 @@ static void manageBuildingEvent(color_ostream& out) {
}
static void manageConstructionEvent(color_ostream& out) {
if (!df::global::world)
return;
//unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end());
multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end());
@ -582,6 +663,8 @@ static void manageConstructionEvent(color_ostream& out) {
}
static void manageSyndromeEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end());
int32_t highestTime = -1;
for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) {
@ -609,6 +692,8 @@ static void manageSyndromeEvent(color_ostream& out) {
}
static void manageInvasionEvent(color_ostream& out) {
if (!df::global::ui)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end());
if ( df::global::ui->invasions.next_id <= nextInvasion )
@ -617,11 +702,13 @@ static void manageInvasionEvent(color_ostream& out) {
for ( auto a = copy.begin(); a != copy.end(); a++ ) {
EventHandler handle = (*a).second;
handle.eventHandler(out, (void*)nextInvasion);
handle.eventHandler(out, (void*)(nextInvasion-1));
}
}
static void manageEquipmentEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end());
unordered_map<int32_t, InventoryItem> itemIdToInventoryItem;
@ -635,51 +722,59 @@ static void manageEquipmentEvent(color_ostream& out) {
*/
auto oldEquipment = equipmentLog.find(unit->id);
if ( oldEquipment != equipmentLog.end() ) {
vector<InventoryItem>& v = (*oldEquipment).second;
for ( auto b = v.begin(); b != v.end(); b++ ) {
InventoryItem& i = *b;
itemIdToInventoryItem[i.itemId] = i;
}
for ( size_t b = 0; b < unit->inventory.size(); b++ ) {
df::unit_inventory_item* dfitem_new = unit->inventory[b];
currentlyEquipped.insert(dfitem_new->item->id);
InventoryItem item_new(dfitem_new->item->id, *dfitem_new);
auto c = itemIdToInventoryItem.find(dfitem_new->item->id);
if ( c == itemIdToInventoryItem.end() ) {
//new item equipped (probably just picked up)
InventoryChangeData data(unit->id, NULL, &item_new);
for ( auto h = copy.begin(); h != copy.end(); h++ ) {
EventHandler handle = (*h).second;
handle.eventHandler(out, (void*)&data);
}
continue;
}
InventoryItem item_old = (*c).second;
df::unit_inventory_item& item0 = item_old.item;
df::unit_inventory_item& item1 = item_new.item;
if ( item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && item0.wound_id == item1.wound_id )
continue;
//some sort of change in how it's equipped
InventoryChangeData data(unit->id, &item_old, &item_new);
bool hadEquipment = oldEquipment != equipmentLog.end();
vector<InventoryItem>* temp;
if ( hadEquipment ) {
temp = &((*oldEquipment).second);
} else {
temp = new vector<InventoryItem>;
}
//vector<InventoryItem>& v = (*oldEquipment).second;
vector<InventoryItem>& v = *temp;
for ( auto b = v.begin(); b != v.end(); b++ ) {
InventoryItem& i = *b;
itemIdToInventoryItem[i.itemId] = i;
}
for ( size_t b = 0; b < unit->inventory.size(); b++ ) {
df::unit_inventory_item* dfitem_new = unit->inventory[b];
currentlyEquipped.insert(dfitem_new->item->id);
InventoryItem item_new(dfitem_new->item->id, *dfitem_new);
auto c = itemIdToInventoryItem.find(dfitem_new->item->id);
if ( c == itemIdToInventoryItem.end() ) {
//new item equipped (probably just picked up)
InventoryChangeData data(unit->id, NULL, &item_new);
for ( auto h = copy.begin(); h != copy.end(); h++ ) {
EventHandler handle = (*h).second;
handle.eventHandler(out, (void*)&data);
}
continue;
}
//check for dropped items
for ( auto b = v.begin(); b != v.end(); b++ ) {
InventoryItem i = *b;
if ( currentlyEquipped.find(i.itemId) != currentlyEquipped.end() )
continue;
//TODO: delete ptr if invalid
InventoryChangeData data(unit->id, &i, NULL);
for ( auto h = copy.begin(); h != copy.end(); h++ ) {
EventHandler handle = (*h).second;
handle.eventHandler(out, (void*)&data);
}
InventoryItem item_old = (*c).second;
df::unit_inventory_item& item0 = item_old.item;
df::unit_inventory_item& item1 = item_new.item;
if ( item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && item0.wound_id == item1.wound_id )
continue;
//some sort of change in how it's equipped
InventoryChangeData data(unit->id, &item_old, &item_new);
for ( auto h = copy.begin(); h != copy.end(); h++ ) {
EventHandler handle = (*h).second;
handle.eventHandler(out, (void*)&data);
}
}
if ( !hadEquipment )
delete temp;
//check for dropped items
for ( auto b = v.begin(); b != v.end(); b++ ) {
InventoryItem i = *b;
if ( currentlyEquipped.find(i.itemId) != currentlyEquipped.end() )
continue;
//TODO: delete ptr if invalid
InventoryChangeData data(unit->id, &i, NULL);
for ( auto h = copy.begin(); h != copy.end(); h++ ) {
EventHandler handle = (*h).second;
handle.eventHandler(out, (void*)&data);
}
}
@ -694,3 +789,419 @@ static void manageEquipmentEvent(color_ostream& out) {
}
}
static void updateReportToRelevantUnits() {
if (!df::global::world)
return;
if ( df::global::world->frame_counter <= reportToRelevantUnitsTime )
return;
reportToRelevantUnitsTime = df::global::world->frame_counter;
for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) {
df::unit* unit = df::global::world->units.all[a];
for ( int16_t b = df::enum_traits<df::unit_report_type>::first_item_value; b <= df::enum_traits<df::unit_report_type>::last_item_value; b++ ) {
if ( b == df::unit_report_type::Sparring )
continue;
for ( size_t c = 0; c < unit->reports.log[b].size(); c++ ) {
int32_t report = unit->reports.log[b][c];
if ( std::find(reportToRelevantUnits[report].begin(), reportToRelevantUnits[report].end(), unit->id) != reportToRelevantUnits[report].end() )
continue;
reportToRelevantUnits[unit->reports.log[b][c]].push_back(unit->id);
}
}
}
}
static void manageReportEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReport, false);
//this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly
while (a < reports.size() && reports[a]->id <= lastReport) {
a++;
}
for ( ; a < reports.size(); a++ ) {
df::report* report = reports[a];
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)report->id);
}
lastReport = report->id;
}
}
static df::unit_wound* getWound(df::unit* attacker, df::unit* defender) {
for ( size_t a = 0; a < defender->body.wounds.size(); a++ ) {
df::unit_wound* wound = defender->body.wounds[a];
if ( wound->age <= 1 && wound->attacker_unit_id == attacker->id ) {
return wound;
}
}
return NULL;
}
static void manageUnitAttackEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false);
//this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly
while (a < reports.size() && reports[a]->id <= lastReportUnitAttack) {
a++;
}
std::set<int32_t> strikeReports;
for ( ; a < reports.size(); a++ ) {
df::report* report = reports[a];
lastReportUnitAttack = report->id;
if ( report->flags.bits.continuation )
continue;
df::announcement_type type = report->type;
if ( type == df::announcement_type::COMBAT_STRIKE_DETAILS ) {
strikeReports.insert(report->id);
}
}
if ( strikeReports.empty() )
return;
updateReportToRelevantUnits();
map<int32_t, map<int32_t, int32_t> > alreadyDone;
for ( auto a = strikeReports.begin(); a != strikeReports.end(); a++ ) {
int32_t reportId = *a;
df::report* report = df::report::find(reportId);
if ( !report )
continue; //TODO: error
std::string reportStr = report->text;
for ( int32_t b = reportId+1; ; b++ ) {
df::report* report2 = df::report::find(b);
if ( !report2 )
break;
if ( report2->type != df::announcement_type::COMBAT_STRIKE_DETAILS )
break;
if ( !report2->flags.bits.continuation )
break;
reportStr = reportStr + report2->text;
}
std::vector<int32_t>& relevantUnits = reportToRelevantUnits[report->id];
if ( relevantUnits.size() != 2 ) {
continue;
}
df::unit* unit1 = df::unit::find(relevantUnits[0]);
df::unit* unit2 = df::unit::find(relevantUnits[1]);
df::unit_wound* wound1 = getWound(unit1,unit2);
df::unit_wound* wound2 = getWound(unit2,unit1);
if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) {
UnitAttackData data;
data.attacker = unit1->id;
data.defender = unit2->id;
data.wound = wound1->id;
alreadyDone[data.attacker][data.defender] = 1;
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)&data);
}
}
if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) {
UnitAttackData data;
data.attacker = unit2->id;
data.defender = unit1->id;
data.wound = wound2->id;
alreadyDone[data.attacker][data.defender] = 1;
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)&data);
}
}
if ( unit1->flags1.bits.dead ) {
UnitAttackData data;
data.attacker = unit2->id;
data.defender = unit1->id;
data.wound = -1;
alreadyDone[data.attacker][data.defender] = 1;
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)&data);
}
}
if ( unit2->flags1.bits.dead ) {
UnitAttackData data;
data.attacker = unit1->id;
data.defender = unit2->id;
data.wound = -1;
alreadyDone[data.attacker][data.defender] = 1;
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)&data);
}
}
if ( !wound1 && !wound2 ) {
//if ( unit1->flags1.bits.dead || unit2->flags1.bits.dead )
// continue;
if ( reportStr.find("severed part") )
continue;
if ( Once::doOnce("EventManager neither wound") ) {
out.print("%s, %d: neither wound: %s\n", __FILE__, __LINE__, reportStr.c_str());
}
}
}
}
static std::string getVerb(df::unit* unit, std::string reportStr) {
std::string result(reportStr);
std::string name = unit->name.first_name + " ";
bool match = strncmp(result.c_str(), name.c_str(), name.length()) == 0;
if ( match ) {
result = result.substr(name.length());
result = result.substr(0,result.length()-1);
return result;
}
//use profession name
name = "The " + Units::getProfessionName(unit) + " ";
match = strncmp(result.c_str(), name.c_str(), name.length()) == 0;
if ( match ) {
result = result.substr(name.length());
result = result.substr(0,result.length()-1);
return result;
}
if ( unit->id != 0 ) {
return "";
}
std::string you = "You ";
match = strncmp(result.c_str(), name.c_str(), name.length()) == 0;
if ( match ) {
result = result.substr(name.length());
result = result.substr(0,result.length()-1);
return result;
}
return "";
}
static InteractionData getAttacker(color_ostream& out, df::report* attackEvent, df::unit* lastAttacker, df::report* defendEvent, vector<df::unit*>& relevantUnits) {
vector<df::unit*> attackers = relevantUnits;
vector<df::unit*> defenders = relevantUnits;
//find valid interactions: TODO
/*map<int32_t,vector<df::interaction*> > validInteractions;
for ( size_t a = 0; a < relevantUnits.size(); a++ ) {
df::unit* unit = relevantUnits[a];
vector<df::interaction*>& interactions = validInteractions[unit->id];
for ( size_t b = 0; b < unit->body.
}*/
//if attackEvent
// attacker must be same location
// attacker name must start attack str
// attack verb must match valid interaction of this attacker
std::string attackVerb;
if ( attackEvent ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
for ( size_t a = 0; a < attackers.size(); a++ ) {
if ( attackers[a]->pos != attackEvent->pos ) {
attackers.erase(attackers.begin()+a);
a--;
continue;
}
if ( lastAttacker && attackers[a] != lastAttacker ) {
attackers.erase(attackers.begin()+a);
a--;
continue;
}
std::string verbC = getVerb(attackers[a], attackEvent->text);
if ( verbC.length() == 0 ) {
attackers.erase(attackers.begin()+a);
a--;
continue;
}
attackVerb = verbC;
}
}
//if defendEvent
// defender must be same location
// defender name must start defend str
// defend verb must match valid interaction of some attacker
std::string defendVerb;
if ( defendEvent ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
for ( size_t a = 0; a < defenders.size(); a++ ) {
if ( defenders[a]->pos != defendEvent->pos ) {
defenders.erase(defenders.begin()+a);
a--;
continue;
}
std::string verbC = getVerb(defenders[a], defendEvent->text);
if ( verbC.length() == 0 ) {
defenders.erase(defenders.begin()+a);
a--;
continue;
}
defendVerb = verbC;
}
}
//keep in mind one attacker zero defenders is perfectly valid for self-cast
if ( attackers.size() == 1 && defenders.size() == 1 && attackers[0] == defenders[0] ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
} else {
if ( defenders.size() == 1 ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
auto a = std::find(attackers.begin(),attackers.end(),defenders[0]);
if ( a != attackers.end() )
attackers.erase(a);
}
if ( attackers.size() == 1 ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
auto a = std::find(defenders.begin(),defenders.end(),attackers[0]);
if ( a != defenders.end() )
defenders.erase(a);
}
}
//if trying attack-defend pair and it fails to find attacker, try defend only
InteractionData result = /*(InteractionData)*/ { std::string(), std::string(), -1, -1, -1, -1 };
if ( attackers.size() > 1 ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
if ( Once::doOnce("EventManager interaction ambiguous attacker") ) {
out.print("%s,%d: ambiguous attacker on report\n \'%s\'\n '%s'\n", __FILE__, __LINE__, attackEvent ? attackEvent->text.c_str() : "", defendEvent ? defendEvent->text.c_str() : "");
}
} else if ( attackers.size() < 1 ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
if ( attackEvent && defendEvent )
return getAttacker(out, NULL, NULL, defendEvent, relevantUnits);
} else {
//out.print("%s,%d\n",__FILE__,__LINE__);
//attackers.size() == 1
result.attacker = attackers[0]->id;
if ( defenders.size() > 0 )
result.defender = defenders[0]->id;
if ( defenders.size() > 1 ) {
if ( Once::doOnce("EventManager interaction ambiguous defender") ) {
out.print("%s,%d: ambiguous defender: shouldn't happen. On report\n \'%s\'\n '%s'\n", __FILE__, __LINE__, attackEvent ? attackEvent->text.c_str() : "", defendEvent ? defendEvent->text.c_str() : "");
}
}
result.attackVerb = attackVerb;
result.defendVerb = defendVerb;
if ( attackEvent )
result.attackReport = attackEvent->id;
if ( defendEvent )
result.defendReport = defendEvent->id;
}
//out.print("%s,%d\n",__FILE__,__LINE__);
return result;
}
static vector<df::unit*> gatherRelevantUnits(color_ostream& out, df::report* r1, df::report* r2) {
vector<df::report*> reports;
if ( r1 == r2 ) r2 = NULL;
if ( r1 ) reports.push_back(r1);
if ( r2 ) reports.push_back(r2);
vector<df::unit*> result;
unordered_set<int32_t> ids;
//out.print("%s,%d\n",__FILE__,__LINE__);
for ( size_t a = 0; a < reports.size(); a++ ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
vector<int32_t>& units = reportToRelevantUnits[reports[a]->id];
if ( units.size() > 2 ) {
if ( Once::doOnce("EventManager interaction too many relevant units") ) {
out.print("%s,%d: too many relevant units. On report\n \'%s\'\n", __FILE__, __LINE__, reports[a]->text.c_str());
}
}
for ( size_t b = 0; b < units.size(); b++ )
if ( ids.find(units[b]) == ids.end() ) {
ids.insert(units[b]);
result.push_back(df::unit::find(units[b]));
}
}
//out.print("%s,%d\n",__FILE__,__LINE__);
return result;
}
static void manageInteractionEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReportInteraction, false);
while (a < reports.size() && reports[a]->id <= lastReportInteraction) {
a++;
}
if ( a < reports.size() )
updateReportToRelevantUnits();
df::report* lastAttackEvent = NULL;
df::unit* lastAttacker = NULL;
df::unit* lastDefender = NULL;
unordered_map<int32_t,unordered_set<int32_t> > history;
for ( ; a < reports.size(); a++ ) {
df::report* report = reports[a];
lastReportInteraction = report->id;
df::announcement_type type = report->type;
if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET )
continue;
if ( report->flags.bits.continuation )
continue;
bool attack = type == df::announcement_type::INTERACTION_ACTOR;
if ( attack ) {
lastAttackEvent = report;
lastAttacker = NULL;
lastDefender = NULL;
}
vector<df::unit*> relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report);
InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits);
if ( data.attacker < 0 )
continue;
//out.print("%s,%d\n",__FILE__,__LINE__);
//if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id )
// continue; //lazy way of preventing duplicates
if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
vector<df::unit*> relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]);
InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants);
if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) {
//out.print("%s,%d\n",__FILE__,__LINE__);
data = data2;
a++;
}
}
{
#define HISTORY_ITEM 1
#if HISTORY_ITEM
unordered_set<int32_t>& b = history[data.attacker];
if ( b.find(data.defender) != b.end() )
continue;
history[data.attacker].insert(data.defender);
//b.insert(data.defender);
#else
unordered_set<int32_t>& b = history[data.attackReport];
if ( b.find(data.defendReport) != b.end() )
continue;
history[data.attackReport].insert(data.defendReport);
//b.insert(data.defendReport);
#endif
}
//out.print("%s,%d\n",__FILE__,__LINE__);
lastAttacker = df::unit::find(data.attacker);
lastDefender = df::unit::find(data.defender);
//fire event
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)&data);
}
//TODO: deduce attacker from latest defend event first
}
}

@ -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;
}

@ -773,7 +773,7 @@ df::job *Gui::getSelectedJob(color_ostream &out, bool quiet)
return getSelectedWorkshopJob(out, quiet);
}
static df::unit *getAnyUnit(df::viewscreen *top)
df::unit *Gui::getAnyUnit(df::viewscreen *top)
{
using namespace ui_sidebar_mode;
using df::global::ui;
@ -904,7 +904,7 @@ df::unit *Gui::getSelectedUnit(color_ostream &out, bool quiet)
return unit;
}
static df::item *getAnyItem(df::viewscreen *top)
df::item *Gui::getAnyItem(df::viewscreen *top)
{
using namespace ui_sidebar_mode;
using df::global::ui;
@ -1017,7 +1017,7 @@ df::item *Gui::getSelectedItem(color_ostream &out, bool quiet)
return item;
}
static df::building *getAnyBuilding(df::viewscreen *top)
df::building *Gui::getAnyBuilding(df::viewscreen *top)
{
using namespace ui_sidebar_mode;
using df::global::ui;
@ -1310,6 +1310,9 @@ void Gui::showAutoAnnouncement(
df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed)
{
if (!gview)
return NULL;
df::viewscreen * ws = &gview->view;
while (ws && ws->child)
ws = ws->child;

@ -22,69 +22,72 @@ must not be misrepresented as being the original software.
distribution.
*/
#include "Core.h"
#include "Error.h"
#include "Internal.h"
#include "MemAccess.h"
#include "MiscUtils.h"
#include "Types.h"
#include "VersionInfo.h"
#include <string>
#include <sstream>
#include <vector>
#include <cstdio>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <set>
using namespace std;
#include "Types.h"
#include "VersionInfo.h"
#include "MemAccess.h"
#include "ModuleFactory.h"
#include "modules/MapCache.h"
#include "modules/Materials.h"
#include "modules/Items.h"
#include "modules/Units.h"
#include "modules/MapCache.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "Error.h"
#include "MiscUtils.h"
#include "df/ui.h"
#include "df/world.h"
#include "df/item.h"
#include "df/body_part_raw.h"
#include "df/body_part_template_flags.h"
#include "df/building.h"
#include "df/building_actual.h"
#include "df/tool_uses.h"
#include "df/itemdef_weaponst.h"
#include "df/itemdef_trapcompst.h"
#include "df/itemdef_toyst.h"
#include "df/itemdef_toolst.h"
#include "df/itemdef_instrumentst.h"
#include "df/itemdef_armorst.h"
#include "df/caste_raw.h"
#include "df/creature_raw.h"
#include "df/general_ref.h"
#include "df/general_ref_building_holderst.h"
#include "df/general_ref_contained_in_itemst.h"
#include "df/general_ref_contains_itemst.h"
#include "df/general_ref_projectile.h"
#include "df/general_ref_unit_itemownerst.h"
#include "df/general_ref_unit_holderst.h"
#include "df/historical_entity.h"
#include "df/item.h"
#include "df/item_type.h"
#include "df/itemdef_ammost.h"
#include "df/itemdef_siegeammost.h"
#include "df/itemdef_armorst.h"
#include "df/itemdef_foodst.h"
#include "df/itemdef_glovesst.h"
#include "df/itemdef_shoesst.h"
#include "df/itemdef_shieldst.h"
#include "df/itemdef_helmst.h"
#include "df/itemdef_instrumentst.h"
#include "df/itemdef_pantsst.h"
#include "df/itemdef_foodst.h"
#include "df/trapcomp_flags.h"
#include "df/itemdef_shieldst.h"
#include "df/itemdef_shoesst.h"
#include "df/itemdef_siegeammost.h"
#include "df/itemdef_toolst.h"
#include "df/itemdef_toyst.h"
#include "df/itemdef_trapcompst.h"
#include "df/itemdef_weaponst.h"
#include "df/job_item.h"
#include "df/general_ref.h"
#include "df/general_ref_unit_itemownerst.h"
#include "df/general_ref_contains_itemst.h"
#include "df/general_ref_contained_in_itemst.h"
#include "df/general_ref_building_holderst.h"
#include "df/general_ref_projectile.h"
#include "df/viewscreen_itemst.h"
#include "df/vermin.h"
#include "df/map_block.h"
#include "df/proj_itemst.h"
#include "df/proj_list_link.h"
#include "df/unit_inventory_item.h"
#include "df/body_part_raw.h"
#include "df/reaction_product_itemst.h"
#include "df/tool_uses.h"
#include "df/trapcomp_flags.h"
#include "df/ui.h"
#include "df/unit.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/body_part_template_flags.h"
#include "df/general_ref_unit_holderst.h"
#include "df/unit_inventory_item.h"
#include "df/vermin.h"
#include "df/viewscreen_itemst.h"
#include "df/world.h"
#include "df/world_site.h"
using namespace DFHack;
using namespace df::enums;
@ -234,6 +237,8 @@ ITEMDEF_VECTORS
#undef ITEM
default:
if (items[1] == "NONE")
return true;
break;
}
@ -1153,7 +1158,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
case item_type::MEAT:
case item_type::PLANT:
case item_type::LEAVES:
case item_type::PLANT_GROWTH:
case item_type::CHEESE:
value = 2;
break;
@ -1327,4 +1332,53 @@ int Items::getValue(df::item *item)
value /= divisor;
}
return value;
}
}
int32_t Items::createItem(df::item_type item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_index, df::unit* unit) {
//based on Quietust's plugins/createitem.cpp
df::map_block* block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z);
CHECK_NULL_POINTER(block);
df::reaction_product_itemst* prod = df::allocate<df::reaction_product_itemst>();
prod->item_type = item_type;
prod->item_subtype = item_subtype;
prod->mat_type = mat_type;
prod->mat_index = mat_index;
prod->probability = 100;
prod->count = 1;
switch(item_type) {
case df::item_type::BAR:
case df::item_type::POWDER_MISC:
case df::item_type::LIQUID_MISC:
case df::item_type::DRINK:
prod->product_dimension = 150;
break;
case df::item_type::THREAD:
prod->product_dimension = 15000;
break;
case df::item_type::CLOTH:
prod->product_dimension = 10000;
break;
default:
prod->product_dimension = 1;
break;
}
//makeItem
vector<df::item*> out_items;
vector<df::reaction_reagent*> in_reag;
vector<df::item*> in_items;
df::enums::game_type::game_type type = *df::global::gametype;
prod->produce(unit, &out_items, &in_reag, &in_items, 1, job_skill::NONE,
df::historical_entity::find(unit->civ_id),
((type == df::enums::game_type::DWARF_MAIN) || (type == df::enums::game_type::DWARF_RECLAIM)) ? df::world_site::find(df::global::ui->site_id) : NULL);
if ( out_items.size() != 1 )
return -1;
for (size_t a = 0; a < out_items.size(); a++ ) {
out_items[a]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z);
}
return out_items[0]->id;
}

@ -63,6 +63,8 @@ using namespace std;
#include "df/region_map_entry.h"
#include "df/flow_info.h"
#include "df/plant.h"
#include "df/plant_tree_info.h"
#include "df/plant_tree_tile.h"
#include "df/building_type.h"
using namespace DFHack;
@ -726,14 +728,51 @@ void MapExtras::BlockInfo::prepare(Block *mblock)
block = mblock->getRaw();
parent = mblock->getParent();
column = Maps::getBlockColumn((block->map_pos.x / 48) * 3, (block->map_pos.y / 48) * 3);
SquashVeins(block,veinmats,veintype);
SquashVeins(block, veinmats, veintype);
SquashGrass(block, grass);
for (size_t i = 0; i < block->plants.size(); i++)
for (size_t i = 0; i < column->plants.size(); i++)
{
auto pp = block->plants[i];
plants[pp->pos] = pp;
auto pp = column->plants[i];
// A plant without tree_info is single tile
// TODO: verify that x any y lie inside the block.
if (!pp->tree_info)
{
if (pp->pos.z == block->map_pos.z)
plants[pp->pos] = pp;
continue;
}
// tree_info contains vertical slices of the tree. This ensures there's a slice for our Z-level.
df::plant_tree_info * info = pp->tree_info;
if (!((pp->pos.z - info->roots_depth <= block->map_pos.z) && ((pp->pos.z + info->body_height) > block->map_pos.z)))
continue;
// Parse through a single horizontal slice of the tree.
for (int xx = 0; xx < info->dim_x; xx++)
for (int yy = 0; yy < info->dim_y; yy++)
{
// Any non-zero value here other than blocked means there's some sort of branch here.
// If the block is at or above the plant's base level, we use the body array
// otherwise we use the roots.
// TODO: verify that the tree bounds intersect the block.
df::plant_tree_tile tile;
int z_diff = block->map_pos.z - pp->pos.z;
if (z_diff >= 0)
tile = info->body[z_diff][xx + (yy*info->dim_x)];
else
tile = info->roots[-1 - z_diff][xx + (yy*info->dim_x)];
if (tile.whole && !(tile.bits.blocked))
{
df::coord pos = pp->pos;
pos.x = pos.x - (info->dim_x / 2) + xx;
pos.y = pos.y - (info->dim_y / 2) + yy;
pos.z = block->map_pos.z;
plants[pos] = pp;
}
}
}
global_feature = Maps::getGlobalInitFeature(block->global_feature);
@ -804,6 +843,9 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos
rv.mat_index = mblock->biomeInfoAt(pos).lava_stone;
break;
case MUSHROOM:
case ROOT:
case TREE:
case PLANT:
rv.mat_type = MaterialInfo::PLANT_BASE;
if (auto plant = plants[block->map_pos + df::coord(x,y,0)])

@ -143,6 +143,17 @@ df::map_block *Maps::getBlock (int32_t blockx, int32_t blocky, int32_t blockz)
return world->map.block_index[blockx][blocky][blockz];
}
df::map_block_column *Maps::getBlockColumn(int32_t blockx, int32_t blocky)
{
if (!IsValid())
return NULL;
if ((blockx < 0) || (blocky < 0))
return NULL;
if ((blockx >= world->map.x_count_block) || (blocky >= world->map.y_count_block))
return NULL;
return world->map.column_index[blockx][blocky];
}
bool Maps::isValidTilePos(int32_t x, int32_t y, int32_t z)
{
if (!IsValid())
@ -242,7 +253,7 @@ void Maps::enableBlockUpdates(df::map_block *blk, bool flow, bool temperature)
blk->flags.bits.update_liquid_twice = true;
}
auto z_flags = world->map.z_level_flags;
auto z_flags = world->map_extras.z_level_flags;
int z_level = blk->map_pos.z;
if (z_flags && z_level >= 0 && z_level < world->map.z_count_block)
@ -375,20 +386,26 @@ bool Maps::ReadFeatures(df::map_block * block, t_feature * local, t_feature * gl
bool Maps::SortBlockEvents(df::map_block *block,
vector <df::block_square_event_mineralst *>* veins,
vector <df::block_square_event_frozen_liquidst *>* ices,
vector <df::block_square_event_material_spatterst *> *splatter,
vector <df::block_square_event_material_spatterst *> *materials,
vector <df::block_square_event_grassst *> *grasses,
vector <df::block_square_event_world_constructionst *> *constructions)
vector <df::block_square_event_world_constructionst *> *constructions,
vector <df::block_square_event_spoorst *> *spoors,
vector <df::block_square_event_item_spatterst *> *items)
{
if (veins)
veins->clear();
if (ices)
ices->clear();
if (splatter)
splatter->clear();
if (grasses)
grasses->clear();
if (constructions)
constructions->clear();
if (materials)
materials->clear();
if (grasses)
grasses->clear();
if (spoors)
spoors->clear();
if (items)
items->clear();
if (!block)
return false;
@ -407,17 +424,25 @@ bool Maps::SortBlockEvents(df::map_block *block,
if (ices)
ices->push_back((df::block_square_event_frozen_liquidst *)evt);
break;
case block_square_event_type::world_construction:
if (constructions)
constructions->push_back((df::block_square_event_world_constructionst *)evt);
break;
case block_square_event_type::material_spatter:
if (splatter)
splatter->push_back((df::block_square_event_material_spatterst *)evt);
if (materials)
materials->push_back((df::block_square_event_material_spatterst *)evt);
break;
case block_square_event_type::grass:
if (grasses)
grasses->push_back((df::block_square_event_grassst *)evt);
break;
case block_square_event_type::world_construction:
if (constructions)
constructions->push_back((df::block_square_event_world_constructionst *)evt);
case block_square_event_type::spoor:
if (spoors)
spoors->push_back((df::block_square_event_spoorst *)evt);
break;
case block_square_event_type::item_spatter:
if (items)
items->push_back((df::block_square_event_item_spatterst *)evt);
break;
}
}

@ -479,7 +479,6 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma
TEST(extract_bearing_fish, false);
TEST(extract_bearing_vermin, false);
TEST(processable_to_vial, structural && FLAG(plant, plant_raw_flags::EXTRACT_VIAL));
TEST(processable_to_bag, structural && FLAG(plant, plant_raw_flags::LEAVES));
TEST(processable_to_barrel, structural && FLAG(plant, plant_raw_flags::EXTRACT_BARREL));
TEST(solid, !(MAT_FLAG(ALCOHOL_PLANT) ||
MAT_FLAG(ALCOHOL_CREATURE) ||

@ -413,6 +413,29 @@ string Screen::getKeyDisplay(df::interface_key key)
return "?";
}
int Screen::keyToChar(df::interface_key key)
{
if (key < interface_key::STRING_A000 ||
key > interface_key::STRING_A255)
return -1;
if (key < interface_key::STRING_A128)
return key - interface_key::STRING_A000;
return key - interface_key::STRING_A128 + 128;
}
df::interface_key Screen::charToKey(char code)
{
int val = (unsigned char)code;
if (val < 127)
return df::interface_key(interface_key::STRING_A000 + val);
else if (val == 127)
return interface_key::NONE;
else
return df::interface_key(interface_key::STRING_A128 + (val-128));
}
/*
* Base DFHack viewscreen.
*/
@ -654,10 +677,10 @@ int dfhack_lua_viewscreen::do_input(lua_State *L)
lua_pushboolean(L, true);
lua_rawset(L, -3);
if (key >= interface_key::STRING_A000 &&
key <= interface_key::STRING_A255)
int charval = Screen::keyToChar(key);
if (charval >= 0)
{
lua_pushinteger(L, key - interface_key::STRING_A000);
lua_pushinteger(L, charval);
lua_setfield(L, -2, "_STRING");
}
}

@ -46,6 +46,7 @@ using namespace df::enums;
using df::global::world;
using df::global::d_init;
using df::global::gametype;
bool Translation::IsValid ()
{
@ -153,7 +154,7 @@ string Translation::TranslateName(const df::language_name * name, bool inEnglish
if (!name->nickname.empty())
{
word = "`" + name->nickname + "'";
switch (d_init ? d_init->nickname_dwarf : d_init_nickname::CENTRALIZE)
switch ((d_init && gametype) ? d_init->nickname[*gametype] : d_init_nickname::CENTRALIZE)
{
case d_init_nickname::REPLACE_ALL:
out = word;

@ -61,7 +61,7 @@ using namespace std;
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/burrow.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
@ -69,6 +69,7 @@ using namespace std;
#include "df/unit_misc_trait.h"
#include "df/unit_skill.h"
#include "df/curse_attr_change.h"
#include "df/squad.h"
using namespace DFHack;
using namespace df::enums;
@ -153,7 +154,7 @@ void Units::CopyCreature(df::unit * source, t_unit & furball)
// profession
furball.profession = source->profession;
// happiness
furball.happiness = source->status.happiness;
furball.happiness = 100;//source->status.happiness;
// physical attributes
memcpy(&furball.strength, source->body.physical_attrs, sizeof(source->body.physical_attrs));
@ -543,15 +544,15 @@ df::item *Units::getContainer(df::unit *unit)
return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM);
}
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure)
static df::identity *getFigureIdentity(df::historical_figure *figure)
{
if (figure && figure->info && figure->info->reputation)
return df::assumed_identity::find(figure->info->reputation->cur_identity);
return df::identity::find(figure->info->reputation->cur_identity);
return NULL;
}
df::assumed_identity *Units::getIdentity(df::unit *unit)
df::identity *Units::getIdentity(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
@ -1403,10 +1404,12 @@ std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::pro
{
std::string prof, race_prefix;
if (pid < (df::profession)0 || !is_valid_enum_item(pid))
return "";
bool use_race_prefix = (race >= 0 && race != df::global::ui->race_id);
if (pid < (df::profession)0 || !is_valid_enum_item(pid))
return "";
int16_t current_race = df::global::ui->race_id;
if (df::global::gamemode && *df::global::gamemode == df::game_mode::ADVENTURE)
current_race = world->units.active[0]->race;
bool use_race_prefix = (race >= 0 && race != current_race);
if (auto creature = df::creature_raw::find(race))
{
@ -1552,3 +1555,15 @@ int8_t DFHack::Units::getCasteProfessionColor(int race, int casteid, df::profess
// default to dwarven peasant color
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);
}

@ -1 +1 @@
Subproject commit 499507eac5bdd3abc70e204497248627327cf8de
Subproject commit 55b60e3aa99aece7e9a239b041dc4f95a58dcef3

@ -0,0 +1,30 @@
Notes by PeridexisErrant, on the needs_porting scripts and plugins:
I deleted:
attachtest.py obsolete
digger.cpp less useful than digger2, replaced by autochop
digger2.cpp replaced by digfort
dfstatus.cpp replaced by dfstatus.lua
drawtile.cpp replaced by tiletypes
fix-3708.cpp obsolete, bug fixed in vanilla
lair.cpp replaced by lair
reveal.py replaced by reveal
treedump.py replaced by prospect & autochop
veinlook.cpp replaced by prospect
veinswap.cpp replaced by changevein
To investigate further:
creaturemanager.cpp modify skills and labors of creatures, kill creatures, etc; impressive but I suspect most functions implemented elsewhere
digpattern.cpp allows multi-Z designations. Obsolete, or is there more here?
incrementalsearch.cpp linux-only memory stuff; unqualified to judge
SegementedFinder.h more memory stuff
To port:
copypaste.cpp high value target - a proof of concept plugin to allow copy-pasting in DF; does both terrain and buildings/constructions
dfbauxtite.cpp changes material of mechanisms to bauxtite (ie magma-safe)
hellhole.cpp instantly creates a hole to the HFS
hotkeynotedump.py outputs a list of hotkeys and names; most useful before keybindings were possible. Trival to port but low value.
itemdesignator.cpp mostly replaced by Falconne's enhanced stocks, but could port the interesting 'set fire to things' function
position.py outputs very detailed time& place info

@ -1,53 +0,0 @@
import time
from pydfhack import ContextManager
df_cm = ContextManager("Memory.xml")
df = None
def test_attach():
global df
if not df:
df = df_cm.get_single_context()
if not df.attach():
print "Unable to attach!"
return False
elif not df.detach():
print "Unabled to detach!"
return False
else:
return True
def suspend_test():
global df
if not df:
df = df_cm.get_single_context()
print "Testing suspend/resume"
df.attach()
t1 = time.time()
for i in xrange(1000):
df.suspend()
if i % 10 == 0:
print "%i%%" % (i / 10.0,)
df.resume()
t2 = time.time()
df.detach()
print "suspend test done in $0.9f seconds" % (t2 - t1)
if __name__ == "__main__":
if test_attach():
suspend_test()
print "Done. Press any key to continue"
raw_input()

@ -1,206 +0,0 @@
/*
* dfstatus.cpp
*/
#include <curses.h>
#ifndef LINUX_BUILD
#include <windows.h>
#endif
#include <time.h>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <climits>
#include <vector>
#include <cstring>
#include <string>
//#include <conio.h> //to break on keyboard input
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#include <DFHack.h>
#include <DFVector.h>
#include <extra/stopwatch.h>
WINDOW *create_newwin(int height, int width, int starty, int startx);
int32_t drinkCount = 0;
int32_t mealsCount = 0;
int32_t plantCount = 0;
int32_t fishCount = 0;
int32_t meatCount = 0;
int32_t logsCount = 0;
int32_t barCount = 0;
int32_t clothCount = 0;
int32_t ironBars = 0;
int32_t pigIronBars = 0;
int32_t goldBars = 0;
int32_t silverBars = 0;
int32_t copperBars = 0;
int32_t steelBars = 0;
int32_t fuel = 0;
uint64_t start_time = 0;
uint64_t end_time = 0;
uint64_t total_time = 0;
WINDOW *create_newwin(int height, int width, int starty, int startx){
WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, 0, 0); /* 0, 0 gives default characters
* for the vertical and horizontal
* lines */
//first row
mvwprintw(local_win,2 ,2,"Drinks : %d", drinkCount);
mvwprintw(local_win,4 ,2,"Meals : %d", mealsCount);
mvwprintw(local_win,6 ,2,"Plants : %d", plantCount);
mvwprintw(local_win,7 ,2,"Fish : %d", fishCount);
mvwprintw(local_win,8 ,2,"Meat : %d", meatCount);
mvwprintw(local_win,10,2,"Logs : %d", logsCount);
mvwprintw(local_win,12,2,"Cloth : %d", clothCount);
//second row
mvwprintw(local_win,2,22,"Iron Bars : %d", ironBars);
mvwprintw(local_win,3,22,"Gold Bars : %d", goldBars);
mvwprintw(local_win,4,22,"Silver Bars : %d", silverBars);
mvwprintw(local_win,5,22,"Copper Bars : %d", copperBars);
mvwprintw(local_win,6,22,"Steel Bars : %d", steelBars);
mvwprintw(local_win,7,22,"Pig iron Bars : %d", pigIronBars);
mvwprintw(local_win,9,22,"Fuel : %d", fuel);
total_time += end_time - start_time;
mvwprintw(local_win,14,2,"Time: %d ms last update, %d ms total", end_time - start_time, total_time);
wrefresh(local_win); // paint the screen and all components.
return local_win;
}
int main()
{
WINDOW *my_win;
int startx, starty, width, height;
DFHack::Process * p;
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context * DF;
DFHack::Materials * Materials;
try{ //is DF running?
DF = DFMgr.getSingleContext();
DF->Attach();
Materials = DF->getMaterials();
Materials->ReadAllMaterials();
DF->Resume();
}
catch (exception& e){
cerr << e.what() << endl;
return 1;
}
//init and Attach
ofstream file("dfstatus_errors.txt");
streambuf* strm_buffer = cerr.rdbuf(); // save cerr's output buffer
cerr.rdbuf (file.rdbuf()); // redirect output into the file
initscr(); //start curses.
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
do{
drinkCount = 0;
mealsCount = 0;
plantCount = 0;
fishCount = 0;
meatCount = 0;
logsCount = 0;
barCount = 0;
clothCount = 0;
ironBars = 0;
pigIronBars = 0;
goldBars = 0;
silverBars = 0;
copperBars = 0;
steelBars = 0;
fuel = 0;
//FILE * pFile;
//pFile = fopen("dump.txt","w");
start_time = GetTimeMs64();
if(!DF->Suspend())
{
break;
}
//DFHack::Gui * Gui = DF->getGui();
DFHack::Items * Items = DF->getItems();
Items->Start();
DFHack::VersionInfo * mem = DF->getMemoryInfo();
p = DF->getProcess();
DFHack::OffsetGroup* itemGroup = mem->getGroup("Items");
DFHack::DfVector <uint32_t> p_items (p, itemGroup->getAddress("items_vector"));
uint32_t size = p_items.size();
DFHack::dfh_item itm; //declare itm
//memset(&itm, 0, sizeof(DFHack::dfh_item)); //seems to set every value in itm to 0
for(unsigned int idx = 0; idx < size; idx++) //fill our item variables with this loop
{
Items->readItem(p_items[idx], itm);
if (itm.base.flags.owned) //only count what we actually own.
continue;
string s0 = Items->getItemClass(itm.matdesc.itemType).c_str();
string s1 = Items->getItemDescription(itm, Materials).c_str();
if( s0 == "drink" ) {drinkCount += itm.quantity;}
else if(s0 == "food"){mealsCount += itm.quantity;}
else if(s0 == "plant"){plantCount += itm.quantity;}
else if(s0 == "fish"){fishCount += itm.quantity;}
else if(s0 == "meat"){meatCount += itm.quantity;}
else if(s0 == "wood"){logsCount += itm.quantity;}
else if(s0 == "cloth"){clothCount += itm.quantity;}
else if(s0 == "bar") //need to break it down by ItemDescription to get the different types of bars.
{
barCount = barCount + itm.quantity;
if(s1.find("PIG_IRON")!=string::npos){pigIronBars++;}
else if(s1.find("IRON")!=string::npos){ironBars++;}
else if(s1.find("GOLD")!=string::npos){goldBars++;}
else if(s1.find("SILVER")!=string::npos){silverBars++;}
else if(s1.find("COPPER")!=string::npos){copperBars++;}
else if(s1.find("STEEL")!=string::npos){steelBars++;}
else if(s1.find("COAL")!=string::npos){fuel++;}
}
/*if(s0 != "boulder" && s0 != "thread"){
fprintf(pFile,"%5d: %12s - %64s - [%d]\n", idx, Items->getItemClass(itm.matdesc.itemType).c_str(), Items->getItemDescription(itm, Materials).c_str(), itm.quantity);
}*/
}
DF->Resume();
end_time = GetTimeMs64();
//printf("%d - %d\n", (clock()/CLOCKS_PER_SEC),(clock()/CLOCKS_PER_SEC)%60);
height = LINES;
width = COLS;
starty = (LINES - height) / 2;
startx = (COLS - width) / 2;
my_win = create_newwin(height, width, starty, startx);
#ifdef LINUX_BUILD
sleep(10);
#else
Sleep(10000);
#endif
} while(true);
endwin(); /* End curses mode */
cerr.rdbuf (strm_buffer); // restore old output buffer
file.close();
return 0;
}

@ -1,363 +0,0 @@
// digger.cpp
// NOTE currently only works with trees
// TODO add a sort of "sub-target" to dig() to make it able to designate stone as well
#include <iostream>
#include <vector>
#include <list>
#include <cstdlib>
#include <algorithm>
#include <assert.h>
using namespace std;
#include <DFHack.h>
#include <DFTileTypes.h>
#include <argstream.h>
// counts the occurances of a certain element in a vector
// used to determine of a given tile is a target
int vec_count(vector<uint16_t>& vec, uint16_t t)
{
int count = 0;
for (uint32_t i = 0; i < vec.size(); ++i)
{
if (vec[i] == t)
++count;
}
return count;
}
// splits a string on a certain char
//
// src is the string to split
// delim is the delimiter to split the string around
// tokens is filled with every occurance between delims
void string_split(vector<string>& tokens, const std::string& src, const std::string& delim)
{
std::string::size_type start = 0;
std::string::size_type end;
while (true)
{
end = src.find(delim, start);
tokens.push_back(src.substr(start, end - start));
if (end == std::string::npos) // last token handled
break;
start = end + delim.size(); // skip next delim
}
}
// this is used to parse the command line options
void parse_int_csv(vector<uint16_t>& targets, const std::string& src)
{
std::string::size_type start = 0;
std::string::size_type end;
while (true)
{
end = src.find(",", start);
targets.push_back(atoi(src.substr(start, end - start).c_str()));
if (end == std::string::npos) // last token handled
break;
start = end + 1; // skip next delim
}
}
struct DigTarget
{
DigTarget() :
source_distance(0),
grid_x(0), grid_y(0),
local_x(0), local_y(0),
real_x(0), real_y(0), z(0)
{
}
DigTarget(
int realx, int realy, int _z,
int sourcex, int sourcey, int sourcez) :
real_x(realx), real_y(realy), z(_z)
{
grid_x = realx/16;
grid_y = realy/16;
local_x = realx%16;
local_y = realy%16;
source_distance = manhattan_distance(
real_x, real_y, z,
sourcex, sourcey, sourcez);
}
DigTarget(
int gridx, int gridy, int _z,
int localx, int localy,
int sourcex, int sourcey, int sourcez) :
grid_x(gridx), grid_y(gridy),
local_x(localx), local_y(localy),
z(_z)
{
real_x = (grid_x*16)+local_x;
real_y = (grid_y*16)+local_y;
source_distance = manhattan_distance(
real_x, real_y, z,
sourcex, sourcey, sourcez);
}
int source_distance; // the distance to the source coords, used for sorting
int grid_x, grid_y; // what grid the target is in
int local_x, local_y; // on what coord in the grid the target is in (0-16)
int real_x, real_y; // real coordinates for target, thats grid*16+local
int z; // z position for target, stored plain since there arent z grids
bool operator<(const DigTarget& o) const { return source_distance < o.source_distance; }
private:
// calculates the manhattan distance between two coords
int manhattan_distance(int x, int y, int z, int xx, int yy, int zz)
{
return abs(x-xx)+abs(y-yy)+abs(z-zz);
}
};
int dig(DFHack::Maps* Maps,
vector<uint16_t>& targets,
int num = -1,
const int x_source = 0,
const int y_source = 0,
const int z_source = 0,
bool verbose = false)
{
if (num == 0)
return 0; // max limit of 0, nothing to do
uint32_t x_max,y_max,z_max;
DFHack::designations40d designations;
DFHack::tiletypes40d tiles;
Maps->getSize(x_max,y_max,z_max);
// every tile found, will later be sorted by distance to source
vector<DigTarget> candidates;
if (verbose)
cout << "source is " << x_source << " " << y_source << " " << z_source << endl;
// walk the map
for(uint32_t x = 0; x < x_max; x++)
{
for(uint32_t y = 0; y < y_max; y++)
{
for(uint32_t z = 0; z < z_max; z++)
{
if(Maps->isValidBlock(x,y,z))
{
// read block designations and tiletype
Maps->ReadDesignations(x,y,z, &designations);
Maps->ReadTileTypes(x,y,z, &tiles);
// search all tiles for dig targets:
// visible, not yet marked for dig and matching tile type
for(uint32_t lx = 0; lx < 16; lx++)
{
for(uint32_t ly = 0; ly < 16; ly++)
{
if (/*designations[lx][ly].bits.hidden == 0 && */
designations[lx][ly].bits.dig == 0 &&
vec_count(targets, DFHack::tileShape(tiles[lx][ly])) > 0)
{
DigTarget dt(
x, y, z,
lx, ly,
x_source, y_source, z_source);
candidates.push_back(dt);
if (verbose)
{
cout << "target found at " << dt.real_x << " " << dt.real_y << " " << dt.z;
cout << ", " << dt.source_distance << " tiles to source" << endl;
}
}
} // local y
} // local x
}
}
}
}
// if we found more tiles than was requested, sort them by distance to source,
// keep the front 'num' elements and drop the rest
if (num != -1 && candidates.size() > (unsigned int)num)
{
sort(candidates.begin(), candidates.end());
candidates.resize(num);
}
num = candidates.size();
if (verbose)
cout << "=== proceeding to designating targets ===" << endl;
// mark the tiles for actual digging
for (vector<DigTarget>::const_iterator i = candidates.begin(); i != candidates.end(); ++i)
{
if (verbose)
{
cout << "designating at " << (*i).real_x << " " << (*i).real_y << " " << (*i).z;
cout << ", " << (*i).source_distance << " tiles to source" << endl;
}
// TODO this could probably be made much better, theres a big chance the trees are on the same grid
Maps->ReadDesignations((*i).grid_x, (*i).grid_y, (*i).z, &designations);
designations[(*i).local_x][(*i).local_y].bits.dig = DFHack::designation_default;
Maps->WriteDesignations((*i).grid_x, (*i).grid_y, (*i).z, &designations);
// Mark as dirty so the jobs are properly picked up by the dwarves
Maps->WriteDirtyBit((*i).grid_x, (*i).grid_y, (*i).z, true);
}
return num;
}
void test()
{
//////////////////////////
// DigTarget
{
DigTarget dt(
20, 35, 16,
10, 12, 14);
assert(dt.grid_x == 1);
assert(dt.grid_y == 2);
assert(dt.local_x == 4);
assert(dt.local_y == 3);
assert(dt.real_x == 20);
assert(dt.real_y == 35);
assert(dt.z == 16);
assert(dt.source_distance == 35);
}
{
DigTarget dt(
2, 4, 16,
5, 10,
10, 12, 14);
assert(dt.grid_x == 2);
assert(dt.grid_y == 4);
assert(dt.local_x == 5);
assert(dt.local_y == 10);
assert(dt.real_x == 37);
assert(dt.real_y == 74);
assert(dt.z == 16);
assert(dt.source_distance == 91);
}
//////////////////////////
// string splitter
{
vector<string> tokens;
string src = "10,9,11";
string delim = ",";
string_split(tokens, src, delim);
assert(tokens.size() == 3);
assert(tokens[0] == "10");
assert(tokens[1] == "9");
assert(tokens[2] == "11");
}
{
vector<string> tokens;
string src = "10";
string delim = ",";
string_split(tokens, src, delim);
assert(tokens.size() == 1);
assert(tokens[0] == "10");
}
{
vector<uint16_t> targets;
parse_int_csv(targets, "9,10");
assert(targets[0] == 9);
assert(targets[1] == 10);
}
}
int main (int argc, char** argv)
{
//test();
// Command line options
string s_targets;
string s_origin;
bool verbose;
int max = 10;
argstream as(argc,argv);
as >>option('v',"verbose",verbose,"Active verbose mode")
>>parameter('o',"origin",s_origin,"Close to where we should designate targets, format: x,y,z")
>>parameter('t',"targets",s_targets,"What kinds of tile we should designate, format: type1,type2")
>>parameter('m',"max",max,"The maximum limit of designated targets")
>>help();
// some commands need extra care
vector<uint16_t> targets;
parse_int_csv(targets, s_targets);
vector<uint16_t> origin;
parse_int_csv(origin, s_origin);
// sane check
if (!as.isOk())
{
cout << as.errorLog();
}
else if (targets.size() == 0 || origin.size() != 3)
{
cout << as.usage();
}
else
{
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context *DF = DFMgr.getSingleContext();
try
{
DF->Attach();
}
catch (exception& e)
{
cerr << e.what() << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
DFHack::Maps *Maps = DF->getMaps();
if (Maps && Maps->Start())
{
int count = dig(Maps, targets, max, origin[0],origin[1],origin[2], verbose);
cout << count << " targets designated" << endl;
Maps->Finish();
if (!DF->Detach())
{
cerr << "Unable to detach DF process" << endl;
}
}
else
{
cerr << "Unable to init map" << endl;
}
}
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -1,180 +0,0 @@
/**
* @file digger2.cpp
* @author rOut
*
* Improved digger tool.
*
* Takes a text file as first an only argument.
* The text file is read as a grid, and every character represents a designation for a tile.
* Allowed characters are 'd' for dig, 'u' for up stairs, 'j' for down stairs, 'i' for up and down stairs, 'h' for channel, 'r' for upward ramp and 'x' to remove designation.
* Other characters don't do anything and can be used for padding.
* The designation pattern is the wrote in game memory, centered on the current cursor position. Thus, the game needs to be in designation mode or, perhaps, any other mode that have a cursor.
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <list>
#include <cstdlib>
#include <algorithm>
#include <assert.h>
using namespace std;
#include <DFHack.h>
#include <DFTileTypes.h>
#define BLOCK_SIZE 16
void dig(DFHack::Maps* layers, DFHack::Gui* Gui, ::std::vector< ::std::string >& dig_map, bool verbose = false)
{
int32_t x_cent;
int32_t y_cent;
int32_t z_cent;
Gui->getCursorCoords(x_cent, y_cent, z_cent);
// ::std::cout << "x_cent: " << x_cent << " y_cent: " << y_cent << " z_cent: " << z_cent << ::std::endl;
int32_t z_from = z_cent;
int32_t z = 0;
uint32_t x_max;
uint32_t y_max;
uint32_t z_max;
layers->getSize(x_max, y_max, z_max);
// ::std::cout << "x_max: " << x_max << " y_max: " << y_max << " z_max: " << z_max << ::std::endl;
int32_t dig_height = dig_map.size();
int32_t y_from = y_cent - (dig_height / 2);
// ::std::cout << "dig_height: " << dig_height << ::std::endl;
// ::std::cout << "y_from: " << y_from << ::std::endl;
int32_t y = 0;
DFHack::designations40d designations;
DFHack::tiletypes40d tiles;
::std::vector< ::std::string >::iterator str_it;
for (str_it = dig_map.begin(); str_it != dig_map.end(); ++str_it) {
int32_t dig_width = str_it->size();
int32_t x_from = x_cent - (dig_width / 2);
// ::std::cout << "x_cent: " << x_cent << " y_cent: " << y_cent << " z_cent: " << z_cent << ::std::endl;
// ::std::cout << "dig_width: " << dig_width << ::std::endl;
// ::std::cout << "x_from: " << x_from << ::std::endl;
int32_t x = 0;
::std::string::iterator chr_it;
for (chr_it = str_it->begin(); chr_it != str_it ->end(); ++chr_it)
{
int32_t x_grid = (x_from + x) / BLOCK_SIZE;
int32_t y_grid = (y_from + y) / BLOCK_SIZE;
int32_t z_grid = z_from + z;
int32_t x_locl = (x_from + x) - x_grid * BLOCK_SIZE;
int32_t y_locl = (y_from + y) - y_grid * BLOCK_SIZE;
int32_t z_locl = 0;
if (x_grid >= 0 && y_grid >= 0 && x_grid < x_max && y_grid < y_max)
{
// TODO this could probably be made much better, theres a big chance the trees are on the same grid
layers->ReadDesignations(x_grid, y_grid, z_grid, &designations);
layers->ReadTileTypes(x_grid, y_grid, z_grid, &tiles);
// ::std::cout << ::std::hex << "designations: " << designations[x_locl][y_locl].bits.dig << ::std::dec << ::std::endl;
DFHack::naked_designation & des = designations[x_locl][y_locl].bits;
if ( DFHack::tileShape(tiles[x_locl][y_locl]) == DFHack::WALL)
{
switch ((char) *chr_it)
{
case 'd':
des.dig = DFHack::designation_default;
break;
case 'u':
des.dig = DFHack::designation_u_stair;
break;
case 'j':
des.dig = DFHack::designation_d_stair;
break;
case 'i':
des.dig = DFHack::designation_ud_stair;
break;
case 'h':
des.dig = DFHack::designation_channel;
break;
case 'r':
des.dig = DFHack::designation_ramp;
break;
case 'x':
des.dig = DFHack::designation_no;
break;
}
if (verbose)
{
::std::cout << "designating " << (char) *chr_it << " at " << x_from + x << " " << y_from + y << " " << z_from + z << ::std::endl;
}
layers->WriteDesignations(x_grid, y_grid, z_grid, &designations);
// Mark as dirty so the jobs are properly picked up by the dwarves
layers->WriteDirtyBit(x_grid, y_grid, z_grid, true);
}
}
++x;
}
++y;
}
}
int main(int argc, char** argv) {
if(argc < 2) {
::std::cout << "gimme a file!" << ::std::endl;
return 1;
}
::std::ifstream map_in(argv[1]);
::std::vector< ::std::string > dig_map;
while (map_in.good() && !map_in.eof() && !map_in.bad()) {
::std::string line;
map_in >> line;
dig_map.push_back(line);
}
dig_map.resize(dig_map.size() - 1);
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context * DF = DFMgr.getSingleContext();
try {
DF->Attach();
} catch (::std::exception& e) {
::std::cerr << e.what() << ::std::endl;
#ifndef LINUX_BUILD
::std::cin.ignore();
#endif
return 1;
}
DFHack::Maps *layers = DF->getMaps();
if (layers && layers->Start()) {
dig(layers, DF->getGui(), dig_map, true);
::std::cout << "Finished digging" << ::std::endl;
layers->Finish();
if (!DF->Detach()) {
::std::cerr << "Unable to detach DF process" << ::std::endl;
}
} else {
::std::cerr << "Unable to init map" << ::std::endl;
}
#ifndef LINUX_BUILD
::std::cout << "Done. Press any key to continue" << ::std::endl;
::std::cin.ignore();
#endif
return 0;
}

@ -1,315 +0,0 @@
//
#include <iostream>
#include <vector>
#include <map>
#include <cstdlib>
#include <limits>
using namespace std;
#include <conio.h>
#include <DFHack.h>
#include <DFTileTypes.h>
//Avoid including Windows.h because it causes name clashes
extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long milliseconds);
//Trim
#define WHITESPACE " \t\r\n"
inline string trimr(const string & s, const string & t = WHITESPACE)
{
string d (s);
string::size_type i (d.find_last_not_of (t));
if (i == string::npos)
return "";
else
return d.erase (d.find_last_not_of (t) + 1) ;
}
inline string triml(const string & s, const string & t = WHITESPACE)
{
string d (s);
return d.erase (0, s.find_first_not_of (t)) ;
}
inline string trim(const string & s, const string & t = WHITESPACE)
{
string d (s);
return triml(trimr(d, t), t);
}
void printtiletype( int i ){
printf("%s\n%4i ; %-13s ; %-11s ; %c ; %-12s ; %s\n",
( DFHack::tileTypeTable[i].name ? DFHack::tileTypeTable[i].name : "[invalid tile]" ),
i,
( DFHack::tileTypeTable[i].name ? DFHack::TileShapeString[ DFHack::tileTypeTable[i].shape ] : "" ),
( DFHack::tileTypeTable[i].name ? DFHack::TileMaterialString[ DFHack::tileTypeTable[i].material ] : "" ),
( DFHack::tileTypeTable[i].variant ? '0'+ DFHack::tileTypeTable[i].variant : ' ' ),
( DFHack::tileTypeTable[i].special ? DFHack::TileSpecialString[ DFHack::tileTypeTable[i].special ] : "" ),
( DFHack::tileTypeTable[i].direction.whole ? DFHack::tileTypeTable[i].direction.getStr() : "" ),
0
);
}
int main (void)
{
int32_t x,y,z,tx,ty;
//DFHack::designations40d designations;
DFHack::tiletypes40d tiles;
//DFHack::t_temperatures temp1,temp2;
uint32_t x_max,y_max,z_max;
int32_t oldT, newT;
int count, dirty;
//Brush defaults
DFHack::TileShape BrushClass = DFHack::WALL;
DFHack::TileMaterial BrushMat = DFHack::tilematerial_invalid;
int BrushType = -1;
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context *DF;
DFHack::Maps * Maps;
DFHack::Gui * Gui;
try
{
DF=DFMgr.getSingleContext();
DF->Attach();
Maps = DF->getMaps();
Maps->Start();
Maps->getSize(x_max,y_max,z_max);
Gui = DF->getGui();
}
catch (exception& e)
{
cerr << e.what() << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
bool end = false;
cout << "Welcome to the Tile Drawing tool.\nType 'help' or ? for a list of available commands, 'q' to quit" << endl;
string mode = "wall";
string command = "";
while(!end)
{
DF->Resume();
cout << endl << ":";
getline(cin, command);
int ch = command[0];
if(command.length()<=0) ch=0;
if( ((int)command.find("help")) >=0 ) ch='?'; //under windows, find was casting unsigned!
switch(ch)
{
case '?':
cout << "Modes:" << endl
<< "O - draw Open Space" << endl
<< "M - draw material only (shape unchanged)" << endl
<< "m number - use Material value entered" << endl
<< "r - use Rock/stone material" << endl
<< "l - use Soil material" << endl
<< "v - use Vein material" << endl
<< "H - draw tile shape only (material unchanged)" << endl
<< "h number - draw Tile Shape value entered" << endl
<< "w - draw Wall tiles" << endl
<< "f - draw Floor tiles" << endl
<< "t number - draw exact tile type entered" << endl
<< "Commands:" << endl
<< "p - print tile shapes and materials, and current brush" << endl
<< "P - print all tile types" << endl
<< "q - quit" << endl
<< "help OR ? - print this list of commands" << endl
<< "d - being drawing" << endl
<< endl
<< "Usage:\nChoose a mode (default is walls), then enter 'd' to being drawing.\nMove the cursor in DF wherever you want to draw.\nPress any key to pause drawing." << endl;
break;
case 'p':
//Classes
printf("\nTile Type Classes:\n");
for(int i=0;i<DFHack::tileshape_count;++i)
{
printf("%4i ; %s\n", i, DFHack::TileShapeString[i] ,0 );
}
//Materials
printf("\nTile Type Materials:\n");
for(int i=0;i<DFHack::tilematerial_count;++i)
{
printf("%4i ; %s\n", i, DFHack::TileMaterialString[i] ,0 );
}
//fall through...
case 10:
case 13:
case 0:
//Print current cursor & brush settings.
cout << "\nCurrent Brush:\n";
cout << "tile = ";
if(BrushClass<0) cout<<"(not drawing)"; else cout<<DFHack::TileShapeString[BrushClass]; cout << endl;
cout << "mat = ";
if(BrushMat<0) cout<<"(not drawing)"; else cout<<DFHack::TileMaterialString[BrushMat]; cout << endl;
cout << "type = ";
if(BrushType<0){
cout<<"(not drawing)";
}else{
printtiletype(BrushType);
}
break;
case 'P':
cout << "\nAll Valid Tile Types:\n";
for(int i=0;i<TILE_TYPE_ARRAY_LENGTH;++i)
{
if( DFHack::tileTypeTable[i].name )
printtiletype(i);
}
case 'w':
BrushType=-1;
BrushClass = DFHack::WALL;
cout << "Tile brush shape set to Wall." << endl;
break;
case 'f':
BrushType=-1;
BrushClass = DFHack::FLOOR;
cout << "Tile brush shape set to Floor." << endl;
break;
case 'h':
BrushType=-1;
BrushClass = (DFHack::TileShape)atol( command.c_str()+1 );
cout << "Tile brush shape set to " << BrushClass << endl;
break;
case 'M':
BrushClass = DFHack::tileshape_invalid;
cout << "Tile brush will not draw tile shape." << endl;
break;
case 'r':
BrushType=-1;
BrushMat = DFHack::STONE;
cout << "Tile brush material set to Rock." << endl;
break;
case 'l':
BrushType=-1;
BrushMat = DFHack::SOIL;
cout << "Tile brush material set to Soil." << endl;
break;
case 'v':
BrushType=-1;
BrushMat = DFHack::VEIN;
cout << "Tile brush material set to Vein." << endl;
break;
case 'm':
BrushType=-1;
BrushMat = (DFHack::TileMaterial)atol( command.c_str()+1 );
cout << "Tile brush material set to " << BrushMat << endl;
break;
case 'H':
BrushMat = DFHack::tilematerial_invalid;
cout << "Tile brush will not draw material." << endl;
break;
case 'O':
BrushType=-1;
BrushClass = DFHack::EMPTY;
BrushMat = DFHack::AIR;
cout << "Tile brush will draw Open Space." << endl;
break;
case 't':
BrushClass = DFHack::tileshape_invalid ;
BrushMat = DFHack::tilematerial_invalid;
BrushType = atol( command.c_str()+1 );
cout << "Tile brush type set to:" << endl;
printtiletype(BrushType);
break;
case 'q':
end = true;
cout << "Bye!" << endl;
break;
case 'd':
{
count=0;
cout << "Beginning to draw at cursor." << endl << "Press any key to stop drawing." << endl;
//DF->Suspend();
kbhit(); //throw away, just to be sure.
for(;;)
{
if(!Maps->Start())
{
cout << "Can't see any DF map loaded." << endl;
break;
}
if(!Gui->getCursorCoords(x,y,z))
{
cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
}
//cout << "cursor coords: " << x << "/" << y << "/" << z << endl;
tx=x%16; ty=y%16;
if(!Maps->isValidBlock(x/16,y/16,z))
{
cout << "Invalid block." << endl;
break;
}
//Read the tiles.
dirty=0;
Maps->ReadTileTypes((x/16),(y/16),z, &tiles);
oldT = tiles[tx][ty];
newT = -1;
if( 0<BrushType ){
//Explicit tile type set. Trust the user.
newT = BrushType;
}else if( 0==BrushMat && 0==BrushClass ){
//Special case, Empty Air.
newT = 32;
}else if( BrushMat>=0 && BrushClass>=0 && ( BrushClass != DFHack::tileTypeTable[oldT].shape || BrushMat != DFHack::tileTypeTable[oldT].material) ){
//Set tile material and class
newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tileTypeTable[oldT].variant , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction );
if(newT<0) newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tilevariant_invalid, DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction );
if(newT<0) newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , (uint32_t)0 );
}else if( BrushMat<0 && BrushClass>=0 && BrushClass != DFHack::tileTypeTable[oldT].shape ){
//Set current tile class only, as accurately as can be expected
newT = DFHack::findSimilarTileType(oldT,BrushClass);
}else if( BrushClass<0 && BrushMat>=0 && BrushMat != DFHack::tileTypeTable[oldT].material ){
//Set current tile material only
newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tileTypeTable[oldT].variant , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction );
if(newT<0) newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction );
if(newT<0) newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , (uint32_t)0 );
}
//If no change, skip it (couldn't find a good tile type, or already what we want)
if ( newT > 0 && oldT != newT ){
//Set new tile type
tiles[tx][ty] = newT;
dirty=-1;
}
//If anything was changed, write it all.
if (dirty)
{
//Maps->WriteDesignations(x/16,y/16,z/16, &designations);
Maps->WriteTileTypes(x/16,y/16,z, &tiles);
printf("(%4d,%4d,%4d)",x,y,z);
++count;
}
Maps->Finish();
Sleep(10);
if( kbhit() ) break;
}
cin.clear();
cout << endl << count << " tiles were drawn." << endl << "Drawing halted. Entering command mode." << endl;
}
continue;
break;
default:
cout << "Unknown command: " << command << endl;
}
}
DF->Detach();
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -1,217 +0,0 @@
/* Fixes bug 3708 (Ghosts that can't be engraved on a slab).
Cause of the bug:
In order to be engraved on a slab, the creature must be
a historical figure, i.e. be in the historical figure list
of the Legends mode. It seems that caravan guards are not
added to that list until they do something notable, e.g.
kill a goblin. Unfortunately, their own death doesn't
trigger this sometimes.
Solution:
Steal a historical figure entry from a dead goblin, by
replacing the IDs in the structures; also overwrite his
name, race and profession to make the menus make slightly
more sense.
Downsides:
- Obviously, this is an ugly hack.
- The Legends mode still lists the guard as belonging to
the goblin civilization, and killed by whoever killed the
original goblin. There might be other inconsistencies.
Positive sides:
- Avoids messing with tricky creature control code,
by allowing the ghost to be removed naturally.
*/
#include <iostream>
#include <climits>
#include <string.h>
#include <vector>
#include <list>
#include <stdio.h>
using namespace std;
#define DFHACK_WANT_MISCUTILS
#include <DFHack.h>
enum likeType
{
FAIL = 0,
MATERIAL = 1,
ITEM = 2,
FOOD = 3
};
DFHack::Materials * Materials;
DFHack::VersionInfo *mem;
DFHack::Creatures * Creatures = NULL;
void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature)
{
cout << "Address: " << hex << creature.origin << dec << ", creature race: " << Materials->raceEx[creature.race].rawname
<< ", position: " << creature.x << "x " << creature.y << "y "<< creature.z << "z" << endl
<< "Name: " << creature.name.first_name;
if (creature.name.nickname[0])
cout << " `" << creature.name.nickname << "'";
DFHack::Translation * Tran = DF->getTranslation();
cout << " " << Tran->TranslateName(creature.name,false)
<< " (" << Tran->TranslateName(creature.name,true) << ")" << endl;
cout << "Profession: " << mem->getProfession(creature.profession);
if(creature.custom_profession[0])
cout << ", custom: " << creature.custom_profession;
uint32_t dayoflife = creature.birth_year*12*28 + creature.birth_time/1200;
cout << endl
<< "Born on the year " << creature.birth_year
<< ", month " << (creature.birth_time/1200/28)
<< ", day " << ((creature.birth_time/1200) % 28 + 1)
<< ", " << dayoflife << " days lived." << endl << endl;
}
int main (int numargs, char ** args)
{
DFHack::World * World;
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context* DF;
try
{
DF = DFMgr.getSingleContext();
DF->Attach();
}
catch (exception& e)
{
cerr << e.what() << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
Creatures = DF->getCreatures();
Materials = DF->getMaterials();
World = DF->getWorld();
DFHack::Translation * Tran = DF->getTranslation();
uint32_t numCreatures;
if(!Creatures->Start(numCreatures))
{
cerr << "Can't get creatures" << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
Materials->ReadCreatureTypes();
Materials->ReadCreatureTypesEx();
mem = DF->getMemoryInfo();
DFHack::Process *p = DF->getProcess();
if(!Tran->Start())
{
cerr << "Can't get name tables" << endl;
return 1;
}
DFHack::OffsetGroup *ogc = mem->getGroup("Creatures")->getGroup("creature");
uint32_t o_flags3 = ogc->getOffset("flags3");
uint32_t o_c_hfid = ogc->getGroup("advanced")->getOffset("hist_figure_id");
std::list<uint32_t> goblins;
std::list<uint32_t> ghosts;
for(uint32_t i = 0; i < numCreatures; i++)
{
DFHack::t_creature temp;
Creatures->ReadCreature(i,temp);
int32_t hfid = p->readDWord(temp.origin + o_c_hfid);
if (hfid > 0) {
if (temp.flags1.bits.dead) {
std::string name = Materials->raceEx[temp.race].rawname;
if (name == "GOBLIN")
goblins.push_back(i);
}
} else {
uint32_t flags3 = p->readDWord(temp.origin + o_flags3);
if (!(flags3 & 0x1000))
continue;
ghosts.push_back(i);
}
}
if (goblins.size() >= ghosts.size() && ghosts.size() > 0)
{
DFHack::OffsetGroup *grp_figures = mem->getGroup("Legends")->getGroup("figures");
uint32_t f_vector = p->readDWord(grp_figures->getAddress("vector"));
uint32_t f_id = grp_figures->getOffset("figure_id");
uint32_t f_unit = grp_figures->getOffset("unit_id");
uint32_t f_name = grp_figures->getOffset("name");
uint32_t f_race = grp_figures->getOffset("race");
uint32_t f_profession = grp_figures->getOffset("profession");
for (std::list<uint32_t>::iterator it = ghosts.begin(); it != ghosts.end(); ++it)
{
int i = *it;
DFHack::t_creature ghost;
Creatures->ReadCreature(i,ghost);
printCreature(DF,ghost);
int igoblin = goblins.front();
goblins.pop_front();
DFHack::t_creature goblin;
Creatures->ReadCreature(igoblin,goblin);
printCreature(DF,goblin);
int32_t hfid = p->readDWord(goblin.origin + o_c_hfid);
uint32_t fptr = p->readDWord(f_vector + 4*hfid);
if (p->readDWord(fptr + f_id) != hfid ||
p->readDWord(fptr + f_unit) != goblin.id ||
p->readWord(fptr + f_race) != goblin.race)
{
cout << "Data structure inconsistency detected, aborting.";
break;
}
if (1) {
p->writeDWord(goblin.origin + o_c_hfid, -1);
p->writeDWord(ghost.origin + o_c_hfid, hfid);
p->writeDWord(fptr + f_unit, ghost.id);
p->writeWord(fptr + f_race, ghost.race);
p->writeWord(fptr + f_profession, ghost.profession);
Creatures->CopyNameTo(ghost, fptr + f_name);
cout << "Pair succesfully patched." << endl << endl;
}
}
}
else
{
cout << "No suitable ghosts, or not enough goblins." << endl;
}
Creatures->Finish();
DF->Detach();
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -1,74 +0,0 @@
// This is a simple bit writer... it marks the whole map as a creature lair, preventing item scatter.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
#include <DFHack.h>
#include <extra/termutil.h>
int main (void)
{
bool temporary_terminal = TemporaryTerminal();
uint32_t x_max,y_max,z_max;
DFHack::occupancies40d occupancies;
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context *DF;
try
{
DF = DFMgr.getSingleContext();
DF->Attach();
}
catch (exception& e)
{
cerr << e.what() << endl;
if(temporary_terminal)
cin.ignore();
return 1;
}
DFHack::Maps *Maps =DF->getMaps();
// init the map
if(!Maps->Start())
{
cerr << "Can't init map." << endl;
if(temporary_terminal)
cin.ignore();
return 1;
}
cout << "Designating, please wait..." << endl;
Maps->getSize(x_max,y_max,z_max);
for(uint32_t x = 0; x< x_max;x++)
{
for(uint32_t y = 0; y< y_max;y++)
{
for(uint32_t z = 0; z< z_max;z++)
{
if(Maps->isValidBlock(x,y,z))
{
// read block designations
Maps->ReadOccupancy(x,y,z, &occupancies);
//Maps->ReadTileTypes(x,y,z, &tiles);
// change the monster_lair flag to 1
for (uint32_t i = 0; i < 16;i++) for (uint32_t j = 0; j < 16;j++)
{
// add tile type chack here
occupancies[i][j].bits.monster_lair = 1;
}
// write the designations back
Maps->WriteOccupancy(x,y,z, &occupancies);
}
}
}
}
if(temporary_terminal)
{
cout << "The map has been marked as a creature lair. Items shouldn't scatter." << endl;
cin.ignore();
}
return 0;
}

@ -1,93 +0,0 @@
import time
from context import ContextManager
class HideBlock(object):
__slots__ = [ "x", "y", "z", "hiddens" ]
def __init__(self, *args, **kwargs):
self.x = 0
self.y = 0
self.z = 0
self.hiddens = [[0 for i in xrange(16)] for j in xrange(16)]
df_cm = ContextManager("Memory.xml")
df = df_cm.get_single_context()
df.attach()
m = df.maps
w = df.world
print "Pausing..."
w.start()
#this mimics the hack in the C++ reveal tool that attempts to ensure that DF isn't in the middle of
#a frame update
w.set_pause_state(True)
df.resume()
time.sleep(1)
df.suspend()
w.finish()
m.start()
print "Revealing, please wait..."
m_x, m_y, m_z = m.size
hide_blocks = []
for x in xrange(m_x):
for y in xrange(m_y):
for z in xrange(m_z):
if m.is_valid_block(x, y, z):
hb = HideBlock()
hb.x = x
hb.y = y
hb.z = z
d = m.read_designations(x, y, z)
for k_i, i in enumerate(d):
for k_j, j in enumerate(i):
hb.hiddens[k_i][k_j] = j.bits.hidden
j.bits.hidden = 0
hide_blocks.append(hb)
m.write_designations(x, y, z, d)
m.finish()
df.detach()
print "Map revealed. The game has been paused for you."
print "Unpausing can unleash the forces of hell!"
print "Press any key to unreveal."
print "Close to keep the map revealed !!FOREVER!!"
raw_input()
print "Unrevealing...please wait"
df.attach()
m = df.maps
m.start()
for h in hide_blocks:
d = m.read_designations(h.x, h.y, h.z)
for k_i, i in enumerate(h.hiddens):
for k_j, j in enumerate(i):
d[k_i][k_j].bits.hidden = j
m.write_designations(h.x, h.y, h.z, d)
m.finish()
print "Done. Press any key to continue"
raw_input()
df.detach()

@ -1,52 +0,0 @@
from context import Context, ContextManager
cm = ContextManager("Memory.xml")
df = cm.get_single_context()
df.attach()
gui = df.gui
veg = df.vegetation
mps = df.maps
mat = df.materials
x, y, z = gui.get_cursor_coords()
num_veg = veg.start()
if x == -30000:
print "----==== Trees ====----"
for i in xrange(num_veg):
t = veg.read(i)
print "%d/%d/%d, %d:%d" % (t.x, t.y, t.z, t.type, t.material)
else:
#new method, gets the list of trees in a block. can show farm plants
if mps.start():
pos_tuple = (x, y, z)
trees = mps.read_vegetation(x / 16, y / 16, z)
if trees is not None:
for t in trees:
if (t.x, t.y, t.z) == pos_tuple:
print "----==== Tree at %d/%d/%d ====----" % pos_tuple
print str(t)
break
mps.finish()
#old method, get the tree from the global vegetation vector. can't show farm plants
for i in xrange(num_veg):
t = veg.read(i)
if (t.x, t.y, t.z) == pos_tuple:
print "----==== Tree at %d/%d/%d ====----" % pos_tuple
print str(t)
break
veg.finish()
df.detach()
print "Done. Press any key to continue"
raw_input()

File diff suppressed because it is too large Load Diff

@ -1,229 +0,0 @@
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <map>
#include <cstdlib>
#include <limits>
using namespace std;
#include <conio.h>
#include <DFHack.h>
#include <DFTileTypes.h>
#include <extra/MapExtras.h>
//Globals
DFHack::Context *DF;
DFHack::Maps *maps;
DFHack::Gui *gui;
DFHack::Materials *mats;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main (void)
{
int32_t
cx,cy,z, //cursor coords
tx,ty, //tile coords within block
bx,by; //block coords
//DFHack::designations40d designations;
//DFHack::tiletypes40d tiles;
//DFHack::t_temperatures temp1,temp2;
uint32_t x_max,y_max,z_max;
DFHack::ContextManager DFMgr("Memory.xml");
try
{
DF=DFMgr.getSingleContext();
DF->Attach();
maps = DF->getMaps();
maps->Start();
maps->getSize(x_max,y_max,z_max);
gui = DF->getGui();
mats = DF->getMaterials();
mats->ReadAllMaterials();
if(!mats->ReadInorganicMaterials())
{
printf("Error: Could not load materials!\n");
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
}
catch (exception& e)
{
cerr << e.what() << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
bool end = false;
cout << "Welcome to the Vein Swap tool.\nType 'help' or ? for a list of available commands, 'q' to quit" << endl;
string mode = "";
string command = "";
while(!end)
{
DF->Resume();
cout << endl << ":";
getline(cin, command);
int ch = command[0];
if(command.length()<=0) ch=0;
if( ((int)command.find("help")) >=0 ) ch='?'; //under windows, find was casting unsigned!
//Process user command.
switch(ch)
{
case 'h':
case '?':
cout << "" << endl
<< "Commands:" << endl
<< "p - print vein at cursor" << endl
<< "m - print all inorganic material types" << endl
<< "r MatTo - replace the vein at cursor with MatTo" << endl
<< "R Percent MatFrom MatTo - replace Percent of MatFrom veins with MatTo veins" << endl
<< "help OR ? - print this list of commands" << endl
<< "q - quit" << endl
<< endl
<< "Usage:\n\t" << endl;
break;
case 'p':
case 10:
case 13:
case 0:
{
//Print current cursor
mats->ReadAllMaterials();
if(!maps->Start())
{
cout << "Can't see any DF map loaded." << endl;
break;
}
if(!gui->getCursorCoords(cx,cy,z))
{
cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
}
//cout << "cursor coords: " << x << "/" << y << "/" << z << endl;
tx=cx%16; ty=cy%16;
bx=cx/16; by=cy/16;
DFHack::DFCoord xyz(cx,cy,z);
printf("Cursor[%d,%d,%d] Block(%d,%d) Tile(%d,%d)\n", cx,cy,z, bx,by, tx,ty );
if(!maps->isValidBlock(bx,by,z))
{
cout << "Invalid block." << endl;
break;
}
vector<DFHack::t_vein> veinVector;
int found=0;
maps->ReadVeins(bx,by,z,&veinVector);
printf("Veins in block (%d):\n",veinVector.size());
for(unsigned long i=0;i<veinVector.size();++i){
found = veinVector[i].getassignment(tx,ty);
printf("\t%c %4d %s\n",
(found ? '*' : ' '),
veinVector[i].type,
mats->inorganic[veinVector[i].type].id
);
}
printf("Cursor is%s in vein.\n", (found?"":" not") );
maps->Finish();
DF->Resume();
}
break;
case 'v':
break;
case 'm':
break;
case 'R':
break;
case 'q':
end = true;
cout << "Bye!" << endl;
break;
case 'r':
DF->Suspend();
do{
//Process parameters
long matto = atol( command.c_str()+1 );
if( matto < 0 || matto >= (long)mats->inorganic.size() ){
cout << "Invalid material: " << matto << endl;
break;
}
if(!maps->Start())
{
cout << "Can't see any DF map loaded." << endl;
break;
}
if(!gui->getCursorCoords(cx,cy,z))
{
cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
}
tx=cx%16; ty=cy%16;
bx=cx/16; by=cy/16;
printf("Cursor[%d,%d,%d] Block(%d,%d) Tile(%d,%d)\n", cx,cy,z, bx,by, tx,ty );
if(!maps->isValidBlock(bx,by,z))
{
cout << "Invalid block." << endl;
break;
}
//MapExtras::MapCache MC(maps);
//MapExtras::Block B(maps,DFHack::DFCoord(bx,by,z));
vector<DFHack::t_vein> veinVector;
int v=-1; //the vector pointed to by the cursor
mats->ReadAllMaterials();
maps->ReadVeins(bx,by,z,&veinVector);
for(unsigned long i=0 ; v<0 && i<veinVector.size() ; ++i){
if( veinVector[i].getassignment(tx,ty) )
v=i;
}
printf("Replacing %d %s with %d %s...\n", veinVector[v].type, mats->inorganic[veinVector[v].type].id, matto, mats->inorganic[matto].id );
printf("%X\n",veinVector[v].vtable);
vector<DFHack::t_vein> veinTable;
veinVector[v].type = matto;
maps->WriteVein( &veinVector[v] );
maps->Finish();
cout << endl << "Finished." << endl;
}while(0);
DF->Resume();
break;
default:
cout << "Unknown command: " << command << endl;
}//end switch
}//end while
DF->Detach();
//#ifndef LINUX_BUILD
//cout << "Done. Press any key to continue" << endl;
//cin.ignore();
//#endif
return 0;
}

@ -3,12 +3,15 @@ PWD=`dirname "${0}"`
#thanks to Iriel for figuring this out
OSREV=`uname -r | cut -d. -f1`
if [ "$OSREV" -ge 11 ] ; then
export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib
export DYLD_LIBRARY_PATH=${PWD}/hack:${PWD}/libs
export DYLD_FRAMEWORK_PATH=${PWD}/hack:${PWD}/libs
else
export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib
export DYLD_FALLBACK_LIBRARY_PATH=${PWD}/hack:${PWD}/libs
export DYLD_FALLBACK_FRAMEWORK_PATH=${PWD}/hack:${PWD}/libs
fi
cd "${PWD}"; ./dwarfort.exe
old_tty_settings=$(stty -g)
cd "${PWD}"
DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe "$@" 2>&1 | tee dfhack.log
stty "$old_tty_settings"
echo ""

@ -3,6 +3,6 @@
DF_DIR=$(dirname "$0")
cd "${DF_DIR}"
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./stonesense/deplibs":"./hack"
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:"./stonesense/deplibs":"./hack"
exec hack/dfhack-run "$@"

@ -65,14 +65,14 @@ case "$1" in
ret=$?
;;
*)
setarch i386 -R env LD_PRELOAD=$PRELOAD_LIB ./libs/Dwarf_Fortress "$@"
setarch i386 -R env LD_PRELOAD=$PRELOAD_LIB ./libs/Dwarf_Fortress "$@" 2>&1 | tee dfhack.log
ret=$?
;;
esac
# Restore previous terminal settings
stty "$old_tty_settings"
echo -e "\n"
echo
if [ -n "$DF_POST_CMD" ]; then
eval $DF_POST_CMD

@ -1,142 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x2ac6b
CC CC CC CC CC
66 39 E8 EB 53
.text:0042B86B loc_42B86B:
.text:0042B86B cmp ax, bp
.text:0042B86E jmp short loc_42B8C3
0x2ac7b
CC CC CC CC CC
E9 96 A2 00 00
.text:0042B87B loc_42B87B:
.text:0042B87B jmp loc_435B16
0x2acc3
CC CC CC CC CC CC CC CC CC CC CC CC CC
75 0A 66 FF 4C 24 16 79 03 58 EB AC C3
.text:0042B8C3 loc_42B8C3:
.text:0042B8C3 jnz short locret_42B8CF
.text:0042B8C5 dec word ptr [esp+16h] ; 4+8+8+2
.text:0042B8CA jns short locret_42B8CF
.text:0042B8CC pop eax
.text:0042B8CD jmp short loc_42B87B
.text:0042B8CF locret_42B8CF:
.text:0042B8CF retn
0x2b2a1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
66 C7 44 24 0E 01 00 8B 90 44 01 00 00 C3 CC
.text:0042BEA1 loc_42BEA1:
.text:0042BEA1 mov word ptr [esp+0Eh], 1 ; 4+8+2
.text:0042BEA8 mov edx, [eax+144h]
.text:0042BEAE retn
0x34d91
8B 90 44 01 00 00
E8 0B 65 FF FF 90
<<<<
.text:00435991 mov edx, [eax+144h]
====
.text:00435991 call loc_42BEA1
.text:00435996 nop
>>>>
0x34e53
0F 84 BD 00 00 00
E8 6B 5E FF FF 90
<<<<
.text:00435A53 jz loc_435B16
====
.text:00435A53 call loc_42B8C3
.text:00435A58 nop
>>>>
0x34ef3
66 3B C5 74 1E
E8 73 5D FF FF
<<<<
.text:00435AF3 cmp ax, bp
.text:00435AF6 jz short loc_435B16
====
.text:00435AF3 call loc_42B86B
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC6B: CC 66
0002AC6C: CC 39
0002AC6D: CC E8
0002AC6E: CC EB
0002AC6F: CC 53
0002AC7B: CC E9
0002AC7C: CC 96
0002AC7D: CC A2
0002AC7E: CC 00
0002AC7F: CC 00
0002ACC3: CC 75
0002ACC4: CC 0A
0002ACC5: CC 66
0002ACC6: CC FF
0002ACC7: CC 4C
0002ACC8: CC 24
0002ACC9: CC 16
0002ACCA: CC 79
0002ACCB: CC 03
0002ACCC: CC 58
0002ACCD: CC EB
0002ACCE: CC AC
0002ACCF: CC C3
0002B2A1: CC 66
0002B2A2: CC C7
0002B2A3: CC 44
0002B2A4: CC 24
0002B2A5: CC 0E
0002B2A6: CC 01
0002B2A7: CC 00
0002B2A8: CC 8B
0002B2A9: CC 90
0002B2AA: CC 44
0002B2AB: CC 01
0002B2AC: CC 00
0002B2AD: CC 00
0002B2AE: CC C3
00034D91: 8B E8
00034D92: 90 0B
00034D93: 44 65
00034D94: 01 FF
00034D95: 00 FF
00034D96: 00 90
00034E53: 0F E8
00034E54: 84 6B
00034E55: BD 5E
00034E56: 00 FF
00034E57: 00 FF
00034E58: 00 90
00034EF3: 66 E8
00034EF4: 3B 73
00034EF5: C5 5D
00034EF6: 74 FF
00034EF7: 1E FF

@ -1,91 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
Original code:
.text:00916BCE mov edi, ebp
.text:00916BD0 call eax
.text:00916BD2 test eax, eax
.text:00916BD4 jnz short loc_916C1C
.text:00916C0A mov edi, ebp
.text:00916C14 mov edi, ebp
Patch:
0x2ac34:
CC CC CC CC CC CC CC CC CC CC CC CC
8B 7C 24 78 8B 3C B7 FF D0 EB 25 CC
.text:0042B834 loc_42B834:
.text:0042B834 mov edi, [esp+78h]
.text:0042B838 mov edi, [edi+esi*4]
.text:0042B83B call eax
.text:0042B83D jmp short unk_42B864
0x2ac64
CC CC CC CC CC CC CC CC CC CC CC CC
85 C0 E9 69 B3 4E 00 CC CC CC CC CC
.text:0042B864 loc_42B864:
.text:0042B864 test eax, eax
.text:0042B866 jmp loc_916BD4
0x515fce
8B FD FF D0 85 C0
E9 61 4C B1 FF 90
.text:00916BCE jmp loc_42B834
.text:00916BD3 nop
.text:00916BD4 loc_916BD4:
0x51600a
8B FD
90 90
.text:00916C0A nop
.text:00916C0B nop
0x516014
8B FD
90 90
.text:00916C14 nop
.text:00916C15 nop
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC34: CC 8B
0002AC35: CC 7C
0002AC36: CC 24
0002AC37: CC 78
0002AC38: CC 8B
0002AC39: CC 3C
0002AC3A: CC B7
0002AC3B: CC FF
0002AC3C: CC D0
0002AC3D: CC EB
0002AC3E: CC 25
0002AC64: CC 85
0002AC65: CC C0
0002AC66: CC E9
0002AC67: CC 69
0002AC68: CC B3
0002AC69: CC 4E
0002AC6A: CC 00
00515FCE: 8B E9
00515FCF: FD 61
00515FD0: FF 4C
00515FD1: D0 B1
00515FD2: 85 FF
00515FD3: C0 90
0051600A: 8B 90
0051600B: FD 90
00516014: 8B 90
00516015: FD 90

@ -1,61 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:008629BD mov edi, [eax+38h]
.text:008629C0 mov eax, [eax+3Ch]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp edi, eax
.text:008629C9 jnb short loc_862A22
.text:008629CB jmp short loc_8629D0
.text:008629CD lea ecx, [ecx+0]
...
.text:00862A19 add edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jb short loc_8629D0
Patch:
0x461dbd
8B 78 38 8B 40 3C 89 44 24 1C 3B F8
8B 78 3C 8B 40 38 89 44 24 1C 39 F8
.text:008629BD mov edi, [eax+3Ch]
.text:008629C0 mov eax, [eax+38h]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp eax, edi
0x461dcb
EB 03 8D 49 00
83 EF 04 90 90
.text:008629CB sub edi, 4
.text:008629CE nop
.text:008629CF nop
0x461e19
83 C7 04 3B 7C 24 1C 72 AE
83 EF 04 3B 7C 24 1C 73 AE
.text:00862A19 sub edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jnb short loc_8629D0
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00461DBF: 38 3C
00461DC2: 3C 38
00461DC7: 3B 39
00461DCB: EB 83
00461DCC: 03 EF
00461DCD: 8D 04
00461DCE: 49 90
00461DCF: 00 90
00461E1A: C7 EF
00461E20: 72 73

@ -1,104 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x461de2
F6 46 0C 01 74 31
E9 0A 8E BC FF 90
.text:008629E2 jmp near ptr loc_42B7F1 ; << CAVE
.text:008629E7 nop
.text:008629E8 loc_8629E8:
0x2abf1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 4C 24 2C F6 46 0C 01 75 08 E9 19 72 43 00
.text:0042B7F1 loc_42B7F1:
.text:0042B7F1 mov ecx, [esp+2Ch] ; job
.text:0042B7F5 test byte ptr [esi+0Ch], 1
.text:0042B7F9 jnz short near ptr loc_42B803
.text:0042B7FB coord_test_jfail:
.text:0042B7FB jmp loc_862A19
0x2ac03
CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 41 10 3B 46 04 75 F0 EB 06 CC CC CC
.text:0042B803 loc_42B803:
.text:0042B803 mov eax, [ecx+10h] ; job->pos.(x,y)
.text:0042B806 cmp eax, [esi+4] ; item->pos.(x,y)
.text:0042B809 jnz short coord_test_jfail
.text:0042B80B jmp short near ptr loc_42B813
0x2ac13
CC CC CC CC CC CC CC CC CC CC CC CC CC
66 8B 41 14 66 3B 46 08 75 DE EB 06 CC
text:0042B813 loc_42B813:
.text:0042B813 mov ax, [ecx+14h] ; job->pos.z
.text:0042B817 cmp ax, [esi+8] ; item->pos.z
.text:0042B81B jnz short coord_test_jfail
.text:0042B81D jmp short near ptr loc_42B825
0x2ac25
CC CC CC CC CC CC CC CC CC CC CC
E9 BE 71 43 00 CC CC CC CC CC CC
.text:0042B825 loc_42B825:
.text:0042B825 jmp loc_8629E8
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002ABF1: CC 8B
0002ABF2: CC 4C
0002ABF3: CC 24
0002ABF4: CC 2C
0002ABF5: CC F6
0002ABF6: CC 46
0002ABF7: CC 0C
0002ABF8: CC 01
0002ABF9: CC 75
0002ABFA: CC 08
0002ABFB: CC E9
0002ABFC: CC 19
0002ABFD: CC 72
0002ABFE: CC 43
0002ABFF: CC 00
0002AC03: CC 8B
0002AC04: CC 41
0002AC05: CC 10
0002AC06: CC 3B
0002AC07: CC 46
0002AC08: CC 04
0002AC09: CC 75
0002AC0A: CC F0
0002AC0B: CC EB
0002AC0C: CC 06
0002AC13: CC 66
0002AC14: CC 8B
0002AC15: CC 41
0002AC16: CC 14
0002AC17: CC 66
0002AC18: CC 3B
0002AC19: CC 46
0002AC1A: CC 08
0002AC1B: CC 75
0002AC1C: CC DE
0002AC1D: CC EB
0002AC1E: CC 06
0002AC25: CC E9
0002AC26: CC BE
0002AC27: CC 71
0002AC28: CC 43
0002AC29: CC 00
00461DE2: F6 E9
00461DE3: 46 0A
00461DE4: 0C 8E
00461DE5: 01 BC
00461DE6: 74 FF
00461DE7: 31 90

@ -1,62 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x68a63
0F 85 58 01 00 00
90 90 90 90 90 90
<<<<
.text:00469663 jnz loc_4697C1
====
.text:00469663 nop
.text:00469664 nop
.text:00469665 nop
.text:00469666 nop
.text:00469667 nop
.text:00469668 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x3dcbf9
8B 0C 90 8B 81 80 00 00 00
8B 3C 90 8B 87 80 00 00 00
<<<<
.text:007DD7F9 mov ecx, [eax+edx*4]
.text:007DD7FC mov eax, [ecx+80h]
====
.text:007DD7F9 mov edi, [eax+edx*4]
.text:007DD7FC mov eax, [edi+80h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
00068A63: 0F 90
00068A64: 85 90
00068A65: 58 90
00068A66: 01 90
00068A67: 00 90
00068A68: 00 90
003DCBFA: 0C 3C
003DCBFD: 81 87

@ -1,83 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530
0x2ac85
CC CC CC CC CC CC CC CC CC CC CC
29 44 24 24 8B 9D 28 01 00 00 C3
.text:0042B885 loc_42B885:
.text:0042B885 sub [esp+24h], eax
.text:0042B889 mov ebx, [ebp+128h]
.text:0042B88F retn
0x2ac94
CC CC CC CC CC CC CC CC CC CC CC CC
89 C1 8B 00 FF 90 34 02 00 00 EB E5
.text:0042B894 loc_42B894:
.text:0042B894 mov ecx, eax
.text:0042B896 mov eax, [eax]
.text:0042B898 call dword ptr [eax+234h]
.text:0042B89E jmp short loc_42B885
0x6e28ff
29 44 24 20
90 90 90 90
<<<<
.text:00AE34FF sub [esp+20h], eax
====
.text:00AE34FF nop
.text:00AE3500 nop
.text:00AE3501 nop
.text:00AE3502 nop
>>>>
0x6e2999
8B 9D 28 01 00 00
E8 F6 82 94 FF 90
<<<<
.text:00AE3599 mov ebx, [ebp+128h]
====
.text:00AE3599 call loc_42B894
.text:00AE359E nop
>>>>
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC85: CC 29
0002AC86: CC 44
0002AC87: CC 24
0002AC88: CC 24
0002AC89: CC 8B
0002AC8A: CC 9D
0002AC8B: CC 28
0002AC8C: CC 01
0002AC8D: CC 00
0002AC8E: CC 00
0002AC8F: CC C3
0002AC94: CC 89
0002AC95: CC C1
0002AC96: CC 8B
0002AC97: CC 00
0002AC98: CC FF
0002AC99: CC 90
0002AC9A: CC 34
0002AC9B: CC 02
0002AC9C: CC 00
0002AC9D: CC 00
0002AC9E: CC EB
0002AC9F: CC E5
006E28FF: 29 90
006E2900: 44 90
006E2901: 24 90
006E2902: 20 90
006E2999: 8B E8
006E299A: 9D F6
006E299B: 28 82
006E299C: 01 94
006E299D: 00 FF
006E299E: 00 90

@ -1,61 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x4c05c4
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C11C4 mov ecx, [esp+98h+var_18]
====
.text:008C11C4 mov ecx, eax
.text:008C11C6 nop
.text:008C11C7 nop
.text:008C11C8 nop
.text:008C11C9 nop
.text:008C11CA nop
>>>>
0x4c06a1
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C12A1 mov ecx, [esp+98h+var_18]
====
.text:008C12A1 mov ecx, eax
.text:008C12A3 nop
.text:008C12A4 nop
.text:008C12A5 nop
.text:008C12A6 nop
.text:008C12A7 nop
>>>>
basically:
b_squad_id = building->getSpecificSquad();
- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4))
+ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4))
unassign(building);
the reason being, some_other_squad_id contains irrelevant garbage at this point
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
004C05C4: 8B 89
004C05C5: 8C C1
004C05C6: 24 90
004C05C7: 80 90
004C05C8: 00 90
004C05C9: 00 90
004C05CA: 00 90
004C06A1: 8B 89
004C06A2: 8C C1
004C06A3: 24 90
004C06A4: 80 90
004C06A5: 00 90
004C06A6: 00 90
004C06A7: 00 90

@ -1,147 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x9461
90 90 90 90 90 90 90 90 90 90 90 90 90 90
C7 44 24 18 01 00 00 00 FF A0 44 01 00 00
.text:08051461 sub_8051461 proc near
.text:08051461 mov dword ptr [esp+18h], 1
.text:08051469 jmp dword ptr [eax+144h]
.text:08051469 sub_8051461 endp
0x9548
90 90 90 90 90 90 90 90
FF 4C 24 14 78 08 EB 0B
.text:08051548 loc_8051548:
.text:08051548 dec dword ptr [esp+14h]
.text:0805154C js short loc_8051556
.text:0805154E jmp short loc_805155B
0x9556
90 90 90 90 90 90 90 90 90 90
E9 F6 8C 05 00 E9 D0 8D 05 00
.text:08051556 loc_8051556:
.text:08051556 jmp loc_80AA251
.text:0805155B loc_805155B:
.text:0805155B jmp loc_80AA330
0x9568
90 90 90 90 90 90 90 90
FF 4C 24 14 78 E8 EB 06
.text:08051568 loc_8051568:
.text:08051568 dec [esp+14h]
.text:0805156C js short loc_8051556
.text:0805156E jmp short loc_8051576
0x9576
90 90 90 90 90
E9 4D 8E 05 00
.text:08051576 loc_8051576:
.text:08051576 jmp loc_80AA3C8
0x62243
FF 90 44 01 00 00
E8 19 72 FA FF 90
<<<<
.text:080AA243 call dword ptr [eax+144h]
====
.text:080AA243 call sub_8051461
.text:080AA248 nop
>>>>
0x62369
E9 E3 FE FF FF
E9 DA 71 FA FF
<<<<
.text:080AA369 jmp loc_80AA251
====
.text:080AA369 jmp loc_8051548
>>>>
0x623f6
E9 56 FE FF FF
E9 6D 71 FA FF
<<<<
.text:080AA3F6 jmp loc_80AA251
====
.text:080AA3F6 jmp loc_8051568
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009461: 90 C7
00009462: 90 44
00009463: 90 24
00009464: 90 18
00009465: 90 01
00009466: 90 00
00009467: 90 00
00009468: 90 00
00009469: 90 FF
0000946A: 90 A0
0000946B: 90 44
0000946C: 90 01
0000946D: 90 00
0000946E: 90 00
00009548: 90 FF
00009549: 90 4C
0000954A: 90 24
0000954B: 90 14
0000954C: 90 78
0000954D: 90 08
0000954E: 90 EB
0000954F: 90 0B
00009556: 90 E9
00009557: 90 F6
00009558: 90 8C
00009559: 90 05
0000955A: 90 00
0000955B: 90 E9
0000955C: 90 D0
0000955D: 90 8D
0000955E: 90 05
0000955F: 90 00
00009568: 90 FF
00009569: 90 4C
0000956A: 90 24
0000956B: 90 14
0000956C: 90 78
0000956D: 90 E8
0000956E: 90 EB
0000956F: 90 06
00009576: 90 E9
00009577: 90 4D
00009578: 90 8E
00009579: 90 05
0000957A: 90 00
00062243: FF E8
00062244: 90 19
00062245: 44 72
00062246: 01 FA
00062247: 00 FF
00062248: 00 90
0006236A: E3 DA
0006236B: FE 71
0006236C: FF FA
000623F7: 56 6D
000623F8: FE 71
000623F9: FF FA

@ -1,40 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
for (i = 0; i < num_items; i++)
{
ridx = reagent_idx[i];
sz = reagent_quantity[ridx]; // used quantity
if (sz <= 0) continue;
reag = reagent[ridx];
if (reag->flags.PRESERVE_REAGENT) continue;
rsz = items[i]->getTotalDimension();
<<<<<<<<
if (reag->flags3.ANY_RAW_MATERIAL)
rsz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(rsz))
========
/* Not in patch, but necessary for full correctness:
if (reag->flags3.ANY_RAW_MATERIAL)
rsz /= BASE_SIZE(items[i]->getType());
*/
if (reag->flags3.ANY_RAW_MATERIAL)
sz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(sz))
>>>>>>>>
destroy_item(items[i]);
reagent_quantity[ridx] -= rsz
if (reagent_quantity[ridx] < 0)
reagent_quantity[ridx] = 0;
}
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
0087F7EF: F8 D8
0087F86F: F8 D8
0087F9AD: C7 C3
0087F9ED: C7 C3

@ -1,83 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:087AC378 cmp edx, eax
.text:087AC37A mov [esp+4Ch], eax
.text:087AC37E jnb loc_87A7034
.text:087AC384 mov [esp+48h], edx
.text:087AC388 mov [esp+54h], ebx
...
.text:087AC440 add dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D ja loc_87AC38C
Patch:
0x76437a
89 44 24 4C
89 54 24 4C
.text:087AC37A mov [esp+4Ch], edx
0x764384
89 54 24 48 89 5C 24 54
E8 8A 51 8A FF 90 90 90
.text:087AC384 call sub_8051513
.text:087AC389 nop
.text:087AC38A nop
.text:087AC38B nop
0x764440
83 44 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 87 39 FF FF FF
83 6C 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 86 39 FF FF FF
.text:087AC440 sub dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D jbe loc_87AC38C
0x9513
90 90 90 90 90 90 90 90 90 90 90 90 90
83 E8 04 89 44 24 4C 89 5C 24 58 C3 90
.text:08051513 sub_8051513 proc near
.text:08051513 sub eax, 4
.text:08051516 mov [esp+4Ch], eax ; 48h
.text:0805151A mov [esp+58h], ebx ; 54h
.text:0805151E retn
.text:0805151E sub_8051513 endp
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009513: 90 83
00009514: 90 E8
00009515: 90 04
00009516: 90 89
00009517: 90 44
00009518: 90 24
00009519: 90 4C
0000951A: 90 89
0000951B: 90 5C
0000951C: 90 24
0000951D: 90 58
0000951E: 90 C3
0076437B: 44 54
00764384: 89 E8
00764385: 54 8A
00764386: 24 51
00764387: 48 8A
00764388: 89 FF
00764389: 5C 90
0076438A: 24 90
0076438B: 54 90
00764441: 44 6C
0076444E: 87 86

@ -1,139 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x7643f8
F6 46 0C 01 74 42
E9 B6 50 8A FF 90
.text:087AC3F8 jmp loc_80514B3 ; << CAVE
.text:087AC3FD nop
.text:087AC3FE loc_87AC3FE:
0x94b3
90 90 90 90 90 90 90 90 90 90 90 90 90
F6 46 0C 01 75 0A E9 82 AF 75 00 90 90
.text:080514B3 loc_80514B3:
.text:080514B3 test byte ptr [esi+0Ch], 1
.text:080514B7 jnz short loc_80514C3
.text:080514B9 coord_test_jfail:
.text:080514B9 jmp loc_87AC440
0x94c3
90 90 90 90 90 90 90 90 90 90 90 90 90
8D 9C 24 60 03 00 00 0F BF 03 EB 07 90
.text:080514C3 loc_80514C3:
.text:080514C3 lea ebx, [esp+360h]
.text:080514CA movsx eax, word ptr [ebx] ; job_z
.text:080514CD jmp short loc_80514D6
0x94d6
90 90 90 90 90 90 90 90 90 90
66 3B 46 08 75 DD EB 05 90 90
.text:080514D6 loc_80514D6:
.text:080514D6 cmp ax, [esi+8] ; item->pos.z
.text:080514DA jnz short coord_test_jfail
.text:080514DC jmp short loc_80514E3
0x94e3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 10 66 3B 46 04 75 CC EB 04 90
.text:080514E3 loc_80514E3:
.text:080514E3 movsx eax, word ptr [ebx+10h] ; job_x
.text:080514E7 cmp ax, [esi+4] ; item->pos.x
.text:080514EB jnz short coord_test_jfail
.text:080514ED jmp short loc_80514F3
0x94f3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 20 66 3B 46 06 75 BC EB 04 90
.text:080514F3 loc_80514F3:
.text:080514F3 movsx eax, word ptr [ebx+20h] ; job_y
.text:080514F7 cmp ax, [esi+6] ; item->pos.y
.text:080514FB jnz short coord_test_jfail
.text:080514FD jmp short loc_8051503
0x9503
90 90 90 90 90 90 90 90 90 90 90 90 90
E9 F6 AE 75 00 90 90 90 90 90 90 90 90
.text:08051503 loc_8051503:
.text:08051503 jmp loc_87AC3FE
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000094B3: 90 F6
000094B4: 90 46
000094B5: 90 0C
000094B6: 90 01
000094B7: 90 75
000094B8: 90 0A
000094B9: 90 E9
000094BA: 90 82
000094BB: 90 AF
000094BC: 90 75
000094BD: 90 00
000094C3: 90 8D
000094C4: 90 9C
000094C5: 90 24
000094C6: 90 60
000094C7: 90 03
000094C8: 90 00
000094C9: 90 00
000094CA: 90 0F
000094CB: 90 BF
000094CC: 90 03
000094CD: 90 EB
000094CE: 90 07
000094D6: 90 66
000094D7: 90 3B
000094D8: 90 46
000094D9: 90 08
000094DA: 90 75
000094DB: 90 DD
000094DC: 90 EB
000094DD: 90 05
000094E3: 90 0F
000094E4: 90 BF
000094E5: 90 43
000094E6: 90 10
000094E7: 90 66
000094E8: 90 3B
000094E9: 90 46
000094EA: 90 04
000094EB: 90 75
000094EC: 90 CC
000094ED: 90 EB
000094EE: 90 04
000094F3: 90 0F
000094F4: 90 BF
000094F5: 90 43
000094F6: 90 20
000094F7: 90 66
000094F8: 90 3B
000094F9: 90 46
000094FA: 90 06
000094FB: 90 75
000094FC: 90 BC
000094FD: 90 EB
000094FE: 90 04
00009503: 90 E9
00009504: 90 F6
00009505: 90 AE
00009506: 90 75
00009507: 90 00
007643F8: F6 E9
007643F9: 46 B6
007643FA: 0C 50
007643FB: 01 8A
007643FC: 74 FF
007643FD: 42 90

@ -1,60 +0,0 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x746d7
75 D7
90 90
<<<<
.text:080BC6D7 jnz short loc_80BC6B0
====
.text:080BC6D7 nop
.text:080BC6D8 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x67cb0e
0B 04 90
8B 1C 90
0x67cb18
8B 40 74
8B 43 74
<<<<
.text:086C4B0E mov eax, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [eax+74h]
====
.text:086C4B0E mov ebx, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [ebx+74h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000746D7: 75 90
000746D8: D7 90
0067CB0F: 04 1C
0067CB19: 40 43

Some files were not shown because too many files have changed in this diff Show More