ruby: merge upstream

develop
jj 2012-04-17 14:40:59 +02:00
commit d901dd28cf
92 changed files with 9022 additions and 2270 deletions

@ -14,6 +14,11 @@ endif(CMAKE_CONFIGURATION_TYPES)
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(dfhack)
if(MSVC)
# disable C4819 code-page warning
add_definitions( "/wd4819" )
endif()
# set up folder structures for IDE solutions
# MSVC Express won't load solutions that use this. It also doesn't include MFC supported
# Check for MFC!
@ -58,9 +63,9 @@ set(DF_VERSION_MINOR "34")
set(DF_VERSION_PATCH "07")
set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}")
set(DFHACK_RELEASE "1")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-r${DFHACK_RELEASE}")
set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
## where to install things (after the build is done, classic 'make install' or package structure)

@ -113,3 +113,27 @@ 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.
--------------------------------------------------------------------------
See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
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.

@ -68,6 +68,11 @@ All typed objects have the following built-in features:
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.
**WARNING**: a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.
* ``ref._kind``
Returns one of: ``primitive``, ``struct``, ``container``,
@ -456,6 +461,31 @@ Currently it defines the following features:
to group operations together in one big critical section. A plugin
can choose to run all lua code inside a C++-side suspend lock.
* ``dfhack.call_with_finalizer(num_cleanup_args,always,cleanup_fn[,cleanup_args...],fn[,args...])``
Invokes ``fn`` with ``args``, and after it returns or throws an
error calls ``cleanup_fn`` with ``cleanup_args``. Any return values from
``fn`` are propagated, and errors are re-thrown.
The ``num_cleanup_args`` integer specifies the number of ``cleanup_args``,
and the ``always`` boolean specifies if cleanup should be called in any case,
or only in case of an error.
* ``dfhack.with_finalize(cleanup_fn,fn[,args...])``
Calls ``fn`` with arguments, then finalizes with ``cleanup_fn``.
Implemented using ``call_with_finalizer(0,true,...)``.
* ``dfhack.with_onerror(cleanup_fn,fn[,args...])``
Calls ``fn`` with arguments, then finalizes with ``cleanup_fn`` on any thrown error.
Implemented using ``call_with_finalizer(0,false,...)``.
* ``dfhack.with_temp_object(obj,fn[,args...])``
Calls ``fn(obj,args...)``, then finalizes with ``obj:delete()``.
Persistent configuration storage
================================
@ -497,3 +527,289 @@ Since the data is hidden in data structures owned by the DF world,
and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.
Material info lookup
====================
A material info record has fields:
* ``type``, ``index``, ``material``
DF material code pair, and a reference to the material object.
* ``mode``
One of ``'builtin'``, ``'inorganic'``, ``'plant'``, ``'creature'``.
* ``inorganic``, ``plant``, ``creature``
If the material is of the matching type, contains a reference to the raw object.
* ``figure``
For a specific creature material contains a ref to the historical figure.
Functions:
* ``dfhack.matinfo.decode(type,index)``
Looks up material info for the given number pair; if not found, returs *nil*.
* ``....decode(matinfo)``, ``....decode(item)``, ``....decode(obj)``
Uses ``matinfo.type``/``matinfo.index``, item getter vmethods,
or ``obj.mat_type``/``obj.mat_index`` to get the code pair.
* ``dfhack.matinfo.find(token[,token...])``
Looks up material by a token string, or a pre-split string token sequence.
* ``dfhack.matinfo.getToken(...)``, ``info:getToken()``
Applies ``decode`` and constructs a string token.
* ``info:toString([temperature[,named]])``
Returns the human-readable name at the given temperature.
* ``info:getCraftClass()``
Returns the classification used for craft skills.
* ``info:matches(obj)``
Checks if the material matches job_material_category or job_item.
Accept dfhack_material_category auto-assign table.
C++ function wrappers
=====================
Thin wrappers around C++ functions, similar to the ones for virtual methods.
* ``dfhack.TranslateName(name,in_english,only_last_name)``
Convert a language_name or only the last name part to string.
Gui module
----------
* ``dfhack.gui.getSelectedWorkshopJob(silent)``
When a job is selected in *'q'* mode, returns the job, else
prints error unless silent and returns *nil*.
* ``dfhack.gui.getSelectedJob(silent)``
Returns the job selected in a workshop or unit/jobs screen.
* ``dfhack.gui.getSelectedUnit(silent)``
Returns the unit selected via *'v'*, *'k'*, unit/jobs, or
a full-screen item view of a cage or suchlike.
* ``dfhack.gui.getSelectedItem(silent)``
Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or
a full-screen item view of a container. Note that in the
last case, the highlighted *contained item* is returned, not
the container itself.
* ``dfhack.gui.showAnnouncement(text,color,is_bright)``
Adds a regular announcement with given text, color, and brightness.
The is_bright boolean actually seems to invert the brightness.
* ``dfhack.gui.showPopupAnnouncement(text,color,is_bright)``
Pops up a titan-style modal announcement window.
Job module
----------
* ``dfhack.job.cloneJobStruct(job)``
Creates a deep copy of the given job.
* ``dfhack.job.printJobDetails(job)``
Prints info about the job.
* ``dfhack.job.printItemDetails(jobitem,idx)``
Prints info about the job item.
* ``dfhack.job.getHolder(job)``
Returns the building holding the job.
* ``dfhack.job.getWorker(job)``
Returns the unit performing the job.
* ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures.
* ``dfhack.job.is_item_equal(job_item1,job_item2)``
Compares important fields in the job item structures.
* ``dfhack.job.listNewlyCreated(first_id)``
Returns the current value of ``df.global.job_next_id``, and
if there are any jobs with ``first_id <= id < job_next_id``,
a lua list containing them.
Units module
------------
* ``dfhack.units.getPosition(unit)``
Returns true *x,y,z* of the unit; may be not equal to unit.pos if caged.
* ``dfhack.units.getContainer(unit)``
Returns the container (cage) item or *nil*.
* ``dfhack.units.setNickname(unit,nick)``
Sets the unit's nickname properly.
* ``dfhack.units.getVisibleName(unit)``
Returns the language_name object visible in game, accounting for false identities.
* ``dfhack.units.getNemesis(unit)``
Returns the nemesis record of the unit if it has one, or *nil*.
* ``dfhack.units.isDead(unit)``
The unit is completely dead and passive.
* ``dfhack.units.isAlive(unit)``
The unit isn't dead or undead.
* ``dfhack.units.isSane(unit)``
The unit is capable of rational action, i.e. not dead, insane or zombie.
* ``dfhack.units.clearBurrowMembers(burrow)``
Removes all units from the burrow.
* ``dfhack.units.isInBurrow(unit,burrow)``
Checks if the unit is in the burrow.
* ``dfhack.units.setInBurrow(unit,burrow,enable)``
Adds or removes the unit from the burrow.
Items module
------------
* ``dfhack.items.getPosition(item)``
Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory.
* ``dfhack.items.getOwner(item)``
Returns the owner unit or *nil*.
* ``dfhack.items.setOwner(item,unit)``
Replaces the owner of the item. If unit is *nil*, removes ownership.
Returns *false* in case of error.
* ``dfhack.items.getContainer(item)``
Returns the container item or *nil*.
* ``dfhack.items.getContainedItems(item)``
Returns a list of items contained in this one.
* ``dfhack.items.moveToGround(item,pos)``
Move the item to the ground at position. Returns *false* if impossible.
* ``dfhack.items.moveToContainer(item,container)``
Move the item to the container. Returns *false* if impossible.
Maps module
-----------
* ``dfhack.maps.getSize()``
Returns map size in blocks: *x, y, z*
* ``dfhack.maps.getTileSize()``
Returns map size in tiles: *x, y, z*
* ``dfhack.maps.getBlock(x,y,z)``
Returns a map block object for given x,y,z in local block coordinates.
* ``dfhack.maps.getTileBlock(coords)``
Returns a map block object for given df::coord in local tile coordinates.
* ``dfhack.maps.getRegionBiome(region_coord2d)``
Returns the biome info struct for the given global map region.
* ``dfhack.maps.getGlobalInitFeature(index)``
Returns the global feature object with the given index.
* ``dfhack.maps.getLocalInitFeature(region_coord2d,index)``
Returns the local feature object with the given region coords and index.
* ``dfhack.maps.findBurrowByName(name)``
Returns the burrow pointer or *nil*.
* ``dfhack.maps.listBurrowBlocks(burrow)``
Returns a table of map block pointers.
* ``dfhack.maps.clearBurrowTiles(burrow)``
Removes all tiles from the burrow.
* ``dfhack.maps.isBurrowTile(burrow,tile_coord)``
Checks if the tile is in burrow.
* ``dfhack.maps.setBurrowTile(burrow,tile_coord,enable)``
Adds or removes the tile from the burrow. Returns *false* if invalid coords.
* ``dfhack.maps.isBlockBurrowTile(burrow,block,x,y)``
Checks if the tile within the block is in burrow.
* ``dfhack.maps.setBlockBurrowTile(burrow,block,x,y,enable)``
Adds or removes the tile from the burrow. Returns *false* if invalid coords.
Core interpreter context
========================
While plugins can create any number of interpreter instances,
there is one special context managed by dfhack core. It is the
only context that can receive events from DF and plugins.
Core context specific functions:
* ``dfhack.is_core_context``
Boolean value; *true* in the core context.

@ -335,6 +335,16 @@ ul.auto-toc {
</li>
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#material-info-lookup" id="id12">Material info lookup</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id13">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id14">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id15">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id16">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id17">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id18">Maps module</a></li>
</ul>
</li>
<li><a class="reference internal" href="#core-interpreter-context" id="id19">Core interpreter context</a></li>
</ul>
</li>
</ul>
@ -388,6 +398,10 @@ Every structured field access produces a new userdata instance.</p>
<p>Returns an iterator for the sequence of actual C++ field names
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.</p>
<p><strong>WARNING</strong>: a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.</p>
</li>
<li><p class="first"><tt class="docutils literal">ref._kind</tt></p>
<p>Returns one of: <tt class="docutils literal">primitive</tt>, <tt class="docutils literal">struct</tt>, <tt class="docutils literal">container</tt>,
@ -717,6 +731,25 @@ the lock. It is safe to nest suspends.</p>
to group operations together in one big critical section. A plugin
can choose to run all lua code inside a C++-side suspend lock.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.call_with_finalizer(num_cleanup_args,always,cleanup_fn[,cleanup_args...],fn[,args...])</span></tt></p>
<p>Invokes <tt class="docutils literal">fn</tt> with <tt class="docutils literal">args</tt>, and after it returns or throws an
error calls <tt class="docutils literal">cleanup_fn</tt> with <tt class="docutils literal">cleanup_args</tt>. Any return values from
<tt class="docutils literal">fn</tt> are propagated, and errors are re-thrown.</p>
<p>The <tt class="docutils literal">num_cleanup_args</tt> integer specifies the number of <tt class="docutils literal">cleanup_args</tt>,
and the <tt class="docutils literal">always</tt> boolean specifies if cleanup should be called in any case,
or only in case of an error.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_finalize(cleanup_fn,fn[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">fn</tt> with arguments, then finalizes with <tt class="docutils literal">cleanup_fn</tt>.
Implemented using <tt class="docutils literal"><span class="pre">call_with_finalizer(0,true,...)</span></tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_onerror(cleanup_fn,fn[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">fn</tt> with arguments, then finalizes with <tt class="docutils literal">cleanup_fn</tt> on any thrown error.
Implemented using <tt class="docutils literal"><span class="pre">call_with_finalizer(0,false,...)</span></tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_temp_object(obj,fn[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal"><span class="pre">fn(obj,args...)</span></tt>, then finalizes with <tt class="docutils literal">obj:delete()</tt>.</p>
</li>
</ul>
<div class="section" id="persistent-configuration-storage">
<h2><a class="toc-backref" href="#id11">Persistent configuration storage</a></h2>
@ -753,6 +786,243 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p>
</div>
<div class="section" id="material-info-lookup">
<h2><a class="toc-backref" href="#id12">Material info lookup</a></h2>
<p>A material info record has fields:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p>
<p>DF material code pair, and a reference to the material object.</p>
</li>
<li><p class="first"><tt class="docutils literal">mode</tt></p>
<p>One of <tt class="docutils literal">'builtin'</tt>, <tt class="docutils literal">'inorganic'</tt>, <tt class="docutils literal">'plant'</tt>, <tt class="docutils literal">'creature'</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">inorganic</tt>, <tt class="docutils literal">plant</tt>, <tt class="docutils literal">creature</tt></p>
<p>If the material is of the matching type, contains a reference to the raw object.</p>
</li>
<li><p class="first"><tt class="docutils literal">figure</tt></p>
<p>For a specific creature material contains a ref to the historical figure.</p>
</li>
</ul>
<p>Functions:</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.matinfo.decode(type,index)</tt></p>
<p>Looks up material info for the given number pair; if not found, returs <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">....decode(matinfo)</span></tt>, <tt class="docutils literal"><span class="pre">....decode(item)</span></tt>, <tt class="docutils literal"><span class="pre">....decode(obj)</span></tt></p>
<p>Uses <tt class="docutils literal">matinfo.type</tt>/<tt class="docutils literal">matinfo.index</tt>, item getter vmethods,
or <tt class="docutils literal">obj.mat_type</tt>/<tt class="docutils literal">obj.mat_index</tt> to get the code pair.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.matinfo.find(token[,token...])</span></tt></p>
<p>Looks up material by a token string, or a pre-split string token sequence.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.matinfo.getToken(...)</span></tt>, <tt class="docutils literal">info:getToken()</tt></p>
<p>Applies <tt class="docutils literal">decode</tt> and constructs a string token.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">info:toString([temperature[,named]])</span></tt></p>
<p>Returns the human-readable name at the given temperature.</p>
</li>
<li><p class="first"><tt class="docutils literal">info:getCraftClass()</tt></p>
<p>Returns the classification used for craft skills.</p>
</li>
<li><p class="first"><tt class="docutils literal">info:matches(obj)</tt></p>
<p>Checks if the material matches job_material_category or job_item.
Accept dfhack_material_category auto-assign table.</p>
</li>
</ul>
</div>
<div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id13">C++ function wrappers</a></h2>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.TranslateName(name,in_english,only_last_name)</tt></p>
<p>Convert a language_name or only the last name part to string.</p>
</li>
</ul>
<div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id14">Gui module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedWorkshopJob(silent)</tt></p>
<p>When a job is selected in <em>'q'</em> mode, returns the job, else
prints error unless silent and returns <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedJob(silent)</tt></p>
<p>Returns the job selected in a workshop or unit/jobs screen.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedUnit(silent)</tt></p>
<p>Returns the unit selected via <em>'v'</em>, <em>'k'</em>, unit/jobs, or
a full-screen item view of a cage or suchlike.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedItem(silent)</tt></p>
<p>Returns the item selected via <em>'v'</em> -&gt;inventory, <em>'k'</em>, <em>'t'</em>, or
a full-screen item view of a container. Note that in the
last case, the highlighted <em>contained item</em> is returned, not
the container itself.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.showAnnouncement(text,color,is_bright)</tt></p>
<p>Adds a regular announcement with given text, color, and brightness.
The is_bright boolean actually seems to invert the brightness.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.showPopupAnnouncement(text,color,is_bright)</tt></p>
<p>Pops up a titan-style modal announcement window.</p>
</li>
</ul>
</div>
<div class="section" id="job-module">
<h3><a class="toc-backref" href="#id15">Job module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.printJobDetails(job)</tt></p>
<p>Prints info about the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.printItemDetails(jobitem,idx)</tt></p>
<p>Prints info about the job item.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getHolder(job)</tt></p>
<p>Returns the building holding the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getWorker(job)</tt></p>
<p>Returns the unit performing the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p>
<p>Compares important fields in the job and nested item structures.</p>
</li>
<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.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>,
a lua list containing them.</p>
</li>
</ul>
</div>
<div class="section" id="units-module">
<h3><a class="toc-backref" href="#id16">Units module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p>
<p>Returns true <em>x,y,z</em> of the unit; may be not equal to unit.pos if caged.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getContainer(unit)</tt></p>
<p>Returns the container (cage) item or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.setNickname(unit,nick)</tt></p>
<p>Sets the unit's nickname properly.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getVisibleName(unit)</tt></p>
<p>Returns the language_name object visible in game, accounting for false identities.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getNemesis(unit)</tt></p>
<p>Returns the nemesis record of the unit if it has one, or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isDead(unit)</tt></p>
<p>The unit is completely dead and passive.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isAlive(unit)</tt></p>
<p>The unit isn't dead or undead.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isSane(unit)</tt></p>
<p>The unit is capable of rational action, i.e. not dead, insane or zombie.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.clearBurrowMembers(burrow)</tt></p>
<p>Removes all units from the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isInBurrow(unit,burrow)</tt></p>
<p>Checks if the unit is in the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.setInBurrow(unit,burrow,enable)</tt></p>
<p>Adds or removes the unit from the burrow.</p>
</li>
</ul>
</div>
<div class="section" id="items-module">
<h3><a class="toc-backref" href="#id17">Items module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p>
<p>Returns true <em>x,y,z</em> of the item; may be not equal to item.pos if in inventory.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getOwner(item)</tt></p>
<p>Returns the owner unit or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.setOwner(item,unit)</tt></p>
<p>Replaces the owner of the item. If unit is <em>nil</em>, removes ownership.
Returns <em>false</em> in case of error.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getContainer(item)</tt></p>
<p>Returns the container item or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getContainedItems(item)</tt></p>
<p>Returns a list of items contained in this one.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.moveToGround(item,pos)</tt></p>
<p>Move the item to the ground at position. Returns <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.moveToContainer(item,container)</tt></p>
<p>Move the item to the container. Returns <em>false</em> if impossible.</p>
</li>
</ul>
</div>
<div class="section" id="maps-module">
<h3><a class="toc-backref" href="#id18">Maps module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getSize()</tt></p>
<p>Returns map size in blocks: <em>x, y, z</em></p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileSize()</tt></p>
<p>Returns map size in tiles: <em>x, y, z</em></p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getBlock(x,y,z)</tt></p>
<p>Returns a map block object for given x,y,z in local block coordinates.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileBlock(coords)</tt></p>
<p>Returns a map block object for given df::coord in local tile coordinates.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getRegionBiome(region_coord2d)</tt></p>
<p>Returns the biome info struct for the given global map region.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getGlobalInitFeature(index)</tt></p>
<p>Returns the global feature object with the given index.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getLocalInitFeature(region_coord2d,index)</tt></p>
<p>Returns the local feature object with the given region coords and index.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.findBurrowByName(name)</tt></p>
<p>Returns the burrow pointer or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.listBurrowBlocks(burrow)</tt></p>
<p>Returns a table of map block pointers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.clearBurrowTiles(burrow)</tt></p>
<p>Removes all tiles from the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.isBurrowTile(burrow,tile_coord)</tt></p>
<p>Checks if the tile is in burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.setBurrowTile(burrow,tile_coord,enable)</tt></p>
<p>Adds or removes the tile from the burrow. Returns <em>false</em> if invalid coords.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.isBlockBurrowTile(burrow,block,x,y)</tt></p>
<p>Checks if the tile within the block is in burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.setBlockBurrowTile(burrow,block,x,y,enable)</tt></p>
<p>Adds or removes the tile from the burrow. Returns <em>false</em> if invalid coords.</p>
</li>
</ul>
</div>
</div>
<div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id19">Core interpreter context</a></h2>
<p>While plugins can create any number of interpreter instances,
there is one special context managed by dfhack core. It is the
only context that can receive events from DF and plugins.</p>
<p>Core context specific functions:</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.is_core_context</tt></p>
<p>Boolean value; <em>true</em> in the core context.</p>
</li>
</ul>
</div>
</div>
</div>
</body>

@ -613,8 +613,10 @@ Contains various tweaks for minor bugs (currently just one).
Options
-------
:tweak clear-missing: Remove the missing status from the selected unit. This allows engraving slabs for ghostly, but not yet found, creatures.
:tweak clear-ghostly: Remove the ghostly status from the selected unit and mark it as dead. This allows getting rid of bugged ghosts which do not show up in the engraving slab menu at all, even after using clear-missing. It works, but is potentially very dangerous - so use with care. Probably (almost certainly) it does not have the same effects like a proper burial. You've been warned.
:tweak clear-missing: Remove the missing status from the selected unit. This allows engraving slabs for ghostly, but not yet found, creatures.
:tweak clear-ghostly: Remove the ghostly status from the selected unit and mark it as dead. This allows getting rid of bugged ghosts which do not show up in the engraving slab menu at all, even after using clear-missing. It works, but is potentially very dangerous - so use with care. Probably (almost certainly) it does not have the same effects like a proper burial. You've been warned.
:tweak fixmigrant: Remove the resident/merchant flag from the selected unit. Intended to fix bugged migrants/traders who stay at the map edge and don't enter your fort. Only works for dwarves (or generally the player's race in modded games). Do NOT abuse this for 'real' caravan merchants (if you really want to kidnap them, use 'tweak makeown' instead, otherwise they will have their clothes set to forbidden etc).
:tweak makeown: Force selected unit to become a member of your fort. Can be abused to grab caravan merchants and escorts, even if they don't belong to the player's race. Foreign sentients (humans, elves) can be put to work, but you can't assign rooms to them and they don't show up in DwarfTherapist because the game treats them like pets. Grabbing draft animals from a caravan can result in weirdness (animals go insane or berserk and are not flagged as tame), but you are allowed to mark them for slaughter. Grabbing wagons results in some funny spam, then they are scuttled.
tubefill
========
@ -801,41 +803,45 @@ Export dwarves to RuneSmith-compatible XML.
zone
====
Helps a bit with managing activity zones (pens, pastures and pits).
Helps a bit with managing activity zones (pens, pastures and pits) and cages.
Options:
--------
:set: Set zone under cursor as default for future assigns.
:set: Set zone or cage under cursor as default for future assigns.
:assign: Assign unit(s) to the pen or pit marked with the 'set' command. If no filters are set a unit must be selected in the in-game ui. Can also be followed by a valid zone id which will be set instead.
:unassign: Unassign selected creature from it's zone.
:autonestbox: Assign all (unless count is specified) unpastured female egg-layers to empty pens which contain a nestbox. If the pen is bigger than 1x1 the nestbox must be placed at the top left corner to be recognized. Only 1 unit will be assigned per pen.
:nick: Mass-assign nicknames, must be followed by the name you want to set.
:remnick: Mass-remove nicknames.
:tocages: Assign unit(s) to cages inside a pasture.
:uinfo: Print info about unit(s). If no filters are set a unit must be selected in the in-game ui.
:zinfo: Print info about zone(s). If no filters are set zones under the cursor are listed.
:verbose: Print some more info.
:filters: Print list of valid filter options.
:examples: Print some usage examples.
:not: Negates the next filter keyword.
Filters:
--------
:all: Process all units (to be used with additional filters).
:count: Must be followed by a number. Process only n units (to be used with additional filters).
:race: Must be followed by a race raw id (e.g. BIRD_TURKEY, ALPACA etc).
:unassigned: Not assigned to zone, chain or built cage.
:caged: In a built cage.
:uncaged: Not in a cage (in case you want your stockpiles to be left alone).
:foreign: Not of own civilization (i.e. own fortress).
:own: From own civilization (i.e. own fortress).
:war: Trained war creature.
:tamed: Creature is tame.
:trained: Creature is trained.
:untrained: Creature is untrained.
:male: Creature is male.
:female: Creature is female.
:egglayer: Race lays eggs.
:grazer: Race is a grazer.
:milkable: Race is milkable.
:minage: Minimum age. Must be followed by number.
:maxage: Maximum age. Must be followed by number.
:all: Process all units (to be used with additional filters).
:count: Must be followed by a number. Process only n units (to be used with additional filters).
:unassigned: Not assigned to zone, chain or built cage.
:minage: Minimum age. Must be followed by number.
:maxage: Maximum age. Must be followed by number.
:race: Must be followed by a race raw id (e.g. BIRD_TURKEY, ALPACA etc). Negatable.
:caged: In a built cage. Negatable.
:own: From own civilization. Negatable.
:merchant: Is a merchant / belongs to a merchant. Should only be used for pitting, not for stealing animals (slaughter should work).
:war: Trained war creature. Negatable.
:hunting: Trained hunting creature. Negatable.
:tamed: Creature is tame. Negatable.
:trained: Creature is trained. Finds war/hunting creatures as well as creatures who have a training level greater than 'domesticated'. If you want to specifically search for war/hunting creatures use 'war' or 'hunting' Negatable.
:trainablewar: Creature can be trained for war (and is not already trained for war/hunt). Negatable.
:trainablehunt: Creature can be trained for hunting (and is not already trained for war/hunt). Negatable.
:male: Creature is male. Negatable.
:female: Creature is female. Negatable.
:egglayer: Race lays eggs. Negatable.
:grazer: Race is a grazer. Negatable.
:milkable: Race is milkable. Negatable.
Usage with single units
-----------------------
@ -843,13 +849,97 @@ One convenient way to use the zone tool is to bind the command 'zone assign' to
Usage with filters
------------------
All filters can be used together with the 'assign' command. The only restriction is that it's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. Usually you should always use the filter 'own' (which implies tame) unless you want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless you specify 'race DWARF' (so it's safe to use 'assign all own' to one big pasture if you want to have all your animals at the same place). 'egglayer' and 'milkable' should be used together with 'female' unless you have a mod with egg-laying male elves who give milk or whatever.
All filters can be used together with the 'assign' command. Restrictions: It's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. It's not possible to cage owned pets because in that case the owner uncages them after a while which results in infinite hauling back and forth. Usually you should always use the filter 'own' (which implies tame) unless you want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless you specify 'race DWARF' (so it's safe to use 'assign all own' to one big pasture if you want to have all your animals at the same place). 'egglayer' and 'milkable' should be used together with 'female' unless you have a mod with egg-laying male elves who give milk or whatever. Merchants and their animals are ignored unless you specify 'merchant' (pitting them should be no problem, but stealing and pasturing their animals is not a good idea since currently they are not properly added to your own stocks; slaughtering them should work). Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).
Mass-renaming
-------------
Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and further optional filters) it will rename units matching the filter conditions.
Cage zones
----------
Using the 'tocages' command you can assign units to a set of cages, for example a room next to your butcher shop(s). They will be spread evenly among available cages to optimize hauling to and butchering from them. For this to work you need to build cages and then place one pen/pasture activity zone above them, covering all cages you want to use. Then use 'zone set' (like with 'assign') and use 'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it would make no sense, but can be used together with 'nick' or 'remnick' and all the usual filters.
Examples
--------
``zone assign all own ALPACA minage 3 maxage 10``
Assign all own alpacas who are between 3 and 10 years old to the selected pasture.
``zone assign all own caged grazer``
Assign all own grazers who are sitting in cages on stockpiles (e.g. after buying them from merchants) to the selected pasture.
``zone assign all own caged grazer nick ineedgrass``
Assign all own grazers who are sitting in cages on stockpiles (e.g. after buying them from merchants) to the selected pasture and give them the nickname 'ineedgrass'.
``zone assign all own not grazer not race CAT``
Assign all own animals who are not grazers, excluding cats.
``zone assign count 5 own female milkable``
Assign up to 5 own female milkable creatures to the selected pasture.
``zone assign all own race DWARF maxage 2``
Throw all useless kids into a pit :)
``zone nick donttouchme``
Nicknames all units in the current default zone or cage to 'donttouchme'. Mostly intended to be used for special pastures or cages which are not marked as rooms you want to protect from autobutcher.
``zone tocages count 50 own tame male not grazer``
Stuff up to 50 owned tame male animals who are not grazers into cages built on the current default zone.
autonestbox
===========
Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless of the size. The age of the units is currently not checked, most birds grow up quite fast. Egglayers who are also grazers will be ignored, since confining them to a 1x1 pasture is not a good idea. Only tame and domesticated own units are processed since pasturing half-trained wild egglayers could destroy your neat nestbox zones when they revert to wild. When called without options autonestbox will instantly run once.
Options:
--------
:start: Start running every X frames (df simulation ticks). Default: X=6000, which would be every 60 seconds at 100fps.
:stop: Stop running automatically.
:sleep: Must be followed by number X. Changes the timer to sleep X frames between runs.
autobutcher
===========
Assigns lifestock for slaughter once it reaches a specific count. Requires that you add the target race(s) to a watch list. Only tame units will be processed. Named units will be completely ignored (to protect specific animals fro autobutcher you can give them nicknames with the tool 'rename unit' for single units or with 'zone nick' to mass-rename units in pastures and cages). Creatures trained for war or hunting will be ignored as well. Creatures assigned to cages will be ignored if the cage is defined as a room (to avoid butchering unnamed zoo animals). Once you have too much adults, the oldest will be butchered first. Once you have too much kids, the youngest will be butchered first. If you don't set any target count the following default will be used: 1 male kid, 5 female kids, 1 male adult, 5 female adults.
Options:
--------
:start: Start running every X frames (df simulation ticks). Default: X=6000, which would be every 60 seconds at 100fps.
:stop: Stop running automatically.
:sleep: Must be followed by number X. Changes the timer to sleep X frames between runs.
:watch R: Start watching a race. R can be a valid race RAW id (ALPACA, BIRD_TURKEY, etc) or a list of ids seperated by spaces or the keyword 'all' which affects all races on your current watchlist.
:unwatch R: Stop watching race(s). The current target settings will be remembered. R can be a list of ids or the keyword 'all'.
:forget R: Stop watching race(s) and forget it's/their target settings. R can be a list of ids or the keyword 'all'.
:autowatch: Automatically adds all new races (animals you buy from merchants, tame yourself or get from migrants)
to the watch list using default target count.
:noautowatch: Stop auto-adding new races to the watchlist.
:list: Print the current status and watchlist.
:list_export: Print status and watchlist in a format which can be used to import them to another savegame (see notes).
:target fk mk fa ma R: Set target count for specified race(s).
fk = number of female kids,
mk = number of male kids,
fa = number of female adults,
ma = number of female adults.
R can be a list of ids or the keyword 'all' or 'new'. R = 'all': change target count for all races on watchlist and set the new default for the future. R = 'new': don't touch current settings on the watchlist, only set the new default for future entries.
:example: Print some usage examples.
Examples:
---------
You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, 1 male) of the race alpaca. Once the kids grow up the oldest adults will get slaughtered. Excess kids will get slaughtered starting with the youngest to allow that the older ones grow into adults. Any unnamed cats will be slaughtered as soon as possible.
::
autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY
autobutcher target 0 0 0 0 CAT
autobutcher watch ALPACA BIRD_TURKEY CAT
autobutcher start
Automatically put all new races onto the watchlist and mark unnamed tame units for slaughter as soon as they arrive in your fort. Settings already made for specific races will be left untouched.
::
autobutcher target 0 0 0 0 new
autobutcher autowatch
autobutcher start
Stop watching the races alpaca and cat, but remember the target count settings so that you can use 'unwatch' without the need to enter the values again. Note: 'autobutcher unwatch all' works, but only makes sense if you want to keep the plugin running with the 'autowatch' feature or manually add some new races with 'watch'. If you simply want to stop it completely use 'autobutcher stop' instead.
::
autobutcher unwatch ALPACA CAT
Note:
-----
Settings and watchlist are stored in the savegame, so that you can have different settings for each world. If you want to copy your watchlist to another savegame you can use the command list_export:
::
Load savegame where you made the settings.
Start a CMD shell and navigate to the df directory. Type the following into the shell:
dfhack-run autobutcher list_export > autobutcher.bat
Load the savegame where you want to copy the settings to, run the batch file (from the shell):
autobutcher.bat

@ -0,0 +1,9 @@
@echo off
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
echo generating a build folder
rem for /f "delims=" %%a in ('DATE /T') do @set myvar=breakfast-%BUILD_NUMBER%
set myvar=breakfast-%BUILD_NUMBER%
cmake ..\.. -G"Visual Studio 10" -DDFHACK_RELEASE="%myvar%" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=1 -DBUILD_DEV_PLUGINS=1 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1

@ -1,4 +1,6 @@
@echo off
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
msbuild /m /p:Platform=Win32 /p:Configuration=RelWithDebInfo PACKAGE.vcxproj
cd ..
cd ..
exit %ERRORLEVEL%

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

@ -1,7 +1,7 @@
PROJECT ( lua CXX )
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DLUA_USE_APICHECK")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLUA_USE_APICHECK")
IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE )

@ -403,7 +403,7 @@ static void finishCcall (lua_State *L) {
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0);
/* finish 'luaD_call' */
L->nCcalls--;
//L->nCcalls--;
/* finish 'lua_callk' */
adjustresults(L, ci->nresults);
/* call continuation function */
@ -513,7 +513,7 @@ static void resume (lua_State *L, void *ud) {
api_checknelems(L, n);
firstArg = L->top - n; /* yield results come from continuation */
}
L->nCcalls--; /* finish 'luaD_call' */
//L->nCcalls--; /* finish 'luaD_call' */
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
}
unroll(L, NULL);

@ -5,8 +5,8 @@
keybinding add Ctrl-W twaterlvl
# with cursor:
keybinding add Ctrl-V vdig
keybinding add Ctrl-Shift-V "vdig x"
keybinding add Ctrl-V digv
keybinding add Ctrl-Shift-V "digv x"
keybinding add Ctrl-C spotclean
keybinding add Ctrl-Shift-K autodump-destroy-here

@ -59,6 +59,7 @@ DataDefs.cpp
LuaWrapper.cpp
LuaTypes.cpp
LuaTools.cpp
LuaApi.cpp
DataStatics.cpp
DataStaticsCtor.cpp
DataStaticsFields.cpp

@ -49,6 +49,8 @@ using namespace std;
#include "modules/Graphic.h"
#include "modules/Windows.h"
#include "RemoteServer.h"
#include "LuaTools.h"
using namespace DFHack;
#include "df/ui.h"
@ -118,7 +120,7 @@ struct Core::Private
}
};
void cheap_tokenise(string const& input, vector<string> &output)
void Core::cheap_tokenise(string const& input, vector<string> &output)
{
string *cur = NULL;
@ -177,7 +179,7 @@ void fHKthread(void * iodata)
color_ostream_proxy out(core->getConsole());
vector <string> args;
cheap_tokenise(stuff, args);
Core::cheap_tokenise(stuff, args);
if (args.empty()) {
out.printerr("Empty hotkey command.\n");
continue;
@ -218,7 +220,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
// cut the input into parts
vector <string> parts;
cheap_tokenise(command,parts);
Core::cheap_tokenise(command,parts);
if(parts.size() == 0)
{
clueless_counter ++;
@ -701,6 +703,9 @@ bool Core::Init()
virtual_identity::Init(this);
df::global::InitGlobals();
// initialize common lua context
Lua::Core::Init(con);
// create mutex for syncing with interactive tasks
misc_data_mutex=new mutex();
cerr << "Initializing Plugins.\n";
@ -803,6 +808,13 @@ void *Core::GetData( std::string key )
}
}
bool Core::isSuspended(void)
{
lock_guard<mutex> lock(d->AccessMutex);
return (d->df_suspend_depth > 0 && d->df_suspend_thread == this_thread::get_id());
}
void Core::Suspend()
{
auto tid = this_thread::get_id();
@ -861,13 +873,9 @@ int Core::TileUpdate()
// should always be from simulation thread!
int Core::Update()
{
if(!started)
Init();
if(errorstate)
return -1;
color_ostream_proxy out(con);
// Pretend this thread has suspended the core in the usual way
{
lock_guard<mutex> lock(d->AccessMutex);
@ -877,6 +885,25 @@ int Core::Update()
d->df_suspend_depth = 1000;
}
// Initialize the core
bool first_update = false;
if(!started)
{
first_update = true;
Init();
if(errorstate)
return -1;
Lua::Core::Reset(con, "core init");
}
color_ostream_proxy out(con);
Lua::Core::Reset(out, "DF code execution");
if (first_update)
plug_mgr->OnStateChange(out, SC_CORE_INITIALIZED);
// detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL;
void *new_mapdata = NULL;
@ -893,26 +920,31 @@ int Core::Update()
if (new_wdata != last_world_data_ptr)
{
// we check for map change too
bool mapchange = new_mapdata != last_local_map_ptr;
bool had_map = isMapLoaded();
last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache();
// and if the world is going away, we report the map change first
if(!new_wdata && mapchange)
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
if(had_map)
plug_mgr->OnStateChange(out, SC_MAP_UNLOADED);
// and if the world is appearing, we report map change after that
plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED);
if(new_wdata && mapchange)
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
if(isMapLoaded())
plug_mgr->OnStateChange(out, SC_MAP_LOADED);
}
// otherwise just check for map change...
else if (new_mapdata != last_local_map_ptr)
{
bool had_map = isMapLoaded();
last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache();
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
if (isMapLoaded() != had_map)
{
getWorld()->ClearPersistentCache();
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
}
}
// detect if the viewscreen changed
@ -959,6 +991,8 @@ int Core::Update()
assert(d->df_suspend_depth == 0);
// destroy condition
delete nc;
// check lua stack depth
Lua::Core::Reset(con, "suspend");
}
return 0;

@ -128,17 +128,6 @@ void compound_identity::Init(Core *core)
// they are called in an undefined order.
for (compound_identity *p = list; p; p = p->next)
p->doInit(core);
//FIXME: ... nuked. the group was empty...
/*
// Read pre-filled vtable ptrs
OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
for (virtual_identity *p = list; p; p = p->next) {
void * tmp;
if (ptr_table->getSafeAddress(p->getName(),tmp))
p->vtable_ptr = tmp;
}
*/
}
bitfield_identity::bitfield_identity(size_t size,
@ -223,17 +212,23 @@ virtual_identity::virtual_identity(size_t size, TAllocateFn alloc,
{
}
/* Vtable name to identity lookup. */
static std::map<std::string, virtual_identity*> name_lookup;
/* Vtable pointer to identity lookup. */
std::map<void*, virtual_identity*> virtual_identity::known;
void virtual_identity::doInit(Core *core)
{
struct_identity::doInit(core);
name_lookup[getOriginalName()] = this;
}
auto vtname = getOriginalName();
name_lookup[vtname] = this;
/* Vtable to identity lookup. */
std::map<void*, virtual_identity*> virtual_identity::known;
vtable_ptr = core->vinfo->getVTable(vtname);
if (vtable_ptr)
known[vtable_ptr] = this;
}
virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
{
@ -265,8 +260,10 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
<< ", previous 0x" << unsigned(p->vtable_ptr) << std::dec << std::endl;
abort();
} else if (!p->vtable_ptr) {
std::cerr << "class '" << p->getName() << "': vtable = 0x"
<< std::hex << unsigned(vtable) << std::dec << std::endl;
uint32_t pv = unsigned(vtable);
pv -= Core::getInstance().vinfo->getRebaseDelta();
std::cerr << "<vtable-address name='" << p->getOriginalName() << "' value='0x"
<< std::hex << pv << std::dec << "'/>" << std::endl;
}
known[vtable] = p;

@ -0,0 +1,725 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 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.
*/
#include "Internal.h"
#include <string>
#include <vector>
#include <map>
#include "MemAccess.h"
#include "Core.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
#include "DataDefs.h"
#include "DataIdentity.h"
#include "DataFuncs.h"
#include "modules/World.h"
#include "modules/Gui.h"
#include "modules/Job.h"
#include "modules/Translation.h"
#include "modules/Units.h"
#include "modules/Items.h"
#include "modules/Materials.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
#include "MiscUtils.h"
#include "df/job.h"
#include "df/job_item.h"
#include "df/building.h"
#include "df/unit.h"
#include "df/item.h"
#include "df/material.h"
#include "df/nemesis_record.h"
#include "df/historical_figure.h"
#include "df/plant_raw.h"
#include "df/creature_raw.h"
#include "df/inorganic_raw.h"
#include "df/dfhack_material_category.h"
#include "df/job_material_category.h"
#include "df/burrow.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
using namespace DFHack;
using namespace DFHack::LuaWrapper;
int Lua::PushPosXYZ(lua_State *state, df::coord pos)
{
if (!pos.isValid())
{
lua_pushnil(state);
return 1;
}
else
{
lua_pushinteger(state, pos.x);
lua_pushinteger(state, pos.y);
lua_pushinteger(state, pos.z);
return 3;
}
}
/**************************************************
* Per-world persistent configuration storage API *
**************************************************/
static PersistentDataItem persistent_by_struct(lua_State *state, int idx)
{
lua_getfield(state, idx, "entry_id");
int id = lua_tointeger(state, -1);
lua_pop(state, 1);
PersistentDataItem ref = Core::getInstance().getWorld()->GetPersistentData(id);
if (ref.isValid())
{
lua_getfield(state, idx, "key");
const char *str = lua_tostring(state, -1);
if (!str || str != ref.key())
luaL_argerror(state, idx, "inconsistent id and key");
lua_pop(state, 1);
}
return ref;
}
static int read_persistent(lua_State *state, PersistentDataItem ref, bool create)
{
if (!ref.isValid())
{
lua_pushnil(state);
lua_pushstring(state, "entry not found");
return 2;
}
if (create)
lua_createtable(state, 0, 4);
lua_pushvalue(state, lua_upvalueindex(1));
lua_setmetatable(state, -2);
lua_pushinteger(state, ref.entry_id());
lua_setfield(state, -2, "entry_id");
lua_pushstring(state, ref.key().c_str());
lua_setfield(state, -2, "key");
lua_pushstring(state, ref.val().c_str());
lua_setfield(state, -2, "value");
lua_createtable(state, PersistentDataItem::NumInts, 0);
for (int i = 0; i < PersistentDataItem::NumInts; i++)
{
lua_pushinteger(state, ref.ival(i));
lua_rawseti(state, -2, i+1);
}
lua_setfield(state, -2, "ints");
return 1;
}
static PersistentDataItem get_persistent(lua_State *state)
{
luaL_checkany(state, 1);
if (lua_istable(state, 1))
{
if (!lua_getmetatable(state, 1) ||
!lua_rawequal(state, -1, lua_upvalueindex(1)))
luaL_argerror(state, 1, "invalid table type");
lua_settop(state, 1);
return persistent_by_struct(state, 1);
}
else
{
const char *str = luaL_checkstring(state, 1);
return Core::getInstance().getWorld()->GetPersistentData(str);
}
}
static int dfhack_persistent_get(lua_State *state)
{
CoreSuspender suspend;
auto ref = get_persistent(state);
return read_persistent(state, ref, !lua_istable(state, 1));
}
static int dfhack_persistent_delete(lua_State *state)
{
CoreSuspender suspend;
auto ref = get_persistent(state);
bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref);
lua_pushboolean(state, ok);
return 1;
}
static int dfhack_persistent_get_all(lua_State *state)
{
CoreSuspender suspend;
const char *str = luaL_checkstring(state, 1);
bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false);
std::vector<PersistentDataItem> data;
Core::getInstance().getWorld()->GetPersistentData(&data, str, prefix);
if (data.empty())
{
lua_pushnil(state);
}
else
{
lua_createtable(state, data.size(), 0);
for (size_t i = 0; i < data.size(); ++i)
{
read_persistent(state, data[i], true);
lua_rawseti(state, -2, i+1);
}
}
return 1;
}
static int dfhack_persistent_save(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 2);
luaL_checktype(state, 1, LUA_TTABLE);
bool add = lua_toboolean(state, 2);
lua_getfield(state, 1, "key");
const char *str = lua_tostring(state, -1);
if (!str)
luaL_argerror(state, 1, "no key field");
lua_settop(state, 1);
// Retrieve existing or create a new entry
PersistentDataItem ref;
bool added = false;
if (add)
{
ref = Core::getInstance().getWorld()->AddPersistentData(str);
added = true;
}
else if (lua_getmetatable(state, 1))
{
if (!lua_rawequal(state, -1, lua_upvalueindex(1)))
return luaL_argerror(state, 1, "invalid table type");
lua_pop(state, 1);
ref = persistent_by_struct(state, 1);
}
else
{
ref = Core::getInstance().getWorld()->GetPersistentData(str);
}
// Auto-add if not found
if (!ref.isValid())
{
ref = Core::getInstance().getWorld()->AddPersistentData(str);
if (!ref.isValid())
luaL_error(state, "cannot create persistent entry");
added = true;
}
// Copy data from lua to C++ memory
lua_getfield(state, 1, "value");
if (const char *str = lua_tostring(state, -1))
ref.val() = str;
lua_pop(state, 1);
lua_getfield(state, 1, "ints");
if (lua_istable(state, -1))
{
for (int i = 0; i < PersistentDataItem::NumInts; i++)
{
lua_rawgeti(state, -1, i+1);
if (lua_isnumber(state, -1))
ref.ival(i) = lua_tointeger(state, -1);
lua_pop(state, 1);
}
}
lua_pop(state, 1);
// Reinitialize lua from C++ and return
read_persistent(state, ref, false);
lua_pushboolean(state, added);
return 2;
}
static const luaL_Reg dfhack_persistent_funcs[] = {
{ "get", dfhack_persistent_get },
{ "delete", dfhack_persistent_delete },
{ "get_all", dfhack_persistent_get_all },
{ "save", dfhack_persistent_save },
{ NULL, NULL }
};
static void OpenPersistent(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "persistent");
lua_dup(state);
luaL_setfuncs(state, dfhack_persistent_funcs, 1);
lua_dup(state);
lua_setfield(state, -2, "__index");
lua_pop(state, 1);
}
/************************
* Material info lookup *
************************/
static void push_matinfo(lua_State *state, MaterialInfo &info)
{
if (!info.isValid())
{
lua_pushnil(state);
return;
}
lua_newtable(state);
lua_pushvalue(state, lua_upvalueindex(1));
lua_setmetatable(state, -2);
lua_pushinteger(state, info.type);
lua_setfield(state, -2, "type");
lua_pushinteger(state, info.index);
lua_setfield(state, -2, "index");
#define SETOBJ(name) { \
Lua::PushDFObject(state, info.name); \
lua_setfield(state, -2, #name); \
}
SETOBJ(material);
if (info.plant) SETOBJ(plant);
if (info.creature) SETOBJ(creature);
if (info.inorganic) SETOBJ(inorganic);
if (info.figure) SETOBJ(figure);
#undef SETOBJ
if (info.mode != MaterialInfo::Builtin)
{
lua_pushinteger(state, info.subtype);
lua_setfield(state, -2, "subtype");
}
const char *id = "builtin";
switch (info.mode)
{
case MaterialInfo::Plant: id = "plant"; break;
case MaterialInfo::Creature: id = "creature"; break;
case MaterialInfo::Inorganic: id = "inorganic"; break;
}
lua_pushstring(state, id);
lua_setfield(state, -2, "mode");
}
static int dfhack_matinfo_find(lua_State *state)
{
MaterialInfo info;
int argc = lua_gettop(state);
if (argc == 1)
info.find(luaL_checkstring(state, 1));
else
{
std::vector<std::string> tokens;
for (int i = 1; i < argc; i++)
tokens.push_back(luaL_checkstring(state, i));
info.find(tokens);
}
push_matinfo(state, info);
return 1;
}
static bool decode_matinfo(lua_State *state, MaterialInfo *info, bool numpair = false)
{
int curtop = lua_gettop(state);
luaL_checkany(state, 1);
if (!lua_isnumber(state, 1))
{
if (lua_isnil(state, 1))
return false;
if (lua_getmetatable(state, 1))
{
if (lua_rawequal(state, -1, lua_upvalueindex(1)))
{
lua_getfield(state, 1, "type");
lua_getfield(state, 1, "index");
goto int_pair;
}
lua_pop(state, 1);
}
if (lua_isuserdata(state, 1))
{
if (auto item = Lua::GetDFObject<df::item>(state, 1))
return info->decode(item);
if (auto mvec = Lua::GetDFObject<df::material_vec_ref>(state, 1))
return info->decode(*mvec, luaL_checkint(state, 2));
}
lua_getfield(state, 1, "mat_type");
lua_getfield(state, 1, "mat_index");
goto int_pair;
}
else
{
if (!numpair)
luaL_argerror(state, 1, "material info object expected");
if (curtop < 2)
lua_settop(state, 2);
}
int_pair:
{
int ok;
int type = lua_tointegerx(state, -2, &ok);
if (!ok)
luaL_argerror(state, 1, "material id is not a number");
int index = lua_tointegerx(state, -1, &ok);
if (!ok)
index = -1;
lua_settop(state, curtop);
return info->decode(type, index);
}
}
static int dfhack_matinfo_decode(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info, true);
push_matinfo(state, info);
return 1;
}
static int dfhack_matinfo_getToken(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info, true);
auto str = info.getToken();
lua_pushstring(state, str.c_str());
return 1;
}
static int dfhack_matinfo_toString(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info);
lua_settop(state, 3);
auto str = info.toString(luaL_optint(state, 2, 10015), lua_toboolean(state, 3));
lua_pushstring(state, str.c_str());
return 1;
}
static int dfhack_matinfo_getCraftClass(lua_State *state)
{
MaterialInfo info;
if (decode_matinfo(state, &info, true))
lua_pushinteger(state, info.getCraftClass());
else
lua_pushnil(state);
return 1;
}
static int dfhack_matinfo_matches(lua_State *state)
{
MaterialInfo info;
if (!decode_matinfo(state, &info))
luaL_argerror(state, 1, "material info object expected");
luaL_checkany(state, 2);
if (lua_isuserdata(state, 2))
{
if (auto mc = Lua::GetDFObject<df::job_material_category>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else if (auto mc = Lua::GetDFObject<df::dfhack_material_category>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else if (auto mc = Lua::GetDFObject<df::job_item>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else
luaL_argerror(state, 2, "material category object expected");
}
else if (lua_istable(state, 2))
{
df::dfhack_material_category tmp;
if (!Lua::AssignDFObject(*Lua::GetOutput(state), state, &tmp, 2, false))
lua_error(state);
lua_pushboolean(state, info.matches(tmp));
}
else
luaL_argerror(state, 2, "material category object expected");
return 1;
}
static const luaL_Reg dfhack_matinfo_funcs[] = {
{ "find", dfhack_matinfo_find },
{ "decode", dfhack_matinfo_decode },
{ "getToken", dfhack_matinfo_getToken },
{ "toString", dfhack_matinfo_toString },
{ "getCraftClass", dfhack_matinfo_getCraftClass },
{ "matches", dfhack_matinfo_matches },
{ NULL, NULL }
};
static void OpenMatinfo(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "matinfo");
lua_dup(state);
luaL_setfuncs(state, dfhack_matinfo_funcs, 1);
lua_dup(state);
lua_setfield(state, -2, "__index");
lua_pop(state, 1);
}
/************************
* Wrappers for C++ API *
************************/
static void OpenModule(lua_State *state, const char *mname,
const LuaWrapper::FunctionReg *reg, const luaL_Reg *reg2 = NULL)
{
luaL_getsubtable(state, lua_gettop(state), mname);
LuaWrapper::SetFunctionWrappers(state, reg);
if (reg2)
luaL_setfuncs(state, reg2, 0);
lua_pop(state, 1);
}
#define WRAPM(module, function) { #function, df::wrap_function(module::function,true) }
#define WRAP(function) { #function, df::wrap_function(function,true) }
#define WRAPN(name, function) { #name, df::wrap_function(function,true) }
static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAPM(Translation, TranslateName),
{ NULL, NULL }
};
static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getSelectedWorkshopJob),
WRAPM(Gui, getSelectedJob),
WRAPM(Gui, getSelectedUnit),
WRAPM(Gui, getSelectedItem),
WRAPM(Gui, showAnnouncement),
WRAPM(Gui, showPopupAnnouncement),
{ NULL, NULL }
};
static bool jobEqual(df::job *job1, df::job *job2) { return *job1 == *job2; }
static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; }
static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,cloneJobStruct),
WRAPM(Job,printItemDetails),
WRAPM(Job,printJobDetails),
WRAPM(Job,getHolder),
WRAPM(Job,getWorker),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
};
static int job_listNewlyCreated(lua_State *state)
{
int nxid = luaL_checkint(state, 1);
lua_settop(state, 1);
std::vector<df::job*> pvec;
if (Job::listNewlyCreated(&pvec, &nxid))
{
lua_pushinteger(state, nxid);
Lua::PushVector(state, pvec);
return 2;
}
else
return 1;
}
static const luaL_Reg dfhack_job_funcs[] = {
{ "listNewlyCreated", job_listNewlyCreated },
{ NULL, NULL }
};
static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getContainer),
WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName),
WRAPM(Units, getNemesis),
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
WRAPM(Units, clearBurrowMembers),
WRAPM(Units, isInBurrow),
WRAPM(Units, setInBurrow),
{ NULL, NULL }
};
static int units_getPosition(lua_State *state)
{
return Lua::PushPosXYZ(state, Units::getPosition(Lua::CheckDFObject<df::unit>(state,1)));
}
static const luaL_Reg dfhack_units_funcs[] = {
{ "getPosition", units_getPosition },
{ NULL, NULL }
};
static bool items_moveToGround(df::item *item, df::coord pos)
{
MapExtras::MapCache mc;
return Items::moveToGround(mc, item, pos);
}
static bool items_moveToContainer(df::item *item, df::item *container)
{
MapExtras::MapCache mc;
return Items::moveToContainer(mc, item, container);
}
static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getOwner),
WRAPM(Items, setOwner),
WRAPM(Items, getContainer),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
{ NULL, NULL }
};
static int items_getPosition(lua_State *state)
{
return Lua::PushPosXYZ(state, Items::getPosition(Lua::CheckDFObject<df::item>(state,1)));
}
static int items_getContainedItems(lua_State *state)
{
std::vector<df::item*> pvec;
Items::getContainedItems(Lua::CheckDFObject<df::item>(state,1),&pvec);
Lua::PushVector(state, pvec);
return 1;
}
static const luaL_Reg dfhack_items_funcs[] = {
{ "getPosition", items_getPosition },
{ "getContainedItems", items_getContainedItems },
{ NULL, NULL }
};
static bool maps_isBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y)
{
return Maps::isBlockBurrowTile(burrow, block, df::coord2d(x,y));
}
static bool maps_setBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y, bool enable)
{
return Maps::setBlockBurrowTile(burrow, block, df::coord2d(x,y), enable);
}
static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPN(getTileBlock, (df::map_block* (*)(df::coord))Maps::getTileBlock),
WRAPM(Maps, getRegionBiome),
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, findBurrowByName),
WRAPM(Maps, clearBurrowTiles),
WRAPN(isBlockBurrowTile, maps_isBlockBurrowTile),
WRAPN(setBlockBurrowTile, maps_setBlockBurrowTile),
WRAPM(Maps, isBurrowTile),
WRAPM(Maps, setBurrowTile),
{ NULL, NULL }
};
static int maps_listBurrowBlocks(lua_State *state)
{
std::vector<df::map_block*> pvec;
Maps::listBurrowBlocks(&pvec, Lua::CheckDFObject<df::burrow>(state,1));
Lua::PushVector(state, pvec);
return 1;
}
static const luaL_Reg dfhack_maps_funcs[] = {
{ "listBurrowBlocks", maps_listBurrowBlocks },
{ NULL, NULL }
};
/************************
* Main Open function *
************************/
void OpenDFHackApi(lua_State *state)
{
OpenPersistent(state);
OpenMatinfo(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module);
OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs);
OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs);
OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs);
}

File diff suppressed because it is too large Load Diff

@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h"
#include "Core.h"
#include "Error.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
@ -79,6 +80,15 @@ void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr,
{
invoke_assign(state, this, ptr, val_index);
}
// Allow by-value assignment for wrapped function parameters
else if (fname_idx == UPVAL_METHOD_NAME && lua_isuserdata(state, val_index))
{
void *nval = get_object_internal(state, this, val_index, false);
if (!nval)
field_error(state, fname_idx, "incompatible type in complex assignment", "write");
if (!copy(ptr, nval))
field_error(state, fname_idx, "no copy support", "write");
}
else
field_error(state, fname_idx, "complex object", "write");
}
@ -1020,34 +1030,77 @@ static int meta_global_newindex(lua_State *state)
static int meta_call_function(lua_State *state)
{
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
if (lua_gettop(state) != id->getNumArgs())
return method_wrapper_core(state, id);
}
int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id)
{
if (id->adjustArgs())
lua_settop(state, id->getNumArgs());
else if (lua_gettop(state) != id->getNumArgs())
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");
id->invoke(state, 1);
try {
id->invoke(state, 1);
}
catch (Error::NullPointer &e) {
const char *vn = e.varname();
std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?");
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
}
catch (std::exception &e) {
std::string tmp = stl_sprintf("C++ exception: %s", e.what());
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
}
return 1;
}
/**
* Create a closure invoking the given function, and add it to the field table.
* Push a closure invoking the given function.
*/
static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
const char *name, function_identity_base *fun)
void LuaWrapper::PushFunctionWrapper(lua_State *state, int meta_idx,
const char *name, function_identity_base *fun)
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
lua_pushvalue(state, meta_idx);
if (meta_idx)
lua_pushvalue(state, meta_idx);
else
lua_pushlightuserdata(state, NULL); // can't be a metatable
lua_pushfstring(state, "%s()", name);
lua_pushlightuserdata(state, fun);
lua_pushcclosure(state, meta_call_function, 4);
}
/**
* Create a closure invoking the given function, and add it to the field table.
*/
static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
const char *name, function_identity_base *fun)
{
PushFunctionWrapper(state, meta_idx, name, fun);
lua_setfield(state, field_idx, name);
}
/**
* Wrap functions and add them to the table on the top of the stack.
*/
void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
{
int base = lua_gettop(state);
for (; reg && reg->name; ++reg)
AddMethodWrapper(state, 0, base, reg->name, reg->identity);
}
/**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
*/
static void IndexFields(lua_State *state, int base, struct_identity *pstruct)
static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bool globals)
{
if (pstruct->getParent())
IndexFields(state, base, pstruct->getParent());
IndexFields(state, base, pstruct->getParent(), globals);
auto fields = pstruct->getFields();
if (!fields)
@ -1077,7 +1130,10 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct)
break;
default:
AssociateId(state, base+3, ++cnt, name.c_str());
// Do not add invalid globals to the enumeration order
if (!globals || *(void**)fields[i].offset)
AssociateId(state, base+3, ++cnt, name.c_str());
lua_pushlightuserdata(state, (void*)&fields[i]);
lua_setfield(state, base+2, name.c_str());
break;
@ -1115,7 +1171,7 @@ void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, st
* Make a struct-style object metatable.
*/
static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
lua_CFunction reader, lua_CFunction writer)
lua_CFunction reader, lua_CFunction writer, bool globals = false)
{
int base = lua_gettop(state);
@ -1124,7 +1180,7 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
// Index the fields
lua_newtable(state);
IndexFields(state, base, pstruct);
IndexFields(state, base, pstruct, globals);
// Add the iteration metamethods
PushStructMethod(state, base+1, base+3, meta_struct_next);
@ -1276,7 +1332,7 @@ void struct_identity::build_metatable(lua_State *state)
void global_identity::build_metatable(lua_State *state)
{
MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex);
MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex, true);
}
/**

@ -53,7 +53,10 @@ static luaL_Reg no_functions[] = { { NULL, NULL } };
*/
void LuaWrapper::field_error(lua_State *state, int index, const char *err, const char *mode)
{
lua_getfield(state, UPVAL_METATABLE, "__metatable");
if (lua_islightuserdata(state, UPVAL_METATABLE))
lua_pushstring(state, "(global)");
else
lua_getfield(state, UPVAL_METATABLE, "__metatable");
const char *cname = lua_tostring(state, -1);
const char *fname = index ? lua_tostring(state, index) : "*";
luaL_error(state, "Cannot %s field %s.%s: %s.",
@ -696,6 +699,9 @@ static int meta_assign(lua_State *state)
{
type_identity *id = get_object_identity(state, 1, "df.assign()", false);
if (lua_getmetatable(state, 2))
luaL_error(state, "cannot use lua tables with metatable in df.assign()");
int base = lua_gettop(state);
// x:assign{ assign = foo } => x:assign(foo)

@ -25,6 +25,7 @@ distribution.
#include "Internal.h"
#include "Export.h"
#include "MiscUtils.h"
#include "Error.h"
#ifndef LINUX_BUILD
#include <Windows.h>
@ -37,6 +38,11 @@ distribution.
#include <stdarg.h>
#include <sstream>
#include <map>
const char *DFHack::Error::NullPointer::what() const throw() {
return "NULL pointer access";
}
std::string stl_sprintf(const char *fmt, ...) {
va_list lst;
@ -149,4 +155,162 @@ uint64_t GetTimeMs64()
return ret;
}
#endif
#endif
/* Character decoding */
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
#define UTF8_ACCEPT 0
#define UTF8_REJECT 12
static const uint8_t utf8d[] = {
// The first part of the table maps bytes to character classes that
// to reduce the size of the transition table and create bitmasks.
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
// The second part is a transition table that maps a combination
// of a state of the automaton and a character class to a state.
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12,
};
static inline uint32_t
decode(uint32_t* state, uint32_t* codep, uint8_t byte) {
uint32_t type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8d[256 + *state + type];
return *state;
}
/* Character encoding */
static inline int encode(uint8_t *out, uint16_t c) {
if (c <= 0x7F)
{
out[0] = c;
return 1;
}
else if (c <= 0x7FF)
{
out[0] = (0xC0 | (c >> 6));
out[1] = (0x80 | (c & 0x3F));
return 2;
}
else /*if (c <= 0xFFFF)*/
{
out[0] = (0xE0 | (c >> 12));
out[1] = (0x80 | ((c >> 6) & 0x3F));
out[2] = (0x80 | (c & 0x3F));
return 3;
}
}
/* CP437 */
static uint16_t character_table[256] = {
0, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, //
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
0x25BA, 0x25C4, 0x2195, 0x203C, 0xB6, 0xA7, 0x25AC, 0x21A8, //
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, //
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, //
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, //
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, //
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, //
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, //
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x2302,
0xC7, 0xFC, 0xE9, 0xE2, 0xE4, 0xE0, 0xE5, 0xE7, //
0xEA, 0xEB, 0xE8, 0xEF, 0xEE, 0xEC, 0xC4, 0xC5,
0xC9, 0xE6, 0xC6, 0xF4, 0xF6, 0xF2, 0xFB, 0xF9, //
0xFF, 0xD6, 0xDC, 0xA2, 0xA3, 0xA5, 0x20A7, 0x192,
0xE1, 0xED, 0xF3, 0xFA, 0xF1, 0xD1, 0xAA, 0xBA, //
0xBF, 0x2310, 0xAC, 0xBD, 0xBC, 0xA1, 0xAB, 0xBB,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, //
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, //
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, //
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
0x3B1, 0xDF, 0x393, 0x3C0, 0x3A3, 0x3C3, 0xB5, 0x3C4, //
0x3A6, 0x398, 0x3A9, 0x3B4, 0x221E, 0x3C6, 0x3B5, 0x2229,
0x2261, 0xB1, 0x2265, 0x2264, 0x2320, 0x2321, 0xF7, 0x2248, //
0xB0, 0x2219, 0xB7, 0x221A, 0x207F, 0xB2, 0x25A0, 0xA0
};
std::string DF2UTF(const std::string &in)
{
std::string out;
out.reserve(in.size());
uint8_t buf[4];
for (size_t i = 0; i < in.size(); i++)
{
int cnt = encode(buf, character_table[(uint8_t)in[i]]);
out.append(&buf[0], &buf[cnt]);
}
return out;
}
std::string UTF2DF(const std::string &in)
{
// Unicode to normal lookup table
static std::map<uint32_t, char> ctable;
if (ctable.empty())
{
for (uint16_t i = 0; i < 256; i++)
if (character_table[i] != i)
ctable[character_table[i]] = char(i);
}
// Actual conversion loop
size_t size = in.size();
std::string out(size, char(0));
uint32_t codepoint = 0;
uint32_t state = UTF8_ACCEPT, prev = UTF8_ACCEPT;
uint32_t pos = 0;
for (unsigned i = 0; i < size; prev = state, i++) {
switch (decode(&state, &codepoint, uint8_t(in[i]))) {
case UTF8_ACCEPT:
if (codepoint < 256 && character_table[codepoint] == codepoint) {
out[pos++] = char(codepoint);
} else {
char v = ctable[codepoint];
out[pos++] = v ? v : '?';
}
break;
case UTF8_REJECT:
out[pos++] = '?';
if (prev != UTF8_ACCEPT) --i;
state = UTF8_ACCEPT;
break;
}
}
if (pos != size)
out.resize(pos);
return out;
}

@ -14,7 +14,7 @@
#include <map>
#include "DFHack.h"
#include "Core.h"
#include "PluginManager.h"
#include "Hooks.h"
#include <iostream>

@ -28,7 +28,7 @@ distribution.
#include <stdint.h>
#include <vector>
#include <string>
#include "Core.h"
#include "PluginManager.h"
#include "Hooks.h"
#include <stdio.h>

@ -32,6 +32,9 @@ distribution.
#include "DataDefs.h"
#include "MiscUtils.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
using namespace DFHack;
#include <string>
@ -107,8 +110,8 @@ struct Plugin::RefLock
void lock_sub()
{
mut->lock();
refcount --;
wakeup->notify_one();
if (--refcount == 0)
wakeup->notify_one();
mut->unlock();
}
void wait()
@ -130,6 +133,13 @@ struct Plugin::RefAutolock
~RefAutolock(){ lock->unlock(); };
};
struct Plugin::RefAutoinc
{
RefLock * lock;
RefAutoinc(RefLock * lck):lock(lck){ lock->lock_add(); };
~RefAutoinc(){ lock->lock_sub(); };
};
Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm)
{
filename = filepath;
@ -210,6 +220,7 @@ bool Plugin::load(color_ostream &con)
plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown");
plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange");
plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect");
index_lua(plug);
this->name = *plug_name;
plugin_lib = plug;
commands.clear();
@ -222,6 +233,7 @@ bool Plugin::load(color_ostream &con)
else
{
con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str());
reset_lua();
ClosePlugin(plugin_lib);
state = PS_BROKEN;
return false;
@ -235,13 +247,22 @@ bool Plugin::unload(color_ostream &con)
// if we are actually loaded
if(state == PS_LOADED)
{
// notify the plugin about an attempt to shutdown
if (plugin_onstatechange &&
plugin_onstatechange(con, SC_BEGIN_UNLOAD) == CR_NOT_FOUND)
{
con.printerr("Plugin %s has refused to be unloaded.\n", name.c_str());
access->unlock();
return false;
}
// wait for all calls to finish
access->wait();
// notify plugin about shutdown, if it has a shutdown function
command_result cr = CR_OK;
if(plugin_shutdown)
cr = plugin_shutdown(con);
// wait for all calls to finish
access->wait();
// cleanup...
reset_lua();
parent->unregisterCommands(this);
commands.clear();
if(cr == CR_OK)
@ -360,6 +381,7 @@ command_result Plugin::on_update(color_ostream &out)
if(state == PS_LOADED && plugin_onupdate)
{
cr = plugin_onupdate(out);
Lua::Core::Reset(out, "plugin_onupdate");
}
access->lock_sub();
return cr;
@ -372,6 +394,7 @@ command_result Plugin::on_state_change(color_ostream &out, state_change_event ev
if(state == PS_LOADED && plugin_onstatechange)
{
cr = plugin_onstatechange(out, event);
Lua::Core::Reset(out, "plugin_onstatechange");
}
access->lock_sub();
return cr;
@ -418,6 +441,125 @@ Plugin::plugin_state Plugin::getState() const
return state;
}
void Plugin::index_lua(DFLibrary *lib)
{
if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands"))
{
for (; cmdlist->name; ++cmdlist)
{
auto &cmd = lua_commands[cmdlist->name];
if (!cmd) cmd = new LuaCommand(this,cmdlist->name);
cmd->command = cmdlist->command;
}
}
if (auto funlist = (FunctionReg*)LookupPlugin(lib, "plugin_lua_functions"))
{
for (; funlist->name; ++funlist)
{
auto &cmd = lua_functions[funlist->name];
if (!cmd) cmd = new LuaFunction(this,funlist->name);
cmd->identity = funlist->identity;
}
}
if (auto evlist = (EventReg*)LookupPlugin(lib, "plugin_lua_events"))
{
for (; evlist->name; ++evlist)
{
auto &cmd = lua_events[evlist->name];
if (!cmd) cmd = new LuaEvent(this,evlist->name);
cmd->handler.identity = evlist->event->get_handler();
cmd->event = evlist->event;
if (cmd->active)
cmd->event->bind(Lua::Core::State, cmd);
}
}
}
void Plugin::reset_lua()
{
for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
it->second->command = NULL;
for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it)
it->second->identity = NULL;
for (auto it = lua_events.begin(); it != lua_events.end(); ++it)
{
it->second->handler.identity = NULL;
it->second->event = NULL;
}
}
int Plugin::lua_cmd_wrapper(lua_State *state)
{
auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1));
RefAutoinc lock(cmd->owner->access);
if (!cmd->command)
luaL_error(state, "plugin command %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str());
return cmd->command(state);
}
int Plugin::lua_fun_wrapper(lua_State *state)
{
auto cmd = (LuaFunction*)lua_touserdata(state, UPVAL_CONTAINER_ID);
RefAutoinc lock(cmd->owner->access);
if (!cmd->identity)
luaL_error(state, "plugin function %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str());
return LuaWrapper::method_wrapper_core(state, cmd->identity);
}
void Plugin::open_lua(lua_State *state, int table)
{
table = lua_absindex(state, table);
RefAutolock lock(access);
for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
{
lua_pushlightuserdata(state, it->second);
lua_pushcclosure(state, lua_cmd_wrapper, 1);
lua_setfield(state, table, it->first.c_str());
}
for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it)
{
push_function(state, it->second);
lua_setfield(state, table, it->first.c_str());
}
if (Lua::IsCoreContext(state))
{
for (auto it = lua_events.begin(); it != lua_events.end(); ++it)
{
Lua::MakeEvent(state, it->second);
push_function(state, &it->second->handler);
lua_rawsetp(state, -2, NULL);
it->second->active = true;
if (it->second->event)
it->second->event->bind(state, it->second);
lua_setfield(state, table, it->first.c_str());
}
}
}
void Plugin::push_function(lua_State *state, LuaFunction *fn)
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &LuaWrapper::DFHACK_TYPETABLE_TOKEN);
lua_pushlightuserdata(state, NULL);
lua_pushfstring(state, "%s.%s()", name.c_str(), fn->name.c_str());
lua_pushlightuserdata(state, fn);
lua_pushcclosure(state, lua_fun_wrapper, 4);
}
PluginManager::PluginManager(Core * core)
{
#ifdef LINUX_BUILD

@ -61,6 +61,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/world.h"
#include "df/world_data.h"
#include "df/unit.h"
#include "df/unit_misc_trait.h"
#include "df/unit_soul.h"
#include "df/unit_skill.h"
#include "df/material.h"
@ -221,30 +222,30 @@ void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat,
void DFHack::describeName(NameInfo *info, df::language_name *name)
{
if (!name->first_name.empty())
info->set_first_name(name->first_name);
info->set_first_name(DF2UTF(name->first_name));
if (!name->nickname.empty())
info->set_nickname(name->nickname);
info->set_nickname(DF2UTF(name->nickname));
if (name->language >= 0)
info->set_language_id(name->language);
std::string lname = Translation::TranslateName(name, false, true);
if (!lname.empty())
info->set_last_name(lname);
info->set_last_name(DF2UTF(lname));
lname = Translation::TranslateName(name, true, true);
if (!lname.empty())
info->set_english_name(lname);
info->set_english_name(DF2UTF(lname));
}
void DFHack::describeNameTriple(NameTriple *info, const std::string &name,
const std::string &plural, const std::string &adj)
{
info->set_normal(name);
info->set_normal(DF2UTF(name));
if (!plural.empty() && plural != name)
info->set_plural(plural);
info->set_plural(DF2UTF(plural));
if (!adj.empty() && adj != name)
info->set_adjective(adj);
info->set_adjective(DF2UTF(adj));
}
void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
@ -256,7 +257,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
info->set_pos_y(unit->pos.y);
info->set_pos_z(unit->pos.z);
auto name = Units::GetVisibleName(unit);
auto name = Units::getVisibleName(unit);
if (name->has_name)
describeName(info->mutable_name(), name);
@ -316,6 +317,19 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
}
}
if (mask && mask->misc_traits())
{
auto &vec = unit -> status.misc_traits;
for (size_t i = 0; i < vec.size(); i++)
{
auto trait = vec[i];
auto item = info->add_misc_traits();
item->set_id(trait->id);
item->set_value(trait->value);
}
}
if (unit->curse.add_tags1.whole ||
unit->curse.add_tags2.whole ||
unit->curse.rem_tags1.whole ||
@ -614,6 +628,20 @@ static command_result ListSquads(color_ostream &stream,
return CR_OK;
}
static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn *in)
{
for (size_t i = 0; i < in->change_size(); i++)
{
auto change = in->change(i);
auto unit = df::unit::find(change.unit_id());
if (unit)
unit->status.labors[change.labor()] = change.value();
}
return CR_OK;
}
CoreService::CoreService() {
suspend_depth = 0;
@ -637,6 +665,8 @@ CoreService::CoreService() {
addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE);
addFunction("ListUnits", ListUnits);
addFunction("ListSquads", ListSquads);
addFunction("SetUnitLabors", SetUnitLabors);
}
CoreService::~CoreService()

@ -118,7 +118,8 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
string type, name, value;
const char *cstr_type = pMemEntry->Value();
type = cstr_type;
if(type == "global-address")
bool is_vtable = (type == "vtable-address");
if(is_vtable || type == "global-address")
{
const char *cstr_key = pMemEntry->Attribute("name");
if(!cstr_key)
@ -129,7 +130,11 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
cerr << "Dummy symbol table entry: " << cstr_key << endl;
continue;
}
mem->setAddress(cstr_key, strtol(cstr_value, 0, 0));
uint32_t addr = strtol(cstr_value, 0, 0);
if (is_vtable)
mem->setVTable(cstr_key, addr);
else
mem->setAddress(cstr_key, addr);
}
else if (type == "md5-hash")
{

@ -65,12 +65,6 @@ namespace DFHack
{
class df_window;
}
// anon type, pretty much
struct DFLibrary;
DFLibrary * OpenPlugin (const char * filename);
void * LookupPlugin (DFLibrary * plugin ,const char * function);
void ClosePlugin (DFLibrary * plugin);
// Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
// There should never be more than one instance
@ -96,6 +90,8 @@ namespace DFHack
static Core instance;
return instance;
}
/// check if the activity lock is owned by this thread
bool isSuspended(void);
/// try to acquire the activity lock
void Suspend(void);
/// return activity lock
@ -139,6 +135,10 @@ namespace DFHack
static void print(const char *format, ...);
static void printerr(const char *format, ...);
PluginManager *getPluginManager() { return plug_mgr; }
static void cheap_tokenise(std::string const& input, std::vector<std::string> &output);
private:
DFHack::Console con;

@ -425,6 +425,9 @@ namespace df
static compound_identity *get() { return &T::_identity; }
};
template<class T>
inline T* allocate() { return (T*)identity_traits<T>::get()->allocate(); }
template<class T>
struct enum_traits {};

@ -46,6 +46,13 @@ namespace df {
template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value>
struct function_wrapper {};
class DFHACK_EXPORT cur_lua_ostream_argument {
DFHack::color_ostream *out;
public:
cur_lua_ostream_argument(lua_State *state);
operator DFHack::color_ostream& () { return *out; }
};
/*
* Since templates can't match variable arg count,
* a separate specialization is needed for every
@ -63,10 +70,15 @@ namespace df {
CT *self = (CT*)DFHack::LuaWrapper::get_object_addr(state, base++, UPVAL_METHOD_NAME, "invoke");
#define LOAD_ARG(type) \
type v##type; df::identity_traits<type>::get()->lua_write(state, UPVAL_METHOD_NAME, &v##type, base++);
#define OSTREAM_ARG DFHack::color_ostream&
#define LOAD_OSTREAM(name) \
cur_lua_ostream_argument name(state);
#define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \
#define INSTANTIATE_RETURN_TYPE(FArgs) \
template<FW_TARGSC class RT> struct return_type<RT (*) FArgs> { typedef RT type; }; \
template<FW_TARGSC class RT, class CT> struct return_type<RT (CT::*) FArgs> { typedef RT type; }; \
template<FW_TARGSC class RT, class CT> struct return_type<RT (CT::*) FArgs> { typedef RT type; };
#define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \
template<FW_TARGS> struct function_wrapper<void (*) FArgs, true> { \
static const bool is_method = false; \
static const int num_args = Count; \
@ -92,26 +104,45 @@ namespace df {
#define FW_TARGSC
#define FW_TARGS
INSTANTIATE_RETURN_TYPE(())
INSTANTIATE_WRAPPERS(0, (), (), ;)
INSTANTIATE_WRAPPERS(0, (OSTREAM_ARG), (out), LOAD_OSTREAM(out);)
#undef FW_TARGS
#undef FW_TARGSC
#define FW_TARGSC FW_TARGS,
#define FW_TARGS class A1
INSTANTIATE_RETURN_TYPE((A1))
INSTANTIATE_WRAPPERS(1, (A1), (vA1), LOAD_ARG(A1);)
INSTANTIATE_WRAPPERS(1, (OSTREAM_ARG,A1), (out,vA1), LOAD_OSTREAM(out); LOAD_ARG(A1);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2
INSTANTIATE_RETURN_TYPE((A1,A2))
INSTANTIATE_WRAPPERS(2, (A1,A2), (vA1,vA2), LOAD_ARG(A1); LOAD_ARG(A2);)
INSTANTIATE_WRAPPERS(2, (OSTREAM_ARG,A1,A2), (out,vA1,vA2),
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3
INSTANTIATE_RETURN_TYPE((A1,A2,A3))
INSTANTIATE_WRAPPERS(3, (A1,A2,A3), (vA1,vA2,vA3), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);)
INSTANTIATE_WRAPPERS(3, (OSTREAM_ARG,A1,A2,A3), (out,vA1,vA2,vA3),
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4))
INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);)
INSTANTIATE_WRAPPERS(4, (OSTREAM_ARG,A1,A2,A3,A4), (out,vA1,vA2,vA3,vA4),
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5))
INSTANTIATE_WRAPPERS(5, (A1,A2,A3,A4,A5), (vA1,vA2,vA3,vA4,vA5),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4); LOAD_ARG(A5);)
#undef FW_TARGS
#undef FW_TARGSC
@ -120,6 +151,8 @@ INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4),
#undef INVOKE_RV
#undef LOAD_CLASS
#undef LOAD_ARG
#undef OSTREAM_ARG
#undef LOAD_OSTREAM
template<class T>
class function_identity : public function_identity_base {
@ -128,15 +161,15 @@ INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4),
public:
typedef function_wrapper<T> wrapper;
function_identity(T ptr)
: function_identity_base(wrapper::num_args), ptr(ptr) {};
function_identity(T ptr, bool vararg)
: function_identity_base(wrapper::num_args, vararg), ptr(ptr) {};
virtual void invoke(lua_State *state, int base) { wrapper::execute(state, base, ptr); }
};
template<class T>
inline function_identity_base *wrap_function(T ptr) {
inline function_identity_base *wrap_function(T ptr, bool vararg = false) {
// bah, but didn't have any idea how to allocate statically
return new function_identity<T>(ptr);
return new function_identity<T>(ptr, vararg);
}
}

@ -39,13 +39,17 @@ namespace DFHack
{
class DFHACK_EXPORT function_identity_base : public type_identity {
int num_args;
bool vararg;
public:
function_identity_base(int num_args) : type_identity(0), num_args(num_args) {};
function_identity_base(int num_args, bool vararg = false)
: type_identity(0), num_args(num_args), vararg(vararg) {};
virtual identity_type type() { return IDTYPE_FUNCTION; }
int getNumArgs() { return num_args; }
bool adjustArgs() { return vararg; }
std::string getFullName() { return "function"; }
virtual void invoke(lua_State *state, int base) = 0;
@ -169,7 +173,7 @@ namespace df
using DFHack::ptr_container_identity;
using DFHack::bit_container_identity;
class number_identity_base : public primitive_identity {
class DFHACK_EXPORT number_identity_base : public primitive_identity {
const char *name;
public:
@ -195,7 +199,7 @@ namespace df
virtual void write(void *ptr, double val) { *(T*)ptr = T(val); }
};
class bool_identity : public primitive_identity {
class DFHACK_EXPORT bool_identity : public primitive_identity {
public:
bool_identity() : primitive_identity(sizeof(bool)) {};
@ -205,7 +209,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class ptr_string_identity : public primitive_identity {
class DFHACK_EXPORT ptr_string_identity : public primitive_identity {
public:
ptr_string_identity() : primitive_identity(sizeof(char*)) {};
@ -215,7 +219,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_string_identity : public DFHack::constructed_identity {
class DFHACK_EXPORT stl_string_identity : public DFHack::constructed_identity {
public:
stl_string_identity()
: constructed_identity(sizeof(std::string), &allocator_fn<std::string>)
@ -231,7 +235,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_ptr_vector_identity : public ptr_container_identity {
class DFHACK_EXPORT stl_ptr_vector_identity : public ptr_container_identity {
public:
typedef std::vector<void*> container;
@ -274,6 +278,8 @@ namespace df
}
};
// Due to export issues, this stuff can only work in the main dll
#ifdef BUILD_DFHACK_LIB
class buffer_container_identity : public container_identity {
int size;
@ -368,8 +374,9 @@ namespace df
((container*)ptr)->set(idx, val);
}
};
#endif
class stl_bit_vector_identity : public bit_container_identity {
class DFHACK_EXPORT stl_bit_vector_identity : public bit_container_identity {
public:
typedef std::vector<bool> container;
@ -398,6 +405,7 @@ namespace df
}
};
#ifdef BUILD_DFHACK_LIB
template<class T>
class enum_list_attr_identity : public container_identity {
public:
@ -419,9 +427,10 @@ namespace df
return (void*)&((container*)ptr)->items[idx];
}
};
#endif
#define NUMBER_IDENTITY_TRAITS(type) \
template<> struct identity_traits<type> { \
template<> struct DFHACK_EXPORT identity_traits<type> { \
static number_identity<type> identity; \
static number_identity_base *get() { return &identity; } \
};
@ -437,37 +446,37 @@ namespace df
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
template<> struct identity_traits<bool> {
template<> struct DFHACK_EXPORT identity_traits<bool> {
static bool_identity identity;
static bool_identity *get() { return &identity; }
};
template<> struct identity_traits<std::string> {
template<> struct DFHACK_EXPORT identity_traits<std::string> {
static stl_string_identity identity;
static stl_string_identity *get() { return &identity; }
};
template<> struct identity_traits<char*> {
template<> struct DFHACK_EXPORT identity_traits<char*> {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }
};
template<> struct identity_traits<const char*> {
template<> struct DFHACK_EXPORT identity_traits<const char*> {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }
};
template<> struct identity_traits<void*> {
template<> struct DFHACK_EXPORT identity_traits<void*> {
static pointer_identity identity;
static pointer_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<void*> > {
template<> struct DFHACK_EXPORT identity_traits<std::vector<void*> > {
static stl_ptr_vector_identity identity;
static stl_ptr_vector_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<bool> > {
template<> struct DFHACK_EXPORT identity_traits<std::vector<bool> > {
static stl_bit_vector_identity identity;
static stl_bit_vector_identity *get() { return &identity; }
};
@ -476,14 +485,17 @@ namespace df
// Container declarations
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > {
static primitive_identity *get();
};
#endif
template<class T> struct identity_traits<T *> {
static pointer_identity *get();
};
#ifdef BUILD_DFHACK_LIB
template<class T, int sz> struct identity_traits<T [sz]> {
static container_identity *get();
};
@ -491,11 +503,13 @@ namespace df
template<class T> struct identity_traits<std::vector<T> > {
static container_identity *get();
};
#endif
template<class T> struct identity_traits<std::vector<T*> > {
static stl_ptr_vector_identity *get();
};
#ifdef BUILD_DFHACK_LIB
template<class T> struct identity_traits<std::deque<T> > {
static container_identity *get();
};
@ -516,13 +530,16 @@ namespace df
template<class T> struct identity_traits<enum_list_attr<T> > {
static container_identity *get();
};
#endif
// Container definitions
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT>
inline primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
return identity_traits<FT>::get();
}
#endif
template<class T>
inline pointer_identity *identity_traits<T *>::get() {
@ -530,6 +547,7 @@ namespace df
return &identity;
}
#ifdef BUILD_DFHACK_LIB
template<class T, int sz>
inline container_identity *identity_traits<T [sz]>::get() {
static buffer_container_identity identity(sz, identity_traits<T>::get());
@ -542,6 +560,7 @@ namespace df
static stl_container_identity<container> identity("vector", identity_traits<T>::get());
return &identity;
}
#endif
template<class T>
inline stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
@ -549,6 +568,7 @@ namespace df
return &identity;
}
#ifdef BUILD_DFHACK_LIB
template<class T>
inline container_identity *identity_traits<std::deque<T> >::get() {
typedef std::deque<T> container;
@ -574,5 +594,5 @@ namespace df
static enum_list_attr_identity<T> identity(identity_traits<T>::get());
return &identity;
}
#endif
}

@ -39,6 +39,18 @@ namespace DFHack
* the whole array of DFHack exceptions from the rest
*/
class DFHACK_EXPORT All : public std::exception{};
class DFHACK_EXPORT NullPointer : public All {
const char *varname_;
public:
NullPointer(const char *varname_ = NULL) : varname_(varname_) {}
const char *varname() const { return varname_; }
virtual const char *what() const throw();
};
#define CHECK_NULL_POINTER(var) \
{ if (var == NULL) throw DFHack::Error::NullPointer(#var); }
class DFHACK_EXPORT AllSymbols : public All{};
// Syntax errors and whatnot, the xml can't be read
class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols

@ -34,18 +34,33 @@ distribution.
#include <lua.h>
#include <lauxlib.h>
namespace DFHack { namespace Lua {
namespace DFHack {
class function_identity_base;
}
namespace DFHack {namespace Lua {
/**
* Create or initialize a lua interpreter with access to DFHack tools.
*/
DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL);
/**
* Load a module using require().
* Load a module using require(). Leaves the stack as is.
*/
DFHACK_EXPORT bool Require(color_ostream &out, lua_State *state,
const std::string &module, bool setglobal = false);
/**
* Push the module table, loading it using require() if necessary.
*/
DFHACK_EXPORT bool PushModule(color_ostream &out, lua_State *state, const char *module);
/**
* Push the public object name exported by the module. Uses PushModule.
*/
DFHACK_EXPORT bool PushModulePublic(color_ostream &out, lua_State *state,
const char *module, const char *name);
/**
* Check if the object at the given index is NIL or NULL.
*/
@ -79,6 +94,12 @@ namespace DFHack { namespace Lua {
*/
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
/**
* Check that the value is a wrapped DF object of the given type, and if so return the pointer.
* Otherwise throw an argument type error.
*/
DFHACK_EXPORT void *CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
/**
* Assign the value at val_index to the target of given identity using df.assign().
* Return behavior is of SafeCall below.
@ -102,6 +123,14 @@ namespace DFHack { namespace Lua {
return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
}
/**
* Check that the value is a wrapped DF object of the correct type, and if so return the pointer. Otherwise throw an argument type error.
*/
template<class T>
T *CheckDFObject(lua_State *state, int val_index, bool exact_type = false) {
return (T*)CheckDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
}
/**
* Assign the value at val_index to the target using df.assign().
*/
@ -110,12 +139,56 @@ namespace DFHack { namespace Lua {
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
}
/**
* Check if the status is a success, i.e. LUA_OK or LUA_YIELD.
*/
inline bool IsSuccess(int status) {
return (status == LUA_OK || status == LUA_YIELD);
}
// Internal helper
template<int (*cb)(lua_State*,int,int)>
int TailPCallK_Thunk(lua_State *state) {
int tmp;
int rv = lua_getctx(state, &tmp);
return cb(state, rv, tmp);
}
/**
* A utility for using the restartable pcall feature more conveniently;
* specifically, the callback is called with the same kind of arguments
* in both yield and non-yield case.
*/
template<int (*cb)(lua_State*,int,int)>
int TailPCallK(lua_State *state, int narg, int nret, int errfun, int ctx) {
int rv = lua_pcallk(state, narg, nret, errfun, ctx, &TailPCallK_Thunk<cb>);
return cb(state, rv, ctx);
}
/**
* Invoke lua function via pcall. Returns true if success.
* If an error is signalled, and perr is true, it is printed and popped from the stack.
*/
DFHACK_EXPORT bool SafeCall(color_ostream &out, lua_State *state, int nargs, int nres, bool perr = true);
/**
* Pops a function from the top of the stack, and pushes a new coroutine.
*/
DFHACK_EXPORT lua_State *NewCoroutine(lua_State *state);
/**
* Resume the coroutine using nargs values from state from. Results or the error are moved back.
* If an error is signalled, and perr is true, it is printed and popped from the stack.
* Returns the lua_resume return value.
*/
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, lua_State *thread, int nargs, int nres, bool perr = true);
/**
* Works just like SafeCall, only expects a coroutine on the stack
* instead of a function. Returns the lua_resume return value.
*/
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, int nargs, int nres, bool perr = true);
/**
* Parse code from string with debug_tag and env_idx, then call it using SafeCall.
* In case of error, it is either left on the stack, or printed like SafeCall does.
@ -131,8 +204,165 @@ namespace DFHack { namespace Lua {
/**
* Run an interactive interpreter loop if possible, or return false.
* Uses RunCoreQueryLoop internally.
*/
DFHACK_EXPORT bool InterpreterLoop(color_ostream &out, lua_State *state,
const char *prompt = NULL, int env = 0, const char *hfile = NULL);
const char *prompt = NULL, const char *hfile = NULL);
/**
* Run an interactive prompt loop. All access to the lua state
* is done inside CoreSuspender, while waiting for input happens
* without the suspend lock.
*/
DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state,
bool (*init)(color_ostream&, lua_State*, void*),
void *arg);
/**
* Push utility functions
*/
#if 0
#define NUMBER_PUSH(type) inline void Push(lua_State *state, type value) { lua_pushnumber(state, value); }
NUMBER_PUSH(char)
NUMBER_PUSH(int8_t) NUMBER_PUSH(uint8_t)
NUMBER_PUSH(int16_t) NUMBER_PUSH(uint16_t)
NUMBER_PUSH(int32_t) NUMBER_PUSH(uint32_t)
NUMBER_PUSH(int64_t) NUMBER_PUSH(uint64_t)
NUMBER_PUSH(float) NUMBER_PUSH(double)
#undef NUMBER_PUSH
#else
template<class T> inline void Push(lua_State *state, T value) {
lua_pushnumber(state, lua_Number(value));
}
#endif
inline void Push(lua_State *state, bool value) {
lua_pushboolean(state, value);
}
inline void Push(lua_State *state, const std::string &str) {
lua_pushlstring(state, str.data(), str.size());
}
inline void Push(lua_State *state, df::coord &obj) { PushDFObject(state, &obj); }
inline void Push(lua_State *state, df::coord2d &obj) { PushDFObject(state, &obj); }
template<class T> inline void Push(lua_State *state, T *ptr) {
PushDFObject(state, ptr);
}
template<class T>
void PushVector(lua_State *state, const T &pvec)
{
lua_createtable(state,pvec.size(),0);
for (size_t i = 0; i < pvec.size(); i++)
{
Push(state, pvec[i]);
lua_rawseti(state, -2, i+1);
}
}
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
DFHACK_EXPORT bool IsCoreContext(lua_State *state);
DFHACK_EXPORT int NewEvent(lua_State *state);
DFHACK_EXPORT void MakeEvent(lua_State *state, void *key);
DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args);
/**
* Namespace for the common lua interpreter state.
* All accesses must be done under CoreSuspender.
*/
namespace Core {
DFHACK_EXPORT extern lua_State *State;
// Not exported; for use by the Core class
void Init(color_ostream &out);
void Reset(color_ostream &out, const char *where);
template<class T> inline void Push(T &arg) { Lua::Push(State, arg); }
template<class T> inline void Push(const T &arg) { Lua::Push(State, arg); }
template<class T> inline void PushVector(const T &arg) { Lua::PushVector(State, arg); }
inline bool SafeCall(color_ostream &out, int nargs, int nres, bool perr = true) {
return Lua::SafeCall(out, State, nargs, nres, perr);
}
inline bool PushModule(color_ostream &out, const char *module) {
return Lua::PushModule(out, State, module);
}
inline bool PushModulePublic(color_ostream &out, const char *module, const char *name) {
return Lua::PushModulePublic(out, State, module, name);
}
}
class DFHACK_EXPORT Notification {
lua_State *state;
void *key;
function_identity_base *handler;
public:
Notification(function_identity_base *handler = NULL)
: state(NULL), key(NULL), handler(handler) {}
lua_State *get_state() { return state; }
function_identity_base *get_handler() { return handler; }
void invoke(color_ostream &out, int nargs);
void bind(lua_State *state, const char *name);
void bind(lua_State *state, void *key);
};
}}
#define DEFINE_LUA_EVENT_0(name, handler) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out) { \
handler(out); \
if (name##_event.get_state()) { \
name##_event.invoke(out, 0); \
} \
}
#define DEFINE_LUA_EVENT_1(name, handler, arg_type1) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1) { \
handler(out, arg1); \
if (auto state = name##_event.get_state()) { \
DFHack::Lua::Push(state, arg1); \
name##_event.invoke(out, 1); \
} \
}
#define DEFINE_LUA_EVENT_2(name, handler, arg_type1, arg_type2) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \
handler(out, arg1, arg2); \
if (auto state = name##_event.get_state()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
name##_event.invoke(out, 2); \
} \
}
#define DEFINE_LUA_EVENT_3(name, handler, arg_type1, arg_type2, arg_type3) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \
handler(out, arg1, arg2, arg3); \
if (auto state = name##_event.get_state()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
name##_event.invoke(out, 3); \
} \
}
#define DEFINE_LUA_EVENT_4(name, handler, arg_type1, arg_type2, arg_type3, arg_type4) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \
handler(out, arg1, arg2, arg3, arg4); \
if (auto state = name##_event.get_state()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
name##_event.invoke(out, 4); \
} \
}

@ -30,6 +30,7 @@ distribution.
#include <map>
#include "DataDefs.h"
#include "PluginManager.h"
#include <lua.h>
#include <lauxlib.h>
@ -156,7 +157,7 @@ namespace DFHack { namespace LuaWrapper {
* Verify that the object is a DF ref with UPVAL_METATABLE.
* If everything ok, extract the address.
*/
uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode);
DFHACK_EXPORT uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode);
bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
type_identity *type2, int meta2, bool exact_equal);
@ -221,6 +222,20 @@ namespace DFHack { namespace LuaWrapper {
*/
void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum);
/**
* Push a closure invoking the given function.
*/
void PushFunctionWrapper(lua_State *state, int meta_idx,
const char *name, function_identity_base *fun);
/**
* Wrap functions and add them to the table on the top of the stack.
*/
using DFHack::FunctionReg;
void SetFunctionWrappers(lua_State *state, const FunctionReg *reg);
int method_wrapper_core(lua_State *state, function_identity_base *id);
void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct);
void AttachDFGlobals(lua_State *state);

@ -207,6 +207,22 @@ unsigned insert_into_vector(std::vector<CT*> &vec, FT CT::*field, CT *obj, bool
return pos;
}
template<typename FT>
bool erase_from_vector(std::vector<FT> &vec, FT key)
{
int pos = binsearch_index(vec, key);
vector_erase_at(vec, pos);
return pos >= 0;
}
template<typename CT, typename FT>
bool erase_from_vector(std::vector<CT*> &vec, FT CT::*field, FT key)
{
int pos = binsearch_index(vec, field, key);
vector_erase_at(vec, pos);
return pos >= 0;
}
template <typename CT, typename KT>
CT *binsearch_in_vector(const std::vector<CT*> &vec, KT value)
{
@ -279,3 +295,7 @@ DFHACK_EXPORT uint64_t GetTimeMs64();
DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...);
DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args);
// Conversion between CP437 and UTF-8
DFHACK_EXPORT std::string UTF2DF(const std::string &in);
DFHACK_EXPORT std::string DF2UTF(const std::string &in);

@ -33,7 +33,8 @@ distribution.
#include "RemoteClient.h"
struct DFLibrary;
typedef struct lua_State lua_State;
namespace tthread
{
class mutex;
@ -49,6 +50,20 @@ namespace DFHack
class PluginManager;
class virtual_identity;
class RPCService;
class function_identity_base;
namespace Lua {
class Notification;
}
// anon type, pretty much
struct DFLibrary;
// Open a plugin library
DFLibrary * OpenPlugin (const char * filename);
// find a symbol inside plugin
void * LookupPlugin (DFLibrary * plugin ,const char * function);
// Close a plugin library
void ClosePlugin (DFLibrary * plugin);
enum state_change_event
{
@ -56,7 +71,21 @@ namespace DFHack
SC_WORLD_UNLOADED,
SC_MAP_LOADED,
SC_MAP_UNLOADED,
SC_VIEWSCREEN_CHANGED
SC_VIEWSCREEN_CHANGED,
SC_CORE_INITIALIZED,
SC_BEGIN_UNLOAD
};
struct DFHACK_EXPORT CommandReg {
const char *name;
int (*command)(lua_State*);
};
struct DFHACK_EXPORT FunctionReg {
const char *name;
function_identity_base *identity;
};
struct DFHACK_EXPORT EventReg {
const char *name;
Lua::Notification *event;
};
struct DFHACK_EXPORT PluginCommand
{
@ -102,6 +131,7 @@ namespace DFHack
{
struct RefLock;
struct RefAutolock;
struct RefAutoinc;
enum plugin_state
{
PS_UNLOADED,
@ -138,6 +168,9 @@ namespace DFHack
{
return name;
}
void open_lua(lua_State *state, int table);
private:
RefLock * access;
std::vector <PluginCommand> commands;
@ -147,6 +180,37 @@ namespace DFHack
DFLibrary * plugin_lib;
PluginManager * parent;
plugin_state state;
struct LuaCommand {
Plugin *owner;
std::string name;
int (*command)(lua_State *state);
LuaCommand(Plugin *owner, std::string name) : owner(owner), name(name) {}
};
std::map<std::string, LuaCommand*> lua_commands;
static int lua_cmd_wrapper(lua_State *state);
struct LuaFunction {
Plugin *owner;
std::string name;
function_identity_base *identity;
LuaFunction(Plugin *owner, std::string name) : owner(owner), name(name) {}
};
std::map<std::string, LuaFunction*> lua_functions;
static int lua_fun_wrapper(lua_State *state);
void push_function(lua_State *state, LuaFunction *fn);
struct LuaEvent {
LuaFunction handler;
Lua::Notification *event;
bool active;
LuaEvent(Plugin *owner, std::string name) : handler(owner,name), active(false) {}
};
std::map<std::string, LuaEvent*> lua_events;
void index_lua(DFLibrary *lib);
void reset_lua();
command_result (*plugin_init)(color_ostream &, std::vector <PluginCommand> &);
command_result (*plugin_status)(color_ostream &, std::string &);
command_result (*plugin_shutdown)(color_ostream &);
@ -199,5 +263,18 @@ namespace DFHack
};
/// You have to have this in every plugin you write - just once. Ideally on top of the main file.
#define DFHACK_PLUGIN(plugin_name) DFhackDataExport const char * version = DFHACK_VERSION;\
DFhackDataExport const char * name = plugin_name;
#define DFHACK_PLUGIN(plugin_name) \
DFhackDataExport const char * version = DFHACK_VERSION;\
DFhackDataExport const char * name = plugin_name;
#define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \
DFhackCExport const DFHack::FunctionReg plugin_lua_functions[] =
#define DFHACK_PLUGIN_LUA_EVENTS \
DFhackCExport const DFHack::EventReg plugin_lua_events[] =
#define DFHACK_LUA_COMMAND(name) { #name, name }
#define DFHACK_LUA_FUNCTION(name) { #name, df::wrap_function(name,true) }
#define DFHACK_LUA_EVENT(name) { #name, &name##_event }
#define DFHACK_LUA_END { NULL, NULL }

@ -66,7 +66,7 @@ namespace DFHack
};
struct RPCMessageHeader {
static const int MAX_MESSAGE_SIZE = 8*1048756;
static const int MAX_MESSAGE_SIZE = 8*1048576;
int16_t id;
int32_t size;

@ -203,6 +203,18 @@ namespace DFHack
return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype));
}
inline
bool isWalkable(df::tiletype tiletype)
{
return ENUM_ATTR(tiletype_shape, walkable, tileShape(tiletype));
}
inline
bool isWalkableUp(df::tiletype tiletype)
{
return ENUM_ATTR(tiletype_shape, walkable_up, tileShape(tiletype));
}
inline
bool isWallTerrain(df::tiletype tiletype)
{

@ -51,13 +51,15 @@ namespace DFHack
std::vector <std::string> md5_list;
std::vector <uint32_t> PE_list;
std::map <std::string, uint32_t> Addresses;
std::map <std::string, uint32_t> VTables;
uint32_t base;
int rebase_delta;
std::string version;
OSType OS;
public:
VersionInfo()
{
base = 0;
base = 0; rebase_delta = 0;
version = "invalid";
OS = OS_BAD;
};
@ -66,12 +68,15 @@ namespace DFHack
md5_list = rhs.md5_list;
PE_list = rhs.PE_list;
Addresses = rhs.Addresses;
VTables = rhs.VTables;
base = rhs.base;
rebase_delta = rhs.rebase_delta;
version = rhs.version;
OS = rhs.OS;
};
uint32_t getBase () const { return base; };
int getRebaseDelta() const { return rebase_delta; }
void setBase (const uint32_t _base) { base = _base; };
void rebaseTo(const uint32_t new_base)
{
@ -79,13 +84,11 @@ namespace DFHack
int64_t newx = new_base;
int64_t rebase = newx - old;
base = new_base;
auto iter = Addresses.begin();
while (iter != Addresses.end())
{
uint32_t & ref = (*iter).second;
ref += rebase;
iter ++;
}
rebase_delta += rebase;
for (auto iter = Addresses.begin(); iter != Addresses.end(); ++iter)
iter->second += rebase;
for (auto iter = VTables.begin(); iter != VTables.end(); ++iter)
iter->second += rebase;
};
void addMD5 (const std::string & _md5)
@ -125,6 +128,7 @@ namespace DFHack
value = (T) (*i).second;
return true;
};
uint32_t getAddress (const std::string& key) const
{
auto i = Addresses.find(key);
@ -133,6 +137,18 @@ namespace DFHack
return (*i).second;
}
void setVTable (const std::string& key, const uint32_t value)
{
VTables[key] = value;
};
void *getVTable (const std::string& key) const
{
auto i = VTables.find(key);
if(i == VTables.end())
return 0;
return (void*)i->second;
}
void setOS(const OSType os)
{
OS = os;

@ -0,0 +1,26 @@
inline bool getassignment( const df::coord2d &xy )
{
return getassignment(xy.x,xy.y);
}
inline bool getassignment( int x, int y )
{
return (tile_bitmask[y] & (1 << x));
}
inline void setassignment( const df::coord2d &xy, bool bit )
{
return setassignment(xy.x,xy.y, bit);
}
inline void setassignment( int x, int y, bool bit )
{
if(bit)
tile_bitmask[y] |= (1 << x);
else
tile_bitmask[y] &= ~(1 << x);
}
bool has_assignments()
{
for (int i = 0; i < 16; i++)
if (tile_bitmask[i])
return true;
return false;
}

@ -15,5 +15,12 @@ inline void setassignment( int x, int y, bool bit )
if(bit)
tile_bitmask[y] |= (1 << x);
else
tile_bitmask[y] &= 0xFFFF ^ (1 << x);
}
tile_bitmask[y] &= ~(1 << x);
}
bool has_assignments()
{
for (int i = 0; i < 16; i++)
if (tile_bitmask[i])
return true;
return false;
}

@ -39,3 +39,7 @@ coord2d operator%(int number) const
{
return coord2d((x+number)%number, (y+number)%number);
}
coord2d operator&(int number) const
{
return coord2d(x&number, y&number);
}

@ -42,6 +42,10 @@ namespace df
struct itemdef;
}
namespace MapExtras {
class MapCache;
}
/**
* \defgroup grp_items Items module and its types
* @ingroup grp_modules
@ -122,18 +126,21 @@ DFHACK_EXPORT bool copyItem(df::item * source, dfh_item & target);
/// write copied item back to its origin
DFHACK_EXPORT bool writeItem(const dfh_item & item);
/// who owns this item we already read?
DFHACK_EXPORT int32_t getItemOwnerID(const df::item * item);
DFHACK_EXPORT df::unit *getItemOwner(const df::item * item);
/// Retrieve the owner of the item.
DFHACK_EXPORT df::unit *getOwner(df::item *item);
/// Set the owner of the item. Pass NULL as unit to remove the owner.
DFHACK_EXPORT bool setOwner(df::item *item, df::unit *unit);
/// which item is it contained in?
DFHACK_EXPORT int32_t getItemContainerID(const df::item * item);
DFHACK_EXPORT df::item *getItemContainer(const df::item * item);
DFHACK_EXPORT df::item *getContainer(df::item *item);
/// which items does it contain?
DFHACK_EXPORT bool getContainedItems(const df::item * item, /*output*/ std::vector<int32_t> &items);
/// wipe out the owner records
DFHACK_EXPORT bool removeItemOwner(df::item * item);
/// read item references, filtered by class
DFHACK_EXPORT bool readItemRefs(const df::item * item, const df::general_ref_type type,
/*output*/ std::vector<int32_t> &values);
DFHACK_EXPORT void getContainedItems(df::item *item, /*output*/ std::vector<df::item*> *items);
/// Returns the true position of the item.
DFHACK_EXPORT df::coord getPosition(df::item *item);
DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos);
DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container);
}
}

@ -36,24 +36,32 @@ namespace df
struct job_item;
struct job_item_filter;
struct building;
struct unit;
}
namespace DFHack
{
// Duplicate the job structure. It is not linked into any DF lists.
DFHACK_EXPORT df::job *cloneJobStruct(df::job *job);
namespace Job {
// Duplicate the job structure. It is not linked into any DF lists.
DFHACK_EXPORT df::job *cloneJobStruct(df::job *job);
// Delete a cloned structure.
DFHACK_EXPORT void deleteJobStruct(df::job *job);
// Delete a cloned structure.
DFHACK_EXPORT void deleteJobStruct(df::job *job);
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);
DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(df::job *job);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
DFHACK_EXPORT df::building *getJobHolder(df::job *job);
// lists jobs with ids >= *id_var, and sets *id_var = *job_next_id;
DFHACK_EXPORT bool listNewlyCreated(std::vector<df::job*> *pvec, int *id_var);
}
DFHACK_EXPORT bool linkJobIntoWorld(df::job *job, bool new_id = true);
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);
DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
}
#endif

@ -33,251 +33,187 @@ distribution.
#include "df/map_block.h"
#include "df/block_square_event_mineralst.h"
#include "df/construction.h"
#include "df/item.h"
using namespace DFHack;
namespace MapExtras
{
void SquashVeins (DFCoord bcoord, mapblock40d & mb, t_blockmaterials & materials)
{
memset(materials,-1,sizeof(materials));
std::vector <df::block_square_event_mineralst *> veins;
Maps::SortBlockEvents(bcoord.x,bcoord.y,bcoord.z,&veins);
for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++)
{
df::tiletype tt = mb.tiletypes[x][y];
if (tileMaterial(tt) == tiletype_material::MINERAL)
{
for (size_t i = 0; i < veins.size(); i++)
{
if (veins[i]->getassignment(x,y))
materials[x][y] = veins[i]->inorganic_mat;
}
}
}
}
void SquashFrozenLiquids (DFCoord bcoord, mapblock40d & mb, tiletypes40d & frozen)
{
std::vector <df::block_square_event_frozen_liquidst *> ices;
Maps::SortBlockEvents(bcoord.x,bcoord.y,bcoord.z,NULL,&ices);
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
df::tiletype tt = mb.tiletypes[x][y];
frozen[x][y] = tiletype::Void;
if (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID)
{
for (size_t i = 0; i < ices.size(); i++)
{
df::tiletype tt2 = ices[i]->tiles[x][y];
if (tt2 != tiletype::Void)
{
frozen[x][y] = tt2;
break;
}
}
}
}
}
class DFHACK_EXPORT MapCache;
void SquashConstructions (DFCoord bcoord, mapblock40d & mb, tiletypes40d & constructions)
{
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
df::tiletype tt = mb.tiletypes[x][y];
constructions[x][y] = tiletype::Void;
if (tileMaterial(tt) == tiletype_material::CONSTRUCTION)
{
DFCoord coord(bcoord.x*16 + x, bcoord.y*16 + y, bcoord.z);
df::construction *con = df::construction::find(coord);
if (con)
constructions[x][y] = con->original_tile;
}
}
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
return v[p.x&15][p.y&15];
}
void SquashRocks ( std::vector< std::vector <uint16_t> > * layerassign, mapblock40d & mb, t_blockmaterials & materials)
{
// get the layer materials
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
materials[x][y] = -1;
uint8_t test = mb.designation[x][y].bits.biome;
if ((test < sizeof(mb.biome_indices)) && (mb.biome_indices[test] < layerassign->size()))
materials[x][y] = layerassign->at(mb.biome_indices[test])[mb.designation[x][y].bits.geolayer_index];
}
inline bool is_valid_tile_coord(df::coord2d p) {
return (p.x & ~15) == 0 && (p.y & ~15) == 0;
}
class Block
class DFHACK_EXPORT Block
{
public:
Block(DFCoord _bcoord, std::vector< std::vector <uint16_t> > * layerassign = 0)
{
dirty_designations = false;
dirty_tiletypes = false;
dirty_temperatures = false;
dirty_blockflags = false;
dirty_occupancies = false;
valid = false;
bcoord = _bcoord;
if(Maps::ReadBlock40d(bcoord.x,bcoord.y,bcoord.z,&raw))
{
Maps::ReadTemperatures(bcoord.x,bcoord.y, bcoord.z,&temp1,&temp2);
SquashVeins(bcoord,raw,veinmats);
SquashConstructions(bcoord, raw, contiles);
SquashFrozenLiquids(bcoord, raw, icetiles);
if(layerassign)
SquashRocks(layerassign,raw,basemats);
else
memset(basemats,-1,sizeof(basemats));
valid = true;
}
public:
Block(MapCache *parent, DFCoord _bcoord);
~Block();
/*
* All coordinates are taken mod 16.
*/
//Arbitrary tag field for flood fills etc.
int16_t &tag(df::coord2d p) {
return index_tile<int16_t&>(tags, p);
}
int16_t veinMaterialAt(df::coord2d p)
{
return veinmats[p.x][p.y];
return index_tile<int16_t>(veinmats,p);
}
int16_t baseMaterialAt(df::coord2d p)
{
return basemats[p.x][p.y];
}
// the clear methods are used by the floodfill in digv and digl to mark tiles which were processed
void ClearBaseMaterialAt(df::coord2d p)
{
basemats[p.x][p.y] = -1;
}
void ClearVeinMaterialAt(df::coord2d p)
{
veinmats[p.x][p.y] = -1;
return index_tile<int16_t>(basemats,p);
}
df::tiletype BaseTileTypeAt(df::coord2d p)
{
if (contiles[p.x][p.y] != tiletype::Void)
return contiles[p.x][p.y];
else if (icetiles[p.x][p.y] != tiletype::Void)
return icetiles[p.x][p.y];
else
return raw.tiletypes[p.x][p.y];
auto tt = index_tile<df::tiletype>(contiles,p);
if (tt != tiletype::Void) return tt;
tt = index_tile<df::tiletype>(icetiles,p);
if (tt != tiletype::Void) return tt;
return index_tile<df::tiletype>(rawtiles,p);
}
df::tiletype TileTypeAt(df::coord2d p)
{
return raw.tiletypes[p.x][p.y];
return index_tile<df::tiletype>(rawtiles,p);
}
bool setTiletypeAt(df::coord2d p, df::tiletype tiletype)
{
if(!valid) return false;
dirty_tiletypes = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
raw.tiletypes[p.x][p.y] = tiletype;
index_tile<df::tiletype&>(rawtiles,p) = tiletype;
return true;
}
uint16_t temperature1At(df::coord2d p)
{
return temp1[p.x][p.y];
return index_tile<uint16_t>(temp1,p);
}
bool setTemp1At(df::coord2d p, uint16_t temp)
{
if(!valid) return false;
dirty_temperatures = true;
temp1[p.x][p.y] = temp;
index_tile<uint16_t&>(temp1,p) = temp;
return true;
}
uint16_t temperature2At(df::coord2d p)
{
return temp2[p.x][p.y];
return index_tile<uint16_t>(temp2,p);
}
bool setTemp2At(df::coord2d p, uint16_t temp)
{
if(!valid) return false;
dirty_temperatures = true;
temp2[p.x][p.y] = temp;
index_tile<uint16_t&>(temp2,p) = temp;
return true;
}
df::tile_designation DesignationAt(df::coord2d p)
{
return raw.designation[p.x][p.y];
return index_tile<df::tile_designation>(designation,p);
}
bool setDesignationAt(df::coord2d p, df::tile_designation des)
{
if(!valid) return false;
dirty_designations = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
raw.designation[p.x][p.y] = des;
index_tile<df::tile_designation&>(designation,p) = des;
if(des.bits.dig)
{
dirty_blockflags = true;
raw.blockflags.bits.designated = true;
blockflags.bits.designated = true;
}
return true;
}
df::tile_occupancy OccupancyAt(df::coord2d p)
{
return raw.occupancy[p.x][p.y];
return index_tile<df::tile_occupancy>(occupancy,p);
}
bool setOccupancyAt(df::coord2d p, df::tile_occupancy des)
{
if(!valid) return false;
dirty_occupancies = true;
raw.occupancy[p.x][p.y] = des;
index_tile<df::tile_occupancy&>(occupancy,p) = des;
return true;
}
int itemCountAt(df::coord2d p)
{
if (!item_counts) init_item_counts();
return index_tile<int>(item_counts,p);
}
t_blockflags BlockFlags()
{
return raw.blockflags;
return blockflags;
}
bool setBlockFlags(t_blockflags des)
{
if(!valid) return false;
dirty_blockflags = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
raw.blockflags = des;
blockflags = des;
return true;
}
bool Write ()
{
if(!valid) return false;
if(dirty_designations)
{
Maps::WriteDesignations(bcoord.x,bcoord.y,bcoord.z, &raw.designation);
Maps::WriteDirtyBit(bcoord.x,bcoord.y,bcoord.z,true);
dirty_designations = false;
}
if(dirty_tiletypes)
{
Maps::WriteTileTypes(bcoord.x,bcoord.y,bcoord.z, &raw.tiletypes);
dirty_tiletypes = false;
}
if(dirty_temperatures)
{
Maps::WriteTemperatures(bcoord.x,bcoord.y,bcoord.z, &temp1, &temp2);
dirty_temperatures = false;
}
if(dirty_blockflags)
{
Maps::WriteBlockFlags(bcoord.x,bcoord.y,bcoord.z,raw.blockflags);
dirty_blockflags = false;
}
if(dirty_occupancies)
{
Maps::WriteOccupancy(bcoord.x,bcoord.y,bcoord.z,&raw.occupancy);
dirty_occupancies = false;
}
return true;
}
bool valid:1;
bool Write();
df::coord2d biomeRegionAt(df::coord2d p);
int16_t GeoIndexAt(df::coord2d p);
bool GetGlobalFeature(t_feature *out);
bool GetLocalFeature(t_feature *out);
bool is_valid() { return valid; }
df::map_block *getRaw() { return block; }
private:
friend class MapCache;
MapCache *parent;
df::map_block *block;
static void SquashVeins(df::map_block *mb, t_blockmaterials & materials);
static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen);
static void SquashConstructions (df::map_block *mb, tiletypes40d & constructions);
static void SquashRocks (df::map_block *mb, t_blockmaterials & materials,
std::vector< std::vector <int16_t> > * layerassign);
bool valid;
bool dirty_designations:1;
bool dirty_tiletypes:1;
bool dirty_temperatures:1;
bool dirty_blockflags:1;
bool dirty_occupancies:1;
mapblock40d raw;
DFCoord bcoord;
int16_t tags[16][16];
typedef int T_item_counts[16];
T_item_counts *item_counts;
void init_item_counts();
bool addItemOnGround(df::item *item);
bool removeItemOnGround(df::item *item);
tiletypes40d rawtiles;
designations40d designation;
occupancies40d occupancy;
t_blockflags blockflags;
t_blockmaterials veinmats;
t_blockmaterials basemats;
t_temperatures temp1;
@ -286,14 +222,15 @@ class Block
tiletypes40d icetiles; // what's underneath ice
};
class MapCache
class DFHACK_EXPORT MapCache
{
public:
MapCache()
{
valid = 0;
Maps::getSize(x_bmax, y_bmax, z_max);
validgeo = Maps::ReadGeology( layerassign );
x_tmax = x_bmax*16; y_tmax = y_bmax*16;
validgeo = Maps::ReadGeology(&layer_mats, &geoidx);
valid = true;
};
~MapCache()
@ -304,192 +241,116 @@ class MapCache
{
return valid;
}
/// get the map block at a *block* coord. Block coord = tile coord / 16
Block * BlockAt (DFCoord blockcoord)
{
if(!valid)
return 0;
std::map <DFCoord, Block*>::iterator iter = blocks.find(blockcoord);
if(iter != blocks.end())
{
return (*iter).second;
}
else
{
if(blockcoord.x >= 0 && blockcoord.x < x_bmax &&
blockcoord.y >= 0 && blockcoord.y < y_bmax &&
blockcoord.z >= 0 && blockcoord.z < z_max)
{
Block * nblo;
if(validgeo)
nblo = new Block(blockcoord, &layerassign);
else
nblo = new Block(blockcoord);
blocks[blockcoord] = nblo;
return nblo;
}
return 0;
}
Block *BlockAt(DFCoord blockcoord);
/// get the map block at a tile coord.
Block *BlockAtTile(DFCoord coord) {
return BlockAt(df::coord(coord.x>>4,coord.y>>4,coord.z));
}
df::tiletype baseTiletypeAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->BaseTileTypeAt(tilecoord % 16);
}
return tiletype::Void;
Block * b= BlockAtTile(tilecoord);
return b ? b->BaseTileTypeAt(tilecoord) : tiletype::Void;
}
df::tiletype tiletypeAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->TileTypeAt(tilecoord % 16);
}
return tiletype::Void;
Block * b= BlockAtTile(tilecoord);
return b ? b->TileTypeAt(tilecoord) : tiletype::Void;
}
bool setTiletypeAt(DFCoord tilecoord, df::tiletype tiletype)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->setTiletypeAt(tilecoord % 16, tiletype);
return true;
}
if (Block * b= BlockAtTile(tilecoord))
return b->setTiletypeAt(tilecoord, tiletype);
return false;
}
uint16_t temperature1At (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->temperature1At(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
return b ? b->temperature1At(tilecoord) : 0;
}
bool setTemp1At(DFCoord tilecoord, uint16_t temperature)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->setTemp1At(tilecoord % 16, temperature);
return true;
}
if (Block * b= BlockAtTile(tilecoord))
return b->setTemp1At(tilecoord, temperature);
return false;
}
uint16_t temperature2At (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->temperature2At(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
return b ? b->temperature2At(tilecoord) : 0;
}
bool setTemp2At(DFCoord tilecoord, uint16_t temperature)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->setTemp2At(tilecoord % 16, temperature);
return true;
}
if (Block * b= BlockAtTile(tilecoord))
return b->setTemp2At(tilecoord, temperature);
return false;
}
int16_t veinMaterialAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->veinMaterialAt(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
return b ? b->veinMaterialAt(tilecoord) : -1;
}
int16_t baseMaterialAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->baseMaterialAt(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
return b ? b->baseMaterialAt(tilecoord) : -1;
}
bool clearVeinMaterialAt (DFCoord tilecoord)
int16_t tagAt(DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->ClearVeinMaterialAt(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
return b ? b->tag(tilecoord) : 0;
}
bool clearBaseMaterialAt (DFCoord tilecoord)
void setTagAt(DFCoord tilecoord, int16_t val)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->ClearBaseMaterialAt(tilecoord % 16);
}
return 0;
Block * b= BlockAtTile(tilecoord);
if (b) b->tag(tilecoord) = val;
}
df::tile_designation designationAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->DesignationAt(tilecoord % 16);
}
df::tile_designation temp;
temp.whole = 0;
return temp;
Block * b= BlockAtTile(tilecoord);
return b ? b->DesignationAt(tilecoord) : df::tile_designation(0);
}
bool setDesignationAt (DFCoord tilecoord, df::tile_designation des)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->setDesignationAt(tilecoord % 16, des);
return true;
}
if(Block * b= BlockAtTile(tilecoord))
return b->setDesignationAt(tilecoord, des);
return false;
}
df::tile_occupancy occupancyAt (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return b->OccupancyAt(tilecoord % 16);
}
df::tile_occupancy temp;
temp.whole = 0;
return temp;
Block * b= BlockAtTile(tilecoord);
return b ? b->OccupancyAt(tilecoord) : df::tile_occupancy(0);
}
bool setOccupancyAt (DFCoord tilecoord, df::tile_occupancy occ)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
b->setOccupancyAt(tilecoord % 16, occ);
return true;
}
if (Block * b= BlockAtTile(tilecoord))
return b->setOccupancyAt(tilecoord, occ);
return false;
}
bool testCoord (DFCoord tilecoord)
{
Block * b= BlockAt(tilecoord / 16);
if(b && b->valid)
{
return true;
}
return false;
Block * b= BlockAtTile(tilecoord);
return (b && b->valid);
}
bool addItemOnGround(df::item *item) {
Block * b= BlockAtTile(item->pos);
return b ? b->addItemOnGround(item) : false;
}
bool removeItemOnGround(df::item *item) {
Block * b= BlockAtTile(item->pos);
return b ? b->removeItemOnGround(item) : false;
}
bool WriteAll()
{
std::map<DFCoord, Block *>::iterator p;
@ -508,15 +369,25 @@ class MapCache
}
blocks.clear();
}
private:
volatile bool valid;
volatile bool validgeo;
uint32_t maxBlockX() { return x_bmax; }
uint32_t maxBlockY() { return y_bmax; }
uint32_t maxTileX() { return x_tmax; }
uint32_t maxTileY() { return y_tmax; }
uint32_t maxZ() { return z_max; }
private:
friend class Block;
bool valid;
bool validgeo;
uint32_t x_bmax;
uint32_t y_bmax;
uint32_t x_tmax;
uint32_t y_tmax;
uint32_t z_max;
std::vector< std::vector <uint16_t> > layerassign;
std::vector<df::coord2d> geoidx;
std::vector< std::vector <int16_t> > layer_mats;
std::map<DFCoord, Block *> blocks;
};
}

@ -38,7 +38,7 @@ distribution.
#include "modules/Materials.h"
#include "df/world.h"
#include "df/feature_init.h"
#include "df/world_data.h"
#include "df/map_block.h"
#include "df/block_square_event.h"
#include "df/block_square_event_mineralst.h"
@ -49,12 +49,20 @@ distribution.
#include "df/tile_liquid.h"
#include "df/tile_dig_designation.h"
#include "df/tile_traffic.h"
#include "df/feature_init.h"
/**
* \defgroup grp_maps Maps module and its types
* @ingroup grp_modules
*/
namespace df
{
struct burrow;
struct world_data;
struct block_burrow;
}
namespace DFHack
{
/***************************************************************************
@ -103,31 +111,11 @@ enum BiomeOffset
eBiomeCount
};
/**
* map block flags
* \ingroup grp_maps
*/
struct naked_blockflags
{
/// designated for jobs (digging and stuff like that)
unsigned int designated : 1;
/// possibly related to the designated flag
unsigned int unk_1 : 1;
/// two flags required for liquid flow.
unsigned int liquid_1 : 1;
unsigned int liquid_2 : 1;
/// rest of the flags is completely unknown
unsigned int unk_2: 4;
};
/**
* map block flags wrapper
* \ingroup grp_maps
*/
union t_blockflags
{
uint32_t whole;
naked_blockflags bits;
};
typedef df::block_flags t_blockflags;
/**
* 16x16 array of tile types
@ -161,30 +149,6 @@ typedef uint8_t biome_indices40d [9];
* \ingroup grp_maps
*/
typedef uint16_t t_temperatures [16][16];
/**
* structure for holding whole blocks
* \ingroup grp_maps
*/
typedef struct
{
DFCoord position;
/// type of the tiles
tiletypes40d tiletypes;
/// flags determining the state of the tiles
designations40d designation;
/// flags determining what's on the tiles
occupancies40d occupancy;
/// values used for geology/biome assignment
biome_indices40d biome_indices;
/// the address where the block came from
df::map_block * origin;
t_blockflags blockflags;
/// index into the global feature vector
int32_t global_feature;
/// index into the local feature vector... complicated
int32_t local_feature;
int32_t mystery;
} mapblock40d;
/**
* The Maps module
@ -231,30 +195,33 @@ void DfMap::applyGeoMatgloss(Block * b)
* @endcode
*/
extern DFHACK_EXPORT bool ReadGeology( std::vector < std::vector <uint16_t> >& assign );
extern DFHACK_EXPORT bool ReadGeology(std::vector<std::vector<int16_t> > *layer_mats,
std::vector<df::coord2d> *geoidx);
/**
* Get the feature indexes of a block
* Get pointers to features of a block
*/
extern DFHACK_EXPORT bool ReadFeatures(uint32_t x, uint32_t y, uint32_t z, int32_t & local, int32_t & global);
extern DFHACK_EXPORT bool ReadFeatures(uint32_t x, uint32_t y, uint32_t z, t_feature * local, t_feature * global);
/**
* Set the feature indexes of a block
* Get pointers to features of an already read block
*/
extern DFHACK_EXPORT bool WriteFeatures(uint32_t x, uint32_t y, uint32_t z, const int32_t & local, const int32_t & global);
extern DFHACK_EXPORT bool ReadFeatures(df::map_block * block,t_feature * local, t_feature * global);
/**
* Get pointers to features of a block
* Get a pointer to a specific global feature directly.
*/
extern DFHACK_EXPORT bool ReadFeatures(uint32_t x, uint32_t y, uint32_t z, t_feature * local, t_feature * global);
DFHACK_EXPORT df::feature_init *getGlobalInitFeature(int32_t index);
/**
* Get pointers to features of an already read block
* Get a pointer to a specific local feature directly. rgn_coord is in the world region grid.
*/
extern DFHACK_EXPORT bool ReadFeatures(mapblock40d * block,t_feature * local, t_feature * global);
DFHACK_EXPORT df::feature_init *getLocalInitFeature(df::coord2d rgn_coord, int32_t index);
/**
* Read a specific global or local feature directly
*/
extern DFHACK_EXPORT bool GetGlobalFeature(t_feature &feature, int32_t index);
extern DFHACK_EXPORT bool GetLocalFeature(t_feature &feature, df::coord2d coord, int32_t index);
//extern DFHACK_EXPORT bool GetLocalFeature(t_feature &feature, df::coord2d rgn_coord, int32_t index);
/*
* BLOCK DATA
@ -269,48 +236,16 @@ extern DFHACK_EXPORT void getPosition(int32_t& x, int32_t& y, int32_t& z);
* Get the map block or NULL if block is not valid
*/
extern DFHACK_EXPORT df::map_block * getBlock (int32_t blockx, int32_t blocky, int32_t blockz);
extern DFHACK_EXPORT df::map_block * getBlockAbs (int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t z);
inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); }
inline df::map_block * getBlockAbs (df::coord pos) { return getBlockAbs(pos.x, pos.y, pos.z); }
/// copy the whole map block at block coords (see DFTypes.h for the block structure)
extern DFHACK_EXPORT bool ReadBlock40d(uint32_t blockx, uint32_t blocky, uint32_t blockz, mapblock40d * buffer);
/// copy/write block tile types
extern DFHACK_EXPORT bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer);
extern DFHACK_EXPORT bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer);
/// copy/write block designations
extern DFHACK_EXPORT bool ReadDesignations(uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer);
extern DFHACK_EXPORT bool WriteDesignations (uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer);
/// copy/write temperatures
extern DFHACK_EXPORT bool ReadTemperatures(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_temperatures *temp1, t_temperatures *temp2);
extern DFHACK_EXPORT bool WriteTemperatures (uint32_t blockx, uint32_t blocky, uint32_t blockz, t_temperatures *temp1, t_temperatures *temp2);
inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); }
/// copy/write block occupancies
extern DFHACK_EXPORT bool ReadOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer);
extern DFHACK_EXPORT bool WriteOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer);
/// copy/write the block dirty bit - this is used to mark a map block so that DF scans it for designated jobs like digging
extern DFHACK_EXPORT bool ReadDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool &dirtybit);
extern DFHACK_EXPORT bool WriteDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool dirtybit);
/// copy/write the block flags
extern DFHACK_EXPORT bool ReadBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags &blockflags);
extern DFHACK_EXPORT bool WriteBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags blockflags);
/// copy/write features
extern DFHACK_EXPORT bool SetBlockLocalFeature(uint32_t blockx, uint32_t blocky, uint32_t blockz, int32_t local = -1);
extern DFHACK_EXPORT bool SetBlockGlobalFeature(uint32_t blockx, uint32_t blocky, uint32_t blockz, int32_t global = -1);
/// copy region offsets of a block - used for determining layer stone matgloss
extern DFHACK_EXPORT bool ReadRegionOffsets(uint32_t blockx, uint32_t blocky, uint32_t blockz, biome_indices40d *buffer);
DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos);
/// sorts the block event vector into multiple vectors by type
/// mineral veins, what's under ice, blood smears and mud
extern DFHACK_EXPORT bool SortBlockEvents(uint32_t x, uint32_t y, uint32_t z,
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,
@ -321,8 +256,33 @@ extern DFHACK_EXPORT bool SortBlockEvents(uint32_t x, uint32_t y, uint32_t z,
/// remove a block event from the block by address
extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which );
/// read all plants in this block
extern DFHACK_EXPORT bool ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector<df::plant *>*& plants);
/*
* BURROWS
*/
DFHACK_EXPORT df::burrow *findBurrowByName(std::string name);
DFHACK_EXPORT void listBurrowBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow);
DFHACK_EXPORT void clearBurrowTiles(df::burrow *burrow);
DFHACK_EXPORT df::block_burrow *getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create = false);
DFHACK_EXPORT bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask);
inline bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block)
{
return deleteBlockBurrowMask(burrow, block, getBlockBurrowMask(burrow, block));
}
DFHACK_EXPORT bool isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile);
DFHACK_EXPORT bool setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable);
inline bool isBurrowTile(df::burrow *burrow, df::coord tile) {
return isBlockBurrowTile(burrow, getTileBlock(tile), tile);
}
inline bool setBurrowTile(df::burrow *burrow, df::coord tile, bool enable) {
return setBlockBurrowTile(burrow, getTileBlock(tile), tile, enable);
}
}
}

@ -114,6 +114,7 @@ namespace DFHack
}
bool find(const std::string &token);
bool find(const std::vector<std::string> &tokens);
bool findBuiltin(const std::string &token);
bool findInorganic(const std::string &token);

@ -50,6 +50,8 @@ DFHACK_EXPORT bool IsValid ();
DFHACK_EXPORT bool readName(t_name & name, df::language_name * address);
DFHACK_EXPORT bool copyName(df::language_name * address, df::language_name * target);
DFHACK_EXPORT void setNickname(df::language_name *name, std::string nick);
// translate a name using the loaded dictionaries
DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true,
bool onlyLastPart = false);

@ -33,6 +33,12 @@ distribution.
#include "DataDefs.h"
#include "df/unit.h"
namespace df
{
struct nemesis_record;
struct burrow;
}
/**
* \defgroup grp_units Unit module parts
* @ingroup grp_modules
@ -179,9 +185,6 @@ DFHACK_EXPORT bool ReadJob(const df::unit * unit, std::vector<t_material> & mat)
DFHACK_EXPORT bool ReadInventoryByIdx(const uint32_t index, std::vector<df::item *> & item);
DFHACK_EXPORT bool ReadInventoryByPtr(const df::unit * unit, std::vector<df::item *> & item);
DFHACK_EXPORT bool ReadOwnedItemsByIdx(const uint32_t index, std::vector<int32_t> & item);
DFHACK_EXPORT bool ReadOwnedItemsByPtr(const df::unit * unit, std::vector<int32_t> & item);
DFHACK_EXPORT int32_t FindIndexById(int32_t id);
/* Getters */
@ -190,14 +193,27 @@ DFHACK_EXPORT int32_t GetDwarfCivId ( void );
DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target);
DFHACK_EXPORT bool RemoveOwnedItemByIdx(const uint32_t index, int32_t id);
DFHACK_EXPORT bool RemoveOwnedItemByPtr(df::unit * unit, int32_t id);
/// Returns the true position of the unit (non-trivial in case of caged).
DFHACK_EXPORT df::coord getPosition(df::unit *unit);
DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT df::language_name *GetVisibleName(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::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isDead(df::unit *unit);
DFHACK_EXPORT bool isAlive(df::unit *unit);
DFHACK_EXPORT bool isSane(df::unit *unit);
DFHACK_EXPORT bool isCitizen(df::unit *unit);
DFHACK_EXPORT bool isDwarf(df::unit *unit);
DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow);
DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow);
DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable);
}
}
#endif

@ -139,8 +139,16 @@ namespace DFHack
PersistentDataItem AddPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(int entry_id);
// Calls GetPersistentData(key); if not found, adds and sets added to true.
// The result can still be not isValid() e.g. if the world is not loaded.
PersistentDataItem GetPersistentData(const std::string &key, bool *added);
// Lists all items with the given key.
// If prefix is true, search for keys starting with key+"/".
// GetPersistentData(&vec,"",true) returns all items.
// Items have alphabetic order by key; same key ordering is undefined.
void GetPersistentData(std::vector<PersistentDataItem> *vec,
const std::string &key, bool prefix = false);
// Deletes the item; returns true if success.
bool DeletePersistentData(const PersistentDataItem &item);
void ClearPersistentCache();

@ -1,12 +1,51 @@
-- Common startup file for all dfhack plugins with lua support
-- The global dfhack table is already created by C++ init code.
-- Console color constants
COLOR_RESET = -1
COLOR_BLACK = 0
COLOR_BLUE = 1
COLOR_GREEN = 2
COLOR_CYAN = 3
COLOR_RED = 4
COLOR_MAGENTA = 5
COLOR_BROWN = 6
COLOR_GREY = 7
COLOR_DARKGREY = 8
COLOR_LIGHTBLUE = 9
COLOR_LIGHTGREEN = 10
COLOR_LIGHTCYAN = 11
COLOR_LIGHTRED = 12
COLOR_LIGHTMAGENTA = 13
COLOR_YELLOW = 14
COLOR_WHITE = 15
-- Error handling
safecall = dfhack.safecall
function dfhack.pcall(f, ...)
return xpcall(f, dfhack.onerror, ...)
end
function dfhack.with_finalize(...)
return dfhack.call_with_finalizer(0,true,...)
end
function dfhack.with_onerror(...)
return dfhack.call_with_finalizer(0,false,...)
end
local function call_delete(obj)
if obj then obj:delete() end
end
function dfhack.with_temp_object(obj,fn,...)
return dfhack.call_with_finalizer(1,true,call_delete,obj,fn,obj,...)
end
-- Module loading
function mkmodule(module,env)
local pkg = package.loaded[module]
if pkg == nil then
@ -16,6 +55,10 @@ function mkmodule(module,env)
error("Not a table in package.loaded["..module.."]")
end
end
local plugname = string.match(module,'^plugins%.(%w+)$')
if plugname then
dfhack.open_plugin(pkg,plugname)
end
setmetatable(pkg, { __index = (env or _G) })
return pkg
end
@ -31,17 +74,137 @@ function reload(module)
dofile(path)
end
-- Misc functions
function printall(table)
if table == nil then return end
for k,v in pairs(table) do
print(k," = "..tostring(v))
if type(table) == 'table' or df.isvalid(table) == 'ref' then
for k,v in pairs(table) 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
return rv
end
function pos2xyz(pos)
local x = pos.x
if x and x ~= -30000 then
return x, pos.y, pos.z
end
end
function xyz2pos(x,y,z)
if x then
return {x=x,y=y,z=z}
else
return {x=-30000,y=-30000,z=-30000}
end
end
function dfhack.event:__tostring()
return "<event>"
end
function dfhack.persistent:__tostring()
return "<persistent "..self.entry_id..":"..self.key.."=\""
..self.value.."\":"..table.concat(self.ints,",")..">"
end
function dfhack.matinfo:__tostring()
return "<material "..self.type..":"..self.index.." "..self:getToken()..">"
end
function dfhack.maps.getSize()
local map = df.global.world.map
return map.x_count_block, map.y_count_block, map.z_count_block
end
function dfhack.maps.getTileSize()
local map = df.global.world.map
return map.x_count, map.y_count, map.z_count
end
-- Interactive
local print_banner = true
function dfhack.interpreter(prompt,hfile,env)
if not dfhack.is_interactive() then
return nil, 'not interactive'
end
print("Type quit to exit interactive lua interpreter.")
if print_banner then
print("Shortcuts:\n"..
" '= foo' => '_1,_2,... = foo'\n"..
" '! foo' => 'print(foo)'\n"..
"Both save the first result as '_'.")
print_banner = false
end
local prompt_str = "["..(prompt or 'lua').."]# "
local prompt_env = {}
local t_prompt
local vcnt = 1
setmetatable(prompt_env, { __index = env or _G })
local cmdlinelist={}
while true do
local cmdline = dfhack.lineedit(t_prompt or prompt_str, hfile)
if cmdline == nil or cmdline == 'quit' then
break
elseif cmdline ~= '' then
local pfix = string.sub(cmdline,1,1)
if pfix == '!' or pfix == '=' then
cmdline = 'return '..string.sub(cmdline,2)
end
table.insert(cmdlinelist,cmdline)
local code,err = load(table.concat(cmdlinelist,'\n'), '=(interactive)', 't', prompt_env)
if code == nil then
if err:sub(-5)=="<eof>" then
t_prompt="[cont]"
else
dfhack.printerr(err)
cmdlinelist={}
t_prompt=nil
end
else
cmdlinelist={}
t_prompt=nil
local data = table.pack(safecall(code))
if data[1] and data.n > 1 then
prompt_env._ = data[2]
if pfix == '!' then
safecall(print, table.unpack(data,2,data.n))
else
for i=2,data.n do
local varname = '_'..vcnt
prompt_env[varname] = data[i]
dfhack.print(varname..' = ')
safecall(print, data[i])
vcnt = vcnt + 1
end
end
end
end
end
end
return true
end
-- Feed the table back to the require() mechanism.
return dfhack

@ -46,6 +46,7 @@ using namespace DFHack;
#include "df/global_objects.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_dungeonmodest.h"
#include "df/viewscreen_dungeon_monsterstatusst.h"
#include "df/viewscreen_joblistst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_itemst.h"
@ -284,6 +285,9 @@ static df::unit *getAnyUnit(df::viewscreen *top)
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_unitlistst, top))
return vector_get(screen->units[screen->page], screen->cursor_pos[screen->page]);
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_dungeon_monsterstatusst, top))
return screen->unit;
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_itemst, top))
{
df::general_ref *ref = vector_get(screen->entry_ref, screen->cursor_pos);

@ -39,12 +39,15 @@ using namespace std;
#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/world.h"
#include "df/item.h"
#include "df/building.h"
#include "df/tool_uses.h"
#include "df/itemdef_weaponst.h"
#include "df/itemdef_trapcompst.h"
@ -63,6 +66,9 @@ using namespace std;
#include "df/trapcomp_flags.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"
using namespace DFHack;
using namespace df::enums;
@ -415,89 +421,244 @@ bool Items::copyItem(df::item * itembase, DFHack::dfh_item &item)
return true;
}
int32_t Items::getItemOwnerID(const df::item * item)
df::unit *Items::getOwner(df::item * item)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == general_ref_type::UNIT_ITEMOWNER)
return ref->getID();
}
return -1;
}
CHECK_NULL_POINTER(item);
df::unit *Items::getItemOwner(const df::item * item)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == general_ref_type::UNIT_ITEMOWNER)
if (strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
return ref->getUnit();
}
return NULL;
}
int32_t Items::getItemContainerID(const df::item * item)
bool Items::setOwner(df::item *item, df::unit *unit)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
CHECK_NULL_POINTER(item);
for (int i = item->itemrefs.size()-1; i >= 0; i--)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getID();
if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
continue;
if (auto cur = ref->getUnit())
{
if (cur == unit)
return true;
erase_from_vector(cur->owned_items, item->id);
}
delete ref;
vector_erase_at(item->itemrefs, i);
}
item->flags.bits.owned = false;
if (unit)
{
auto ref = df::allocate<df::general_ref_unit_itemownerst>();
if (!ref)
return false;
item->flags.bits.owned = true;
ref->unit_id = unit->id;
insert_into_vector(unit->owned_items, item->id);
item->itemrefs.push_back(ref);
}
return -1;
return true;
}
df::item *Items::getItemContainer(const df::item * item)
df::item *Items::getContainer(df::item * item)
{
CHECK_NULL_POINTER(item);
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getItem();
}
return NULL;
}
bool Items::getContainedItems(const df::item * item, std::vector<int32_t> &items)
void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
{
return readItemRefs(item, general_ref_type::CONTAINS_ITEM, items);
}
CHECK_NULL_POINTER(item);
bool Items::readItemRefs(const df::item * item, df::general_ref_type type, std::vector<int32_t> &values)
{
values.clear();
items->clear();
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == type)
values.push_back(ref->getID());
if (ref->getType() != general_ref_type::CONTAINS_ITEM)
continue;
auto child = ref->getItem();
if (child)
items->push_back(child);
}
}
df::coord Items::getPosition(df::item *item)
{
CHECK_NULL_POINTER(item);
if (item->flags.bits.in_inventory ||
item->flags.bits.in_chest ||
item->flags.bits.in_building)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
if (auto item2 = ref->getItem())
return getPosition(item2);
break;
case general_ref_type::UNIT_HOLDER:
if (auto unit = ref->getUnit())
return Units::getPosition(unit);
break;
case general_ref_type::BUILDING_HOLDER:
if (auto bld = ref->getBuilding())
return df::coord(bld->centerx, bld->centery, bld->z);
break;
}
}
}
return !values.empty();
return item->pos;
}
bool Items::removeItemOwner(df::item * item)
static void removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
for (int i = vec.size()-1; i >= 0; i--)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() != general_ref_type::UNIT_ITEMOWNER)
df::general_ref *ref = vec[i];
if (ref->getType() != type || ref->getID() != id)
continue;
df::unit *unit = ref->getUnit();
vector_erase_at(vec, i);
delete ref;
}
}
static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{
if (item->flags.bits.on_ground)
{
if (!mc.removeItemOnGround(item))
Core::printerr("Item was marked on_ground, but not in block: %d (%d,%d,%d)\n",
item->id, item->pos.x, item->pos.y, item->pos.z);
item->flags.bits.on_ground = false;
return true;
}
else if (item->flags.bits.in_inventory)
{
bool found = false;
if (unit == NULL || !Units::RemoveOwnedItemByPtr(unit, item->id))
for (int i = item->itemrefs.size()-1; i >= 0; i--)
{
cerr << "RemoveOwnedItemIdx: CREATURE " << ref->getID() << " ID " << item->id << " FAILED!" << endl;
return false;
df::general_ref *ref = item->itemrefs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
if (auto item2 = ref->getItem())
{
item2->flags.bits.weight_computed = false;
removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id);
}
break;
case general_ref_type::UNIT_HOLDER:
case general_ref_type::BUILDING_HOLDER:
return false;
default:
continue;
}
found = true;
vector_erase_at(item->itemrefs, i);
delete ref;
}
delete ref;
item->itemrefs.erase(item->itemrefs.begin() + i--);
if (!found)
return false;
item->flags.bits.in_inventory = false;
return true;
}
else
return false;
}
static void putOnGround(MapExtras::MapCache &mc, df::item *item, df::coord pos)
{
item->pos = pos;
item->flags.bits.on_ground = true;
if (!mc.addItemOnGround(item))
Core::printerr("Could not add item %d to ground at (%d,%d,%d)\n",
item->id, pos.x, pos.y, pos.z);
}
bool DFHack::Items::moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos)
{
CHECK_NULL_POINTER(item);
if (!detachItem(mc, item))
return false;
putOnGround(mc, item, pos);
return true;
}
bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container)
{
CHECK_NULL_POINTER(item);
CHECK_NULL_POINTER(container);
if (!detachItem(mc, item))
return false;
auto ref1 = df::allocate<df::general_ref_contains_itemst>();
auto ref2 = df::allocate<df::general_ref_contained_in_itemst>();
if (!ref1 || !ref2)
{
delete ref1; delete ref2;
Core::printerr("Could not allocate container refs.\n");
putOnGround(mc, item, getPosition(container));
return false;
}
item->flags.bits.owned = 0;
item->pos = container->pos;
item->flags.bits.in_inventory = true;
container->flags.bits.container = true;
container->flags.bits.weight_computed = false;
ref1->item_id = item->id;
container->itemrefs.push_back(ref1);
ref2->item_id = container->id;
item->itemrefs.push_back(ref2);
return true;
}

@ -32,6 +32,7 @@ distribution.
using namespace std;
#include "Core.h"
#include "Error.h"
#include "PluginManager.h"
#include "MiscUtils.h"
@ -52,8 +53,10 @@ using namespace std;
using namespace DFHack;
using namespace df::enums;
df::job *DFHack::cloneJobStruct(df::job *job)
df::job *DFHack::Job::cloneJobStruct(df::job *job)
{
CHECK_NULL_POINTER(job);
df::job *pnew = new df::job(*job);
// Clean out transient fields
@ -84,7 +87,7 @@ df::job *DFHack::cloneJobStruct(df::job *job)
return pnew;
}
void DFHack::deleteJobStruct(df::job *job)
void DFHack::Job::deleteJobStruct(df::job *job)
{
if (!job)
return;
@ -105,6 +108,9 @@ void DFHack::deleteJobStruct(df::job *job)
bool DFHack::operator== (const df::job_item &a, const df::job_item &b)
{
CHECK_NULL_POINTER(&a);
CHECK_NULL_POINTER(&b);
if (!(CMP(item_type) && CMP(item_subtype) &&
CMP(mat_type) && CMP(mat_index) &&
CMP(flags1.whole) && CMP(quantity) && CMP(vector_id) &&
@ -125,6 +131,9 @@ bool DFHack::operator== (const df::job_item &a, const df::job_item &b)
bool DFHack::operator== (const df::job &a, const df::job &b)
{
CHECK_NULL_POINTER(&a);
CHECK_NULL_POINTER(&b);
if (!(CMP(job_type) && CMP(unk2) &&
CMP(mat_type) && CMP(mat_index) &&
CMP(item_subtype) && CMP(item_category.whole) &&
@ -139,8 +148,10 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
return true;
}
static void print_job_item_details(color_ostream &out, df::job *job, unsigned idx, df::job_item *item)
void DFHack::Job::printItemDetails(color_ostream &out, df::job_item *item, int idx)
{
CHECK_NULL_POINTER(item);
ItemTypeInfo info(item);
out << " Input Item " << (idx+1) << ": " << info.toString();
@ -173,8 +184,10 @@ static void print_job_item_details(color_ostream &out, df::job *job, unsigned id
out << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl;
}
void DFHack::printJobDetails(color_ostream &out, df::job *job)
void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
{
CHECK_NULL_POINTER(job);
out.color(job->flags.bits.suspend ? Console::COLOR_DARKGREY : Console::COLOR_GREY);
out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type);
if (job->flags.whole)
@ -211,11 +224,13 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
out << " reaction: " << job->reaction_name << endl;
for (size_t i = 0; i < job->job_items.size(); i++)
print_job_item_details(out, job, i, job->job_items[i]);
printItemDetails(out, job->job_items[i], i);
}
df::building *DFHack::getJobHolder(df::job *job)
df::building *DFHack::Job::getHolder(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]);
@ -226,7 +241,21 @@ df::building *DFHack::getJobHolder(df::job *job)
return NULL;
}
bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
df::unit *DFHack::Job::getWorker(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]);
if (ref)
return ref->getUnit();
}
return NULL;
}
bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
{
using df::global::world;
using df::global::job_next_id;
@ -254,3 +283,31 @@ bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
return true;
}
}
bool DFHack::Job::listNewlyCreated(std::vector<df::job*> *pvec, int *id_var)
{
using df::global::world;
using df::global::job_next_id;
pvec->clear();
if (!job_next_id || *job_next_id <= *id_var)
return false;
int old_id = *id_var;
int cur_id = *job_next_id;
*id_var = cur_id;
pvec->reserve(std::min(20,cur_id - old_id));
df::job_list_link *link = world->job_list.next;
for (; link; link = link->next)
{
int id = link->item->id;
if (id >= old_id)
pvec->push_back(link->item);
}
return true;
}

@ -33,11 +33,13 @@ distribution.
using namespace std;
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "Error.h"
#include "VersionInfo.h"
#include "MemAccess.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/world_data.h"
@ -45,6 +47,10 @@ using namespace std;
#include "df/world_geo_biome.h"
#include "df/world_geo_layer.h"
#include "df/feature_init.h"
#include "df/world_data.h"
#include "df/burrow.h"
#include "df/block_burrow.h"
#include "df/block_burrow_link.h"
using namespace DFHack;
using namespace df::enums;
@ -125,7 +131,7 @@ 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 *Maps::getBlockAbs (int32_t x, int32_t y, int32_t z)
df::map_block *Maps::getTileBlock (int32_t x, int32_t y, int32_t z)
{
if (!IsValid())
return NULL;
@ -136,207 +142,40 @@ df::map_block *Maps::getBlockAbs (int32_t x, int32_t y, int32_t z)
return world->map.block_index[x >> 4][y >> 4][z];
}
bool Maps::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer)
{
df::map_block * block = getBlock(x,y,z);
if (block)
{
buffer->position = DFCoord(x,y,z);
memcpy(buffer->tiletypes,block->tiletype, sizeof(tiletypes40d));
memcpy(buffer->designation,block->designation, sizeof(designations40d));
memcpy(buffer->occupancy,block->occupancy, sizeof(occupancies40d));
memcpy(buffer->biome_indices,block->region_offset, sizeof(block->region_offset));
buffer->global_feature = block->global_feature;
buffer->local_feature = block->local_feature;
buffer->mystery = block->unk2;
buffer->origin = block;
buffer->blockflags.whole = block->flags.whole;
return true;
}
return false;
}
/*
* Tiletypes
*/
bool Maps::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(buffer, block->tiletype, sizeof(tiletypes40d));
return true;
}
return false;
}
bool Maps::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(block->tiletype, buffer, sizeof(tiletypes40d));
return true;
}
return false;
}
/*
* Dirty bit
*/
bool Maps::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
dirtybit = block->flags.bits.designated;
return true;
}
return false;
}
bool Maps::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
block->flags.bits.designated = true;
return true;
}
return false;
}
/*
* Block flags
*/
// FIXME: maybe truncates, still bullshit
bool Maps::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
blockflags.whole = block->flags.whole;
return true;
}
return false;
}
//FIXME: maybe truncated, still bullshit
bool Maps::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags)
df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
block->flags.whole = blockflags.whole;
return true;
}
return false;
}
/*
* Designations
*/
bool Maps::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(buffer, block->designation, sizeof(designations40d));
return true;
}
return false;
}
auto data = world->world_data;
if (!data)
return NULL;
bool Maps::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(block->designation, buffer, sizeof(designations40d));
return true;
}
return false;
}
if (rgn_pos.x < 0 || rgn_pos.x >= data->world_width ||
rgn_pos.y < 0 || rgn_pos.y >= data->world_height)
return NULL;
/*
* Occupancies
*/
bool Maps::ReadOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(buffer, block->occupancy, sizeof(occupancies40d));
return true;
}
return false;
return &data->region_map[rgn_pos.x][rgn_pos.y];
}
bool Maps::WriteOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer)
df::feature_init *Maps::getGlobalInitFeature(int32_t index)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(block->occupancy, buffer, sizeof(occupancies40d));
return true;
}
return false;
}
auto data = world->world_data;
if (!data)
return NULL;
/*
* Temperatures
*/
bool Maps::ReadTemperatures(uint32_t x, uint32_t y, uint32_t z, t_temperatures *temp1, t_temperatures *temp2)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
if(temp1)
memcpy(temp1, block->temperature_1, sizeof(t_temperatures));
if(temp2)
memcpy(temp2, block->temperature_2, sizeof(t_temperatures));
return true;
}
return false;
}
bool Maps::WriteTemperatures (uint32_t x, uint32_t y, uint32_t z, t_temperatures *temp1, t_temperatures *temp2)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
if(temp1)
memcpy(block->temperature_1, temp1, sizeof(t_temperatures));
if(temp2)
memcpy(block->temperature_2, temp2, sizeof(t_temperatures));
return true;
}
return false;
}
auto rgn = vector_get(data->underground_regions, index);
if (!rgn)
return NULL;
/*
* Region Offsets - used for layer geology
*/
bool Maps::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
memcpy(buffer, block->region_offset,sizeof(biome_indices40d));
return true;
}
return false;
return rgn->feature_init;
}
bool Maps::GetGlobalFeature(t_feature &feature, int32_t index)
{
feature.type = (df::feature_type)-1;
if (!world->world_data)
return false;
if ((index < 0) || (index >= world->world_data->underground_regions.size()))
auto f = Maps::getGlobalInitFeature(index);
if (!f)
return false;
df::feature_init *f = world->world_data->underground_regions[index]->feature_init;
feature.discovered = false;
feature.origin = f;
feature.type = f->getType();
@ -344,35 +183,38 @@ bool Maps::GetGlobalFeature(t_feature &feature, int32_t index)
return true;
}
bool Maps::GetLocalFeature(t_feature &feature, df::coord2d coord, int32_t index)
df::feature_init *Maps::getLocalInitFeature(df::coord2d rgn_pos, int32_t index)
{
feature.type = (df::feature_type)-1;
if (!world->world_data)
return false;
// regionX and regionY are in embark squares!
// we convert to full region tiles
// this also works in adventure mode
// region X coord - whole regions
uint32_t region_x = ( (coord.x / 3) + world->map.region_x ) / 16;
// region Y coord - whole regions
uint32_t region_y = ( (coord.y / 3) + world->map.region_y ) / 16;
auto data = world->world_data;
if (!data)
return NULL;
uint32_t bigregion_x = region_x / 16;
uint32_t bigregion_y = region_y / 16;
if (rgn_pos.x < 0 || rgn_pos.x >= data->world_width ||
rgn_pos.y < 0 || rgn_pos.y >= data->world_height)
return NULL;
uint32_t sub_x = region_x % 16;
uint32_t sub_y = region_y % 16;
// megaregions = 16x16 squares of regions = 256x256 squares of embark squares
df::coord2d bigregion = rgn_pos / 16;
// bigregion is 16x16 regions. for each bigregion in X dimension:
if (!world->world_data->unk_204[bigregion_x][bigregion_y].features)
return false;
auto fptr = data->unk_204[bigregion.x][bigregion.y].features;
if (!fptr)
return NULL;
df::coord2d sub = rgn_pos & 15;
vector <df::feature_init *> &features = fptr->feature_init[sub.x][sub.y];
return vector_get(features, index);
}
vector <df::feature_init *> &features = world->world_data->unk_204[bigregion_x][bigregion_y].features->feature_init[sub_x][sub_y];
if ((index < 0) || (index >= features.size()))
static bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index)
{
feature.type = (df::feature_type)-1;
auto f = Maps::getLocalInitFeature(rgn_pos, index);
if (!f)
return false;
df::feature_init *f = features[index];
feature.discovered = false;
feature.origin = f;
@ -381,58 +223,15 @@ bool Maps::GetLocalFeature(t_feature &feature, df::coord2d coord, int32_t index)
return true;
}
bool Maps::ReadFeatures(uint32_t x, uint32_t y, uint32_t z, int32_t & local, int32_t & global)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
local = block->local_feature;
global = block->global_feature;
return true;
}
return false;
}
bool Maps::WriteFeatures(uint32_t x, uint32_t y, uint32_t z, const int32_t & local, const int32_t & global)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
block->local_feature = local;
block->global_feature = global;
return true;
}
return false;
}
bool Maps::ReadFeatures(uint32_t x, uint32_t y, uint32_t z, t_feature *local, t_feature *global)
{
int32_t loc, glob;
if (!ReadFeatures(x, y, z, loc, glob))
df::map_block *block = getBlock(x,y,z);
if (!block)
return false;
bool result = true;
if (global)
{
if (glob != -1)
result &= GetGlobalFeature(*global, glob);
else
global->type = (df::feature_type)-1;
}
if (local)
{
if (loc != -1)
{
df::coord2d coord(x,y);
result &= GetLocalFeature(*local, coord, loc);
}
else
local->type = (df::feature_type)-1;
}
return result;
return ReadFeatures(block, local, global);
}
bool Maps::ReadFeatures(mapblock40d * block, t_feature * local, t_feature * global)
bool Maps::ReadFeatures(df::map_block * block, t_feature * local, t_feature * global)
{
bool result = true;
if (global)
@ -445,39 +244,17 @@ bool Maps::ReadFeatures(mapblock40d * block, t_feature * local, t_feature * glob
if (local)
{
if (block->local_feature != -1)
result &= GetLocalFeature(*local, block->position, block->local_feature);
result &= GetLocalFeature(*local, block->region_pos, block->local_feature);
else
local->type = (df::feature_type)-1;
}
return result;
}
bool Maps::SetBlockLocalFeature(uint32_t x, uint32_t y, uint32_t z, int32_t local)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
block->local_feature = local;
return true;
}
return false;
}
bool Maps::SetBlockGlobalFeature(uint32_t x, uint32_t y, uint32_t z, int32_t global)
{
df::map_block *block = getBlock(x,y,z);
if (block)
{
block->global_feature = global;
return true;
}
return false;
}
/*
* Block events
*/
bool Maps::SortBlockEvents(uint32_t x, uint32_t y, uint32_t z,
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,
@ -495,7 +272,6 @@ bool Maps::SortBlockEvents(uint32_t x, uint32_t y, uint32_t z,
if (constructions)
constructions->clear();
df::map_block * block = getBlock(x,y,z);
if (!block)
return false;
@ -535,27 +311,38 @@ bool Maps::RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square
df::map_block * block = getBlock(x,y,z);
if (!block)
return false;
for (size_t i = 0; i < block->block_events.size(); i++)
int idx = linear_index(block->block_events, which);
if (idx >= 0)
{
if (block->block_events[i] == which)
{
delete which;
block->block_events.erase(block->block_events.begin() + i);
return true;
}
delete which;
vector_erase_at(block->block_events, idx);
return true;
}
return false;
else
return false;
}
/*
* Layer geology
*/
bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
bool Maps::ReadGeology(vector<vector<int16_t> > *layer_mats, vector<df::coord2d> *geoidx)
{
if (!world->world_data)
return false;
vector<uint16_t> v_geology[eBiomeCount];
layer_mats->resize(eBiomeCount);
geoidx->resize(eBiomeCount);
for (int i = 0; i < eBiomeCount; i++)
{
(*layer_mats)[i].clear();
(*geoidx)[i] = df::coord2d(-30000,-30000);
}
int world_width = world->world_data->world_width;
int world_height = world->world_data->world_height;
// iterate over 8 surrounding regions + local region
for (int i = eNorthWest; i < eBiomeCount; i++)
{
@ -564,14 +351,18 @@ bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
// regionX/16 is in 16x16 embark square regions
// i provides -1 .. +1 offset from the current region
int bioRX = world->map.region_x / 16 + ((i % 3) - 1);
if (bioRX < 0) bioRX = 0;
if (bioRX >= world->world_data->world_width) bioRX = world->world_data->world_width - 1;
int bioRY = world->map.region_y / 16 + ((i / 3) - 1);
if (bioRY < 0) bioRY = 0;
if (bioRY >= world->world_data->world_height) bioRY = world->world_data->world_height - 1;
df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRX,0,world_height-1));
(*geoidx)[i] = rgn_pos;
auto biome = getRegionBiome(rgn_pos);
if (!biome)
continue;
// get index into geoblock vector
uint16_t geoindex = world->world_data->region_map[bioRX][bioRY].geo_index;
int16_t geoindex = biome->geo_index;
/// geology blocks have a vector of layer descriptors
// get the vector with pointer to layers
@ -579,29 +370,483 @@ bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
if (!geo_biome)
continue;
vector <df::world_geo_layer*> &geolayers = geo_biome->layers;
auto &geolayers = geo_biome->layers;
auto &matvec = (*layer_mats)[i];
/// layer descriptor has a field that determines the type of stone/soil
v_geology[i].reserve(geolayers.size());
matvec.resize(geolayers.size());
// finally, read the layer matgloss
for (size_t j = 0; j < geolayers.size(); j++)
v_geology[i].push_back(geolayers[j]->mat_index);
matvec[j] = geolayers[j]->mat_index;
}
assign.clear();
assign.reserve(eBiomeCount);
for (int i = 0; i < eBiomeCount; i++)
assign.push_back(v_geology[i]);
return true;
}
bool Maps::ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector<df::plant *>*& plants)
#define COPY(a,b) memcpy(&a,&b,sizeof(a))
MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
{
dirty_designations = false;
dirty_tiletypes = false;
dirty_temperatures = false;
dirty_blockflags = false;
dirty_occupancies = false;
valid = false;
bcoord = _bcoord;
block = Maps::getBlock(bcoord);
item_counts = NULL;
memset(tags,0,sizeof(tags));
if(block)
{
COPY(rawtiles, block->tiletype);
COPY(designation, block->designation);
COPY(occupancy, block->occupancy);
blockflags = block->flags;
COPY(temp1, block->temperature_1);
COPY(temp2, block->temperature_2);
SquashVeins(block,veinmats);
SquashConstructions(block, contiles);
SquashFrozenLiquids(block, icetiles);
if(parent->validgeo)
SquashRocks(block,basemats,&parent->layer_mats);
else
memset(basemats,-1,sizeof(basemats));
valid = true;
}
else
{
blockflags.whole = 0;
memset(rawtiles,0,sizeof(rawtiles));
memset(designation,0,sizeof(designation));
memset(occupancy,0,sizeof(occupancy));
memset(temp1,0,sizeof(temp1));
memset(temp2,0,sizeof(temp2));
memset(veinmats,-1,sizeof(veinmats));
memset(contiles,0,sizeof(contiles));
memset(icetiles,0,sizeof(icetiles));
memset(basemats,-1,sizeof(basemats));
}
}
MapExtras::Block::~Block()
{
delete[] item_counts;
}
bool MapExtras::Block::Write ()
{
if(!valid) return false;
if(dirty_blockflags)
{
block->flags = blockflags;
dirty_blockflags = false;
}
if(dirty_designations)
{
COPY(block->designation, designation);
block->flags.bits.designated = true;
dirty_designations = false;
}
if(dirty_tiletypes)
{
COPY(block->tiletype, rawtiles);
dirty_tiletypes = false;
}
if(dirty_temperatures)
{
COPY(block->temperature_1, temp1);
COPY(block->temperature_2, temp2);
dirty_temperatures = false;
}
if(dirty_occupancies)
{
COPY(block->occupancy, occupancy);
dirty_occupancies = false;
}
return true;
}
void MapExtras::Block::SquashVeins(df::map_block *mb, t_blockmaterials & materials)
{
memset(materials,-1,sizeof(materials));
std::vector <df::block_square_event_mineralst *> veins;
Maps::SortBlockEvents(mb,&veins);
for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++)
{
df::tiletype tt = mb->tiletype[x][y];
if (tileMaterial(tt) == tiletype_material::MINERAL)
{
for (size_t i = 0; i < veins.size(); i++)
{
if (veins[i]->getassignment(x,y))
materials[x][y] = veins[i]->inorganic_mat;
}
}
}
}
void MapExtras::Block::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen)
{
std::vector <df::block_square_event_frozen_liquidst *> ices;
Maps::SortBlockEvents(mb,NULL,&ices);
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
df::tiletype tt = mb->tiletype[x][y];
frozen[x][y] = tiletype::Void;
if (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID)
{
for (size_t i = 0; i < ices.size(); i++)
{
df::tiletype tt2 = ices[i]->tiles[x][y];
if (tt2 != tiletype::Void)
{
frozen[x][y] = tt2;
break;
}
}
}
}
}
void MapExtras::Block::SquashConstructions (df::map_block *mb, tiletypes40d & constructions)
{
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
df::tiletype tt = mb->tiletype[x][y];
constructions[x][y] = tiletype::Void;
if (tileMaterial(tt) == tiletype_material::CONSTRUCTION)
{
DFCoord coord = mb->map_pos + df::coord(x,y,0);
df::construction *con = df::construction::find(coord);
if (con)
constructions[x][y] = con->original_tile;
}
}
}
void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materials,
std::vector< std::vector <int16_t> > * layerassign)
{
// get the layer materials
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
materials[x][y] = -1;
uint8_t test = mb->designation[x][y].bits.biome;
if (test >= 9)
continue;
uint8_t idx = mb->region_offset[test];
if (idx < layerassign->size())
materials[x][y] = layerassign->at(idx)[mb->designation[x][y].bits.geolayer_index];
}
}
df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p)
{
if (!block)
return df::coord2d(-30000,-30000);
auto des = index_tile<df::tile_designation>(designation,p);
uint8_t idx = des.bits.biome;
if (idx >= 9)
return block->region_pos;
idx = block->region_offset[idx];
if (idx >= parent->geoidx.size())
return block->region_pos;
return parent->geoidx[idx];
}
int16_t MapExtras::Block::GeoIndexAt(df::coord2d p)
{
df::coord2d biome = biomeRegionAt(p);
if (!biome.isValid())
return -1;
auto pinfo = Maps::getRegionBiome(biome);
if (!pinfo)
return -1;
return pinfo->geo_index;
}
bool MapExtras::Block::GetGlobalFeature(t_feature *out)
{
out->type = (df::feature_type)-1;
if (!valid || block->global_feature < 0)
return false;
return Maps::GetGlobalFeature(*out, block->global_feature);
}
bool MapExtras::Block::GetLocalFeature(t_feature *out)
{
out->type = (df::feature_type)-1;
if (!valid || block->local_feature < 0)
return false;
return ::GetLocalFeature(*out, block->region_pos, block->local_feature);
}
void MapExtras::Block::init_item_counts()
{
if (item_counts) return;
item_counts = new T_item_counts[16];
memset(item_counts, 0, sizeof(T_item_counts)*16);
if (!block) return;
for (size_t i = 0; i < block->items.size(); i++)
{
auto it = df::item::find(block->items[i]);
if (!it || !it->flags.bits.on_ground)
continue;
df::coord tidx = it->pos - block->map_pos;
if (!is_valid_tile_coord(tidx) || tidx.z != 0)
continue;
item_counts[tidx.x][tidx.y]++;
}
}
bool MapExtras::Block::addItemOnGround(df::item *item)
{
if (!block)
return false;
init_item_counts();
bool inserted;
insert_into_vector(block->items, item->id, &inserted);
if (inserted)
{
int &count = index_tile<int&>(item_counts,item->pos);
if (count++ == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = true;
index_tile<df::tile_occupancy&>(block->occupancy,item->pos).bits.item = true;
}
}
return inserted;
}
bool MapExtras::Block::removeItemOnGround(df::item *item)
{
df::map_block *block = getBlock(x,y,z);
if (!block)
return false;
plants = &block->plants;
init_item_counts();
int idx = binsearch_index(block->items, item->id);
if (idx < 0)
return false;
vector_erase_at(block->items, idx);
int &count = index_tile<int&>(item_counts,item->pos);
if (--count == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = false;
index_tile<df::tile_occupancy&>(block->occupancy,item->pos).bits.item = false;
}
return true;
}
MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
{
if(!valid)
return 0;
std::map <DFCoord, Block*>::iterator iter = blocks.find(blockcoord);
if(iter != blocks.end())
{
return (*iter).second;
}
else
{
if(blockcoord.x >= 0 && blockcoord.x < x_bmax &&
blockcoord.y >= 0 && blockcoord.y < y_bmax &&
blockcoord.z >= 0 && blockcoord.z < z_max)
{
Block * nblo = new Block(this, blockcoord);
blocks[blockcoord] = nblo;
return nblo;
}
return 0;
}
}
df::burrow *Maps::findBurrowByName(std::string name)
{
auto &vec = df::burrow::get_vector();
for (size_t i = 0; i < vec.size(); i++)
if (vec[i]->name == name)
return vec[i];
return NULL;
}
void Maps::listBurrowBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow)
{
CHECK_NULL_POINTER(burrow);
pvec->clear();
pvec->reserve(burrow->block_x.size());
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
for (size_t i = 0; i < burrow->block_x.size(); i++)
{
df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
auto block = getBlock(pos - base);
if (block)
pvec->push_back(block);
}
}
static void destroyBurrowMask(df::block_burrow *mask)
{
if (!mask) return;
auto link = mask->link;
link->prev->next = link->next;
if (link->next)
link->next->prev = link->prev;
delete link;
delete mask;
}
void Maps::clearBurrowTiles(df::burrow *burrow)
{
CHECK_NULL_POINTER(burrow);
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
for (size_t i = 0; i < burrow->block_x.size(); i++)
{
df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
auto block = getBlock(pos - base);
if (!block)
continue;
destroyBurrowMask(getBlockBurrowMask(burrow, block));
}
burrow->block_x.clear();
burrow->block_y.clear();
burrow->block_z.clear();
}
df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create)
{
CHECK_NULL_POINTER(burrow);
CHECK_NULL_POINTER(block);
int32_t id = burrow->id;
df::block_burrow_link *prev = &block->block_burrows;
df::block_burrow_link *link = prev->next;
for (; link; prev = link, link = link->next)
if (link->item->id == id)
return link->item;
if (create)
{
link = new df::block_burrow_link;
link->item = new df::block_burrow;
link->item->id = burrow->id;
memset(link->item->tile_bitmask,0,sizeof(link->item->tile_bitmask));
link->item->link = link;
link->next = NULL;
link->prev = prev;
prev->next = link;
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
df::coord pos = base + block->map_pos/16;
burrow->block_x.push_back(pos.x);
burrow->block_y.push_back(pos.y);
burrow->block_z.push_back(pos.z);
return link->item;
}
return NULL;
}
bool Maps::deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask)
{
CHECK_NULL_POINTER(burrow);
CHECK_NULL_POINTER(block);
if (!mask)
return false;
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
df::coord pos = base + block->map_pos/16;
destroyBurrowMask(mask);
for (size_t i = 0; i < burrow->block_x.size(); i++)
{
df::coord cur(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
if (cur == pos)
{
vector_erase_at(burrow->block_x, i);
vector_erase_at(burrow->block_y, i);
vector_erase_at(burrow->block_z, i);
break;
}
}
return true;
}
bool Maps::isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile)
{
CHECK_NULL_POINTER(burrow);
if (!block) return false;
auto mask = getBlockBurrowMask(burrow, block);
return mask ? mask->getassignment(tile & 15) : false;
}
bool Maps::setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable)
{
CHECK_NULL_POINTER(burrow);
if (!block) return false;
auto mask = getBlockBurrowMask(burrow, block, enable);
if (mask)
{
mask->setassignment(tile & 15, enable);
if (!enable && !mask->has_assignments())
deleteBlockBurrowMask(burrow, block, mask);
}
return true;
}

@ -164,6 +164,13 @@ bool MaterialInfo::find(const std::string &token)
{
std::vector<std::string> items;
split_string(&items, token, ":");
return find(items);
}
bool MaterialInfo::find(const std::vector<std::string> &items)
{
if (items.empty())
return false;
if (items[0] == "INORGANIC" && items.size() > 1)
return findInorganic(vector_get(items,1));
@ -204,8 +211,11 @@ bool MaterialInfo::findBuiltin(const std::string &token)
df::world_raws &raws = world->raws;
for (int i = 1; i < NUM_BUILTIN; i++)
if (raws.mat_table.builtin[i]->id == token)
{
auto obj = raws.mat_table.builtin[i];
if (obj && obj->id == token)
return decode(i, -1);
}
return decode(-1);
}

@ -35,6 +35,7 @@ using namespace std;
#include "Types.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "Error.h"
using namespace DFHack;
using namespace df::enums;
@ -91,8 +92,25 @@ void addNameWord (string &out, const string &word)
out.append(upper);
}
void Translation::setNickname(df::language_name *name, std::string nick)
{
CHECK_NULL_POINTER(name);
if (!name->has_name)
{
*name = df::language_name();
name->language = 0;
name->has_name = true;
}
name->nickname = nick;
}
string Translation::TranslateName(const df::language_name * name, bool inEnglish, bool onlyLastPart)
{
CHECK_NULL_POINTER(name);
string out;
string word;

@ -40,18 +40,23 @@ using namespace std;
// we connect to those
#include "modules/Units.h"
#include "modules/Items.h"
#include "modules/Materials.h"
#include "modules/Translation.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "MiscUtils.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/unit_inventory_item.h"
#include "df/unit_soul.h"
#include "df/nemesis_record.h"
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
#include "df/burrow.h"
using namespace DFHack;
using namespace df::enums;
@ -491,44 +496,84 @@ bool Units::ReadInventoryByPtr(const df::unit * unit, std::vector<df::item *> &
return true;
}
bool Units::ReadOwnedItemsByIdx(const uint32_t index, std::vector<int32_t> & item)
void Units::CopyNameTo(df::unit * creature, df::language_name * target)
{
if(index >= world->units.all.size()) return false;
df::unit * temp = world->units.all[index];
return ReadOwnedItemsByPtr(temp, item);
Translation::copyName(&creature->name, target);
}
bool Units::ReadOwnedItemsByPtr(const df::unit * unit, std::vector<int32_t> & items)
df::coord Units::getPosition(df::unit *unit)
{
if(!isValid()) return false;
if(!unit) return false;
items = unit->owned_items;
return true;
}
CHECK_NULL_POINTER(unit);
bool Units::RemoveOwnedItemByIdx(const uint32_t index, int32_t id)
{
if(index >= world->units.all.size()) return false;
df::unit * temp = world->units.all[index];
return RemoveOwnedItemByPtr(temp, id);
if (unit->flags1.bits.caged)
{
auto cage = getContainer(unit);
if (cage)
return Items::getPosition(cage);
}
return unit->pos;
}
bool Units::RemoveOwnedItemByPtr(df::unit * unit, int32_t id)
df::item *Units::getContainer(df::unit *unit)
{
if(!isValid()) return false;
if(!unit) return false;
vector <int32_t> & vec = unit->owned_items;
vec.erase(std::remove(vec.begin(), vec.end(), id), vec.end());
return true;
CHECK_NULL_POINTER(unit);
for (size_t i = 0; i < unit->refs.size(); i++)
{
df::general_ref *ref = unit->refs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getItem();
}
return NULL;
}
void Units::CopyNameTo(df::unit * creature, df::language_name * target)
void Units::setNickname(df::unit *unit, std::string nick)
{
Translation::copyName(&creature->name, target);
CHECK_NULL_POINTER(unit);
// There are >=3 copies of the name, and the one
// in the unit is not the authoritative one.
// This is the reason why military units often
// lose nicknames set from Dwarf Therapist.
Translation::setNickname(&unit->name, nick);
if (unit->status.current_soul)
Translation::setNickname(&unit->status.current_soul->name, nick);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
Translation::setNickname(&figure->name, nick);
// v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation)
{
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity);
if (identity)
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
if (id_hfig)
{
// Even DF doesn't do this bit, because it's apparently
// only used for demons masquerading as gods, so you
// can't ever change their nickname in-game.
Translation::setNickname(&id_hfig->name, nick);
}
else
Translation::setNickname(&identity->name, nick);
}
}
}
}
df::language_name *Units::GetVisibleName(df::unit *unit)
df::language_name *Units::getVisibleName(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
@ -553,13 +598,33 @@ df::language_name *Units::GetVisibleName(df::unit *unit)
return &unit->name;
}
df::nemesis_record *Units::getNemesis(df::unit *unit)
{
if (!unit)
return NULL;
for (unsigned i = 0; i < unit->refs.size(); i++)
{
df::nemesis_record *rv = unit->refs[i]->getNemesis();
if (rv && rv->unit == unit)
return rv;
}
return NULL;
}
bool DFHack::Units::isDead(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return unit->flags1.bits.dead;
}
bool DFHack::Units::isAlive(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return !unit->flags1.bits.dead &&
!unit->flags3.bits.ghostly &&
!unit->curse.add_tags1.bits.NOT_LIVING;
@ -567,24 +632,105 @@ bool DFHack::Units::isAlive(df::unit *unit)
bool DFHack::Units::isSane(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly ||
unit->curse.add_tags1.bits.OPPOSED_TO_LIFE ||
unit->curse.add_tags1.bits.CRAZED)
return false;
if (unit->flags1.bits.has_mood)
switch (unit->mood)
{
switch (unit->mood)
{
case mood_type::Melancholy:
case mood_type::Raving:
case mood_type::Berserk:
return false;
default:
break;
}
case mood_type::Melancholy:
case mood_type::Raving:
case mood_type::Berserk:
return false;
default:
break;
}
return true;
}
bool DFHack::Units::isCitizen(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return unit->civ_id == ui->civ_id &&
!unit->flags1.bits.merchant &&
!unit->flags1.bits.diplomat &&
!unit->flags2.bits.resident &&
!unit->flags1.bits.dead &&
!unit->flags3.bits.ghostly;
}
bool DFHack::Units::isDwarf(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return unit->race == ui->race_id;
}
void DFHack::Units::clearBurrowMembers(df::burrow *burrow)
{
CHECK_NULL_POINTER(burrow);
for (size_t i = 0; i < burrow->units.size(); i++)
{
auto unit = df::unit::find(burrow->units[i]);
if (unit)
erase_from_vector(unit->burrows, burrow->id);
}
burrow->units.clear();
// Sync ui if active
if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
{
auto &sel = ui->burrows.sel_units;
for (size_t i = 0; i < sel.size(); i++)
sel[i] = false;
}
}
bool DFHack::Units::isInBurrow(df::unit *unit, df::burrow *burrow)
{
CHECK_NULL_POINTER(unit);
CHECK_NULL_POINTER(burrow);
return binsearch_index(unit->burrows, burrow->id) >= 0;
}
void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable)
{
using df::global::ui;
CHECK_NULL_POINTER(unit);
CHECK_NULL_POINTER(burrow);
if (enable)
{
insert_into_vector(unit->burrows, burrow->id);
insert_into_vector(burrow->units, unit->id);
}
else
{
erase_from_vector(unit->burrows, burrow->id);
erase_from_vector(burrow->units, unit->id);
}
// Sync ui if active
if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
{
int idx = linear_index(ui->burrows.list_units, unit);
if (idx >= 0)
ui->burrows.sel_units[idx] = enable;
}
}

@ -254,6 +254,8 @@ bool World::BuildPersistentCache()
d->persistent_index.insert(T_persistent_item(hfvec[i]->name.first_name, -hfvec[i]->id));
}
return true;
}
PersistentDataItem World::AddPersistentData(const std::string &key)
@ -300,6 +302,21 @@ PersistentDataItem World::GetPersistentData(int entry_id)
return PersistentDataItem();
}
PersistentDataItem World::GetPersistentData(const std::string &key, bool *added)
{
*added = false;
PersistentDataItem rv = GetPersistentData(key);
if (!rv.isValid())
{
*added = true;
rv = AddPersistentData(key);
}
return rv;
}
void World::GetPersistentData(std::vector<PersistentDataItem> *vec, const std::string &key, bool prefix)
{
if (!BuildPersistentCache())

@ -130,6 +130,11 @@ message SkillInfo {
required int32 experience = 3;
};
message UnitMiscTrait {
required int32 id = 1;
required int32 value = 2;
};
message BasicUnitInfo {
required int32 unit_id = 1;
@ -166,6 +171,9 @@ message BasicUnitInfo {
// IF mask.skills:
repeated SkillInfo skills = 12;
// IF mask.misc_traits:
repeated UnitMiscTrait misc_traits = 24;
optional UnitCurseInfo curse = 16;
repeated int32 burrows = 21;
@ -175,6 +183,7 @@ message BasicUnitInfoMask {
optional bool labors = 1 [default = false];
optional bool skills = 2 [default = false];
optional bool profession = 3 [default = false];
optional bool misc_traits = 4 [default = false];
};
message BasicSquadInfo {
@ -188,3 +197,9 @@ message BasicSquadInfo {
// Member histfig ids:
repeated sint32 members = 4;
};
message UnitLaborState {
required int32 unit_id = 1;
required int32 labor = 2;
required bool value = 3;
};

@ -99,4 +99,9 @@ message ListUnitsOut {
message ListSquadsIn {}
message ListSquadsOut {
repeated BasicSquadInfo value = 1;
}
};
// RPC SetUnitLabors : SetUnitLaborsIn -> EmptyMessage
message SetUnitLaborsIn {
repeated UnitLaborState change = 1;
};

@ -1 +1 @@
Subproject commit b630809b25a3e3b541e40f0cb145b6f5bd3b7316
Subproject commit a545167050fee9eedd5e319b518d961f933154a3

@ -6,6 +6,9 @@ class Brush
public:
virtual ~Brush(){};
virtual coord_vec points(MapExtras::MapCache & mc,DFHack::DFCoord start) = 0;
virtual std::string str() const {
return "unknown";
}
};
/**
* generic 3D rectangle brush. you can specify the dimensions of
@ -56,6 +59,13 @@ public:
return v;
};
~RectangleBrush(){};
std::string str() const {
if (x_ == 1 && y_ == 1 && z_ == 1) {
return "point";
} else {
return "rectangle";
}
}
private:
int x_, y_, z_;
int cx_, cy_, cz_;
@ -89,6 +99,9 @@ public:
}
return v;
};
std::string str() const {
return "block";
}
};
/**
@ -117,6 +130,9 @@ public:
}
return v;
};
std::string str() const {
return "column";
}
};
/**
@ -168,6 +184,9 @@ public:
return v;
}
std::string str() const {
return "flood";
}
private:
void maybeFlood(DFCoord c, std::stack<DFCoord> &to_flood, MapExtras::MapCache &mc) {
if (mc.testCoord(c)) {
@ -176,3 +195,7 @@ private:
}
Core *c_;
};
std::ostream &operator<<(std::ostream &stream, const Brush& brush) {
stream << brush.str();
}

@ -20,10 +20,11 @@ if(BUILD_DEV_PLUGINS)
add_subdirectory (devel)
endif()
OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." OFF)
if(BUILD_DF2MC)
add_subdirectory (df2mc)
endif()
#It's dead :<
#OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." OFF)
#if(BUILD_DF2MC)
# add_subdirectory (df2mc)
#endif()
OPTION(BUILD_MAPEXPORT "Build map exporter." ON)
if (BUILD_MAPEXPORT)
@ -40,6 +41,10 @@ if (BUILD_RUBY)
add_subdirectory (ruby)
endif()
install(DIRECTORY lua/
DESTINATION ${DFHACK_LUA_DESTINATION}/plugins
FILES_MATCHING PATTERN "*.lua")
# Protobuf
FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)
@ -102,6 +107,8 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(feature feature.cpp)
DFHACK_PLUGIN(lair lair.cpp)
DFHACK_PLUGIN(zone zone.cpp)
# this one exports functions to lua
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
# not yet. busy with other crud again...
#DFHACK_PLUGIN(versionosd versionosd.cpp)
endif()

@ -121,6 +121,12 @@ command_result lua_run_file (color_ostream &out, std::vector <std::string> &para
}
command_result lua_run (color_ostream &out, std::vector <std::string> &parameters)
{
if (!parameters.empty() && parameters[0] == "--core-context")
{
Lua::InterpreterLoop(out, Lua::Core::State, "core lua");
return CR_OK;
}
mymutex->lock();
lua::state s=lua::glua::Get();

@ -86,14 +86,15 @@ loadall(plugins)
dofile_silent("dfusion/initcustom.lua")
local args={...}
for k,v in pairs(args) do
local f,err=load(v)
if f then
f()
else
Console.printerr(err)
end
local f,err=load(table.concat(args,' '))
if f then
f()
else
Console.printerr(err)
end
if not INIT then
mainmenu(plugins)
end

@ -274,19 +274,50 @@ function tools.changesite(names)
print(string.format("%x->%d",off,n2))
engine.poke(off,ptr_site.type,n2)
end
function tools.project(unit)
function tools.project(unit,trg)
if unit==nil then
unit=getSelectedUnit()
end
if unit==nil then
unit=getCreatureAtPos(getxyz())
unit=getCreatureAtPointer()
end
if unit==nil then
error("Failed to project unit. Unit not selected/valid")
end
-- todo: add projectile to world, point to unit, add flag to unit, add gen-ref to projectile.
local p=df.proj_unitst:new()
local startpos={x=unit.pos.x,y=unit.pos.y,z=unit.pos.z}
p.origin_pos=startpos
p.target_pos=trg
p.cur_pos=startpos
p.prev_pos=startpos
p.unit=unit
--- wtf stuff
p.unk14=100
p.unk16=-1
p.unk23=-1
p.fall_delay=5
p.fall_counter=5
p.collided=true
-- end wtf
local citem=df.global.world.proj_list
local maxid=1
local newlink=df.proj_list_link:new()
newlink.item=p
while citem.item~= nil do
if citem.item.id>maxid then maxid=citem.item.id end
if citem.next ~= nil then
citem=citem.next
else
break
end
end
p.id=maxid+1
newlink.prev=citem
citem.next=newlink
local proj_ref=df.general_ref_projectile:new()
proj_ref.projectile_id=p.id
unit.refs:insert(#unit.refs,proj_ref)
unit.flags1.projectile=true
end
function tools.empregnate(unit)
if unit==nil then

@ -8,6 +8,8 @@
#include "modules/Materials.h"
#include "modules/Maps.h"
#include "modules/Items.h"
#include "modules/Gui.h"
#include "modules/Units.h"
#include "DataDefs.h"
#include "df/world.h"
@ -159,31 +161,6 @@ static bool bodyswap_hotkey(df::viewscreen *top)
!!virtual_cast<df::viewscreen_dungeon_monsterstatusst>(top);
}
df::unit *getCurUnit()
{
auto top = Core::getTopViewscreen();
if (VIRTUAL_CAST_VAR(ms, df::viewscreen_dungeon_monsterstatusst, top))
return ms->unit;
return NULL;
}
df::nemesis_record *getNemesis(df::unit *unit)
{
if (!unit)
return NULL;
for (unsigned i = 0; i < unit->refs.size(); i++)
{
df::nemesis_record *rv = unit->refs[i]->getNemesis();
if (rv && rv->unit == unit)
return rv;
}
return NULL;
}
bool bodySwap(color_ostream &out, df::unit *player)
{
if (!player)
@ -219,7 +196,7 @@ df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap)
if (restore_swap)
{
df::unit *ctl = world->units.other[0][0];
auto ctl_nemesis = getNemesis(ctl);
auto ctl_nemesis = Units::getNemesis(ctl);
if (ctl_nemesis != real_nemesis)
{
@ -672,8 +649,8 @@ command_result adv_bodyswap (color_ostream &out, std::vector <std::string> & par
return CR_FAILURE;
// Get the unit to swap to
auto new_unit = getCurUnit();
auto new_nemesis = getNemesis(new_unit);
auto new_unit = Gui::getSelectedUnit(out, true);
auto new_nemesis = Units::getNemesis(new_unit);
if (!new_nemesis)
{
@ -803,7 +780,7 @@ command_result adv_tools (color_ostream &out, std::vector <std::string> & parame
if (!num)
continue;
df::map_block *block = Maps::getBlockAbs(item->pos);
df::map_block *block = Maps::getTileBlock(item->pos);
if (!block)
continue;

@ -147,26 +147,13 @@ static command_result autodump_main(color_ostream &out, vector <string> & parame
}
}
}
coordmap counts;
// proceed with the dumpification operation
for(size_t i=0; i< numItems; i++)
{
df::item * itm = world->items.all[i];
DFCoord pos_item(itm->pos.x, itm->pos.y, itm->pos.z);
// keep track how many items are at places. all items.
coordmap::iterator it = counts.find(pos_item);
if(it == counts.end())
{
pair< coordmap::iterator, bool > inserted = counts.insert(make_pair(pos_item,1));
it = inserted.first;
}
else
{
it->second ++;
}
// iterator is valid here, we use it later to decrement the pile counter if the item is moved
// only dump the stuff marked for dumping and laying on the ground
if ( !itm->flags.bits.dump
|| !itm->flags.bits.on_ground
@ -194,38 +181,8 @@ static command_result autodump_main(color_ostream &out, vector <string> & parame
itm->flags.bits.forbid = true;
// Don't move items if they're already at the cursor
if (pos_cursor == pos_item)
continue;
// Do we need to fix block-local item ID vector?
if(pos_item/16 != pos_cursor/16)
{
// yes...
cerr << "Moving from block to block!" << endl;
df::map_block * bl_src = Maps::getBlockAbs(itm->pos.x, itm->pos.y, itm->pos.z);
df::map_block * bl_tgt = Maps::getBlockAbs(cx, cy, cz);
if(bl_src)
{
remove(bl_src->items.begin(), bl_src->items.end(),itm->id);
}
else
{
cerr << "No source block" << endl;
}
if(bl_tgt)
{
bl_tgt->items.push_back(itm->id);
}
else
{
cerr << "No target block" << endl;
}
}
// Move the item
itm->pos.x = pos_cursor.x;
itm->pos.y = pos_cursor.y;
itm->pos.z = pos_cursor.z;
if (pos_cursor != pos_item)
Items::moveToGround(MC, itm, pos_cursor);
}
else // destroy
{
@ -238,42 +195,14 @@ static command_result autodump_main(color_ostream &out, vector <string> & parame
itm->flags.bits.forbid = true;
itm->flags.bits.hidden = true;
}
// keeping track of item pile sizes ;)
it->second --;
dumped_total++;
}
if(!destroy) // TODO: do we have to do any of this when destroying items?
{
// for each item pile, see if it reached zero. if so, unset item flag on the tile it's on
coordmap::iterator it = counts.begin();
coordmap::iterator end = counts.end();
while(it != end)
{
if(it->second == 0)
{
df::tile_occupancy occ = MC.occupancyAt(it->first);
occ.bits.item = false;
MC.setOccupancyAt(it->first, occ);
}
it++;
}
// Set "item here" flag on target tile, if we moved any items to the target tile.
if (dumped_total > 0)
{
// assume there is a possibility the cursor points at some weird location with missing block data
Block * b = MC.BlockAt(pos_cursor / 16);
if(b)
{
df::tile_occupancy occ = MC.occupancyAt(pos_cursor);
occ.bits.item = 1;
MC.setOccupancyAt(pos_cursor,occ);
}
}
// write map changes back to DF.
// write map changes back to DF.
if(!destroy)
MC.WriteAll();
// Is this necessary? Is "forbid" a dirtyable attribute like "dig" is?
Maps::WriteDirtyBit(cx/16, cy/16, cz, true);
}
out.print("Done. %d items %s.\n", dumped_total, destroy ? "marked for destruction" : "quickdumped");
return CR_OK;
}

@ -9,6 +9,8 @@
#include <vector>
#include <algorithm>
#include "modules/World.h"
// DF data structure definition headers
#include "DataDefs.h"
#include <df/ui.h>
@ -62,6 +64,13 @@ static bool print_debug = 0;
static std::vector<int> state_count(5);
static PersistentDataItem config;
enum ConfigFlags {
CF_ENABLED = 1,
};
// Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters);
@ -333,6 +342,24 @@ static const dwarf_state dwarf_states[] = {
};
struct labor_info
{
PersistentDataItem config;
bool is_exclusive;
int active_dwarfs;
labor_mode mode() { return (labor_mode) config.ival(0); }
void set_mode(labor_mode mode) { config.ival(0) = mode; }
int minimum_dwarfs() { return config.ival(1); }
void set_minimum_dwarfs(int minimum_dwarfs) { config.ival(1) = minimum_dwarfs; }
int maximum_dwarfs() { return config.ival(2); }
void set_maximum_dwarfs(int maximum_dwarfs) { config.ival(2) = maximum_dwarfs; }
};
struct labor_default
{
labor_mode mode;
bool is_exclusive;
@ -341,9 +368,9 @@ struct labor_info
int active_dwarfs;
};
static struct labor_info* labor_infos;
static std::vector<struct labor_info> labor_infos;
static const struct labor_info default_labor_infos[] = {
static const struct labor_default default_labor_infos[] = {
/* MINE */ {AUTOMATIC, true, 2, 200, 0},
/* HAUL_STONE */ {HAULERS, false, 1, 200, 0},
/* HAUL_WOOD */ {HAULERS, false, 1, 200, 0},
@ -436,15 +463,105 @@ struct dwarf_info_t
bool has_exclusive_labor;
};
static bool isOptionEnabled(unsigned flag)
{
return config.isValid() && (config.ival(0) & flag) != 0;
}
static void setOptionEnabled(ConfigFlags flag, bool on)
{
if (!config.isValid())
return;
if (on)
config.ival(0) |= flag;
else
config.ival(0) &= ~flag;
}
static void cleanup_state()
{
labor_infos.clear();
}
static void reset_labor(df::enums::unit_labor::unit_labor labor)
{
labor_infos[labor].set_minimum_dwarfs(default_labor_infos[labor].minimum_dwarfs);
labor_infos[labor].set_maximum_dwarfs(default_labor_infos[labor].maximum_dwarfs);
labor_infos[labor].set_mode(default_labor_infos[labor].mode);
}
static void init_state()
{
auto pworld = Core::getInstance().getWorld();
config = pworld->GetPersistentData("autolabor/config");
if (config.isValid() && config.ival(0) == -1)
config.ival(0) = 0;
enable_autolabor = isOptionEnabled(CF_ENABLED);
if (!enable_autolabor)
return;
// Load labors from save
labor_infos.resize(ARRAY_COUNT(default_labor_infos));
std::vector<PersistentDataItem> items;
pworld->GetPersistentData(&items, "autolabor/labors/", true);
for (auto p = items.begin(); p != items.end(); p++)
{
string key = p->key();
df::enums::unit_labor::unit_labor labor = (df::enums::unit_labor::unit_labor) atoi(key.substr(strlen("autolabor/labors/")).c_str());
if (labor >= 0 && labor <= labor_infos.size())
{
labor_infos[labor].config = *p;
labor_infos[labor].is_exclusive = default_labor_infos[labor].is_exclusive;
labor_infos[labor].active_dwarfs = 0;
}
}
// Add default labors for those not in save
for (int i = 0; i < ARRAY_COUNT(default_labor_infos); i++) {
if (labor_infos[i].config.isValid())
continue;
std::stringstream name;
name << "autolabor/labors/" << i;
labor_infos[i].config = pworld->AddPersistentData(name.str());
labor_infos[i].is_exclusive = default_labor_infos[i].is_exclusive;
labor_infos[i].active_dwarfs = 0;
reset_labor((df::enums::unit_labor::unit_labor) i);
}
}
static void enable_plugin(color_ostream &out)
{
auto pworld = Core::getInstance().getWorld();
if (!config.isValid())
{
config = pworld->AddPersistentData("autolabor/config");
config.ival(0) = 0;
}
setOptionEnabled(CF_ENABLED, true);
enable_autolabor = true;
out << "Enabling the plugin." << endl;
cleanup_state();
init_state();
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
// initialize labor infos table from default table
if(ARRAY_COUNT(default_labor_infos) != ENUM_LAST_ITEM(unit_labor) + 1)
return CR_FAILURE;
labor_infos = new struct labor_info[ARRAY_COUNT(default_labor_infos)];
for (int i = 0; i < ARRAY_COUNT(default_labor_infos); i++) {
labor_infos[i] = default_labor_infos[i];
}
// Fill the command list with your commands.
commands.push_back(PluginCommand(
"autolabor", "Automatically manage dwarf labors.",
@ -459,8 +576,14 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
" Set a labor to be handled by hauler dwarves.\n"
" autolabor <labor> disable\n"
" Turn off autolabor for a specific labor.\n"
" autolabor <labor> reset\n"
" Return a labor to the default handling.\n"
" autolabor reset-all\n"
" Return all labors to the default handling.\n"
" autolabor list\n"
" List current status of all labors.\n"
" autolabor status\n"
" Show basic status information.\n"
"Function:\n"
" When enabled, autolabor periodically checks your dwarves and enables or\n"
" disables labors. It tries to keep as many dwarves as possible busy but\n"
@ -477,13 +600,15 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
" autolabor CUTWOOD disable\n"
" Turn off autolabor for wood cutting.\n"
));
init_state();
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
// release the labor info table;
delete [] labor_infos;
cleanup_state();
return CR_OK;
}
@ -506,7 +631,7 @@ struct laborinfo_sorter
{
bool operator() (int i,int j)
{
return labor_infos[i].mode < labor_infos[j].mode;
return labor_infos[i].mode() < labor_infos[j].mode();
};
};
@ -520,6 +645,23 @@ struct values_sorter
std::vector<int> & values;
};
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_MAP_LOADED:
cleanup_state();
init_state();
break;
case SC_MAP_UNLOADED:
cleanup_state();
break;
default:
break;
}
return CR_OK;
}
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
static int step_count = 0;
@ -757,7 +899,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
auto labor = *lp;
if (labor_infos[labor].mode != DISABLE)
if (labor_infos[labor].mode() != DISABLE)
continue;
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
@ -785,7 +927,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
df::job_skill skill = labor_to_skill[labor];
if (labor_infos[labor].mode != AUTOMATIC)
if (labor_infos[labor].mode() != AUTOMATIC)
continue;
int best_dwarf = 0;
@ -796,7 +938,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
std::map<int, int> dwarf_skill;
std::vector<bool> previously_enabled(n_dwarfs);
auto mode = labor_infos[labor].mode;
auto mode = labor_infos[labor].mode();
// Find candidate dwarfs, and calculate a preference value for each dwarf
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
@ -867,8 +1009,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
dwarfs[dwarf]->status.labors[labor] = false;
}
int min_dwarfs = labor_infos[labor].minimum_dwarfs;
int max_dwarfs = labor_infos[labor].maximum_dwarfs;
int min_dwarfs = labor_infos[labor].minimum_dwarfs();
int max_dwarfs = labor_infos[labor].maximum_dwarfs();
// Special - don't assign hunt without a butchers, or fish without a fishery
if (df::enums::unit_labor::HUNT == labor && !has_butchers)
@ -964,7 +1106,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
assert(labor < ARRAY_COUNT(labor_infos));
*/
if (labor_infos[labor].mode != HAULERS)
if (labor_infos[labor].mode() != HAULERS)
continue;
for (int i = 0; i < num_haulers; i++)
@ -1009,31 +1151,58 @@ void print_labor (df::enums::unit_labor::unit_labor labor, color_ostream &out)
out << labor_name << ": ";
for (int i = 0; i < 20 - (int)labor_name.length(); i++)
out << ' ';
if (labor_infos[labor].mode == DISABLE)
if (labor_infos[labor].mode() == DISABLE)
out << "disabled" << endl;
else
{
if (labor_infos[labor].mode == HAULERS)
if (labor_infos[labor].mode() == HAULERS)
out << "haulers";
else
out << "minimum " << labor_infos[labor].minimum_dwarfs << ", maximum " << labor_infos[labor].maximum_dwarfs;
out << "minimum " << labor_infos[labor].minimum_dwarfs() << ", maximum " << labor_infos[labor].maximum_dwarfs();
out << ", currently " << labor_infos[labor].active_dwarfs << " dwarfs" << endl;
}
}
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters)
{
CoreSuspender suspend;
if (!Core::getInstance().isWorldLoaded()) {
out.printerr("World is not loaded: please load a game first.\n");
return CR_FAILURE;
}
if (parameters.size() == 1 &&
(parameters[0] == "0" || parameters[0] == "enable" ||
parameters[0] == "1" || parameters[0] == "disable"))
{
if (parameters[0] == "0" || parameters[0] == "disable")
enable_autolabor = 0;
bool enable = (parameters[0] == "1" || parameters[0] == "enable");
if (enable && !enable_autolabor)
{
enable_plugin(out);
}
else
enable_autolabor = 1;
out.print("autolabor %sactivated.\n", (enable_autolabor ? "" : "de"));
{
if (enable_autolabor)
{
enable_autolabor = false;
setOptionEnabled(CF_ENABLED, false);
}
out << "The plugin is disabled." << endl;
return CR_OK;
}
return CR_OK;
}
else if (parameters.size() == 2 || parameters.size() == 3) {
if (!enable_autolabor)
{
out << "Error: The plugin is not enabled." << endl;
return CR_FAILURE;
}
if (parameters.size() == 2 || parameters.size() == 3) {
df::enums::unit_labor::unit_labor labor = df::enums::unit_labor::NONE;
FOR_ENUM_ITEMS(unit_labor, test_labor)
@ -1050,13 +1219,19 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
if (parameters[1] == "haulers")
{
labor_infos[labor].mode = HAULERS;
labor_infos[labor].set_mode(HAULERS);
print_labor(labor, out);
return CR_OK;
}
if (parameters[1] == "disable")
{
labor_infos[labor].mode = DISABLE;
labor_infos[labor].set_mode(DISABLE);
print_labor(labor, out);
return CR_OK;
}
if (parameters[1] == "reset")
{
reset_labor(labor);
print_labor(labor, out);
return CR_OK;
}
@ -1072,12 +1247,22 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
return CR_WRONG_USAGE;
}
labor_infos[labor].minimum_dwarfs = minimum;
labor_infos[labor].maximum_dwarfs = maximum;
labor_infos[labor].mode = AUTOMATIC;
labor_infos[labor].set_minimum_dwarfs(minimum);
labor_infos[labor].set_maximum_dwarfs(maximum);
labor_infos[labor].set_mode(AUTOMATIC);
print_labor(labor, out);
return CR_OK;
}
else if (parameters.size() == 1 && parameters[0] == "list") {
else if (parameters.size() == 1 && parameters[0] == "reset-all") {
for (int i = 0; i < labor_infos.size(); i++)
{
reset_labor((df::enums::unit_labor::unit_labor) i);
}
out << "All labors reset." << endl;
return CR_OK;
}
else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") {
if (!enable_autolabor)
{
out << "autolabor not activated." << endl;
@ -1096,23 +1281,30 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
}
out << endl;
FOR_ENUM_ITEMS(unit_labor, labor)
if (parameters[0] == "list")
{
if (labor == df::enums::unit_labor::NONE)
continue;
FOR_ENUM_ITEMS(unit_labor, labor)
{
if (labor == df::enums::unit_labor::NONE)
continue;
print_labor(labor, out);
print_labor(labor, out);
}
}
return CR_OK;
}
else if (parameters.size() == 1 && parameters[0] == "debug") {
print_debug = 1;
return CR_OK;
}
else
{
out.print("Automatically assigns labors to dwarves.\n"
"Activate with 'autolabor 1', deactivate with 'autolabor 0'.\n"
"Current state: %d.\n", enable_autolabor);
}
return CR_OK;
return CR_OK;
}
}

@ -0,0 +1,702 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "Error.h"
#include "DataFuncs.h"
#include "LuaTools.h"
#include "modules/Gui.h"
#include "modules/Job.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "modules/World.h"
#include "modules/Units.h"
#include "TileTypes.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/world.h"
#include "df/unit.h"
#include "df/burrow.h"
#include "df/map_block.h"
#include "df/block_burrow.h"
#include "df/job.h"
#include "df/job_list_link.h"
#include "MiscUtils.h"
#include <stdlib.h>
using std::vector;
using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using namespace dfproto;
using df::global::ui;
using df::global::world;
/*
* Initialization.
*/
static command_result burrow(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("burrows");
static void init_map(color_ostream &out);
static void deinit_map(color_ostream &out);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"burrow", "Miscellaneous burrow control.", burrow, false,
" burrow enable options...\n"
" burrow disable options...\n"
" Enable or disable features of the plugin.\n"
" See below for a list and explanation.\n"
" burrow clear-units burrow burrow...\n"
" burrow clear-tiles burrow burrow...\n"
" Removes all units or tiles from the burrows.\n"
" burrow set-units target-burrow src-burrow...\n"
" burrow add-units target-burrow src-burrow...\n"
" burrow remove-units target-burrow src-burrow...\n"
" Adds or removes units in source burrows to/from the target\n"
" burrow. Set is equivalent to clear and add.\n"
" burrow set-tiles target-burrow src-burrow...\n"
" burrow add-tiles target-burrow src-burrow...\n"
" burrow remove-tiles target-burrow src-burrow...\n"
" Adds or removes tiles in source burrows to/from the target\n"
" burrow. In place of a source burrow it is possible to use\n"
" one of the following keywords:\n"
" ABOVE_GROUND, SUBTERRANEAN, INSIDE, OUTSIDE,\n"
" LIGHT, DARK, HIDDEN, REVEALED\n"
"Implemented features:\n"
" auto-grow\n"
" When a wall inside a burrow with a name ending in '+' is dug\n"
" out, the burrow is extended to newly-revealed adjacent walls.\n"
" This final '+' may be omitted in burrow name args of commands above.\n"
" Note: Digging 1-wide corridors with the miner inside the burrow is SLOW.\n"
));
if (Core::getInstance().isMapLoaded())
init_map(out);
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
deinit_map(out);
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_MAP_LOADED:
deinit_map(out);
if (df::global::game_mode &&
*df::global::game_mode == GAMEMODE_DWARF)
init_map(out);
break;
case SC_MAP_UNLOADED:
deinit_map(out);
break;
default:
break;
}
return CR_OK;
}
/*
* State change tracking.
*/
static int name_burrow_id = -1;
static void handle_burrow_rename(color_ostream &out, df::burrow *burrow);
DEFINE_LUA_EVENT_1(onBurrowRename, handle_burrow_rename, df::burrow*);
static void detect_burrow_renames(color_ostream &out)
{
if (ui->main.mode == ui_sidebar_mode::Burrows &&
ui->burrows.in_edit_name_mode &&
ui->burrows.sel_id >= 0)
{
name_burrow_id = ui->burrows.sel_id;
}
else if (name_burrow_id >= 0)
{
auto burrow = df::burrow::find(name_burrow_id);
name_burrow_id = -1;
if (burrow)
onBurrowRename(out, burrow);
}
}
struct DigJob {
int id;
df::job_type job;
df::coord pos;
df::tiletype old_tile;
};
static int next_job_id_save = 0;
static std::map<int,DigJob> diggers;
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos,
df::tiletype old_tile, df::tiletype new_tile);
DEFINE_LUA_EVENT_4(onDigComplete, handle_dig_complete,
df::job_type, df::coord, df::tiletype, df::tiletype);
static void detect_digging(color_ostream &out)
{
for (auto it = diggers.begin(); it != diggers.end();)
{
auto worker = df::unit::find(it->first);
if (!worker || !worker->job.current_job ||
worker->job.current_job->id != it->second.id)
{
//out.print("Dig job %d expired.\n", it->second.id);
df::coord pos = it->second.pos;
if (auto block = Maps::getTileBlock(pos))
{
df::tiletype new_tile = block->tiletype[pos.x&15][pos.y&15];
//out.print("Tile %d -> %d\n", it->second.old_tile, new_tile);
if (new_tile != it->second.old_tile)
{
onDigComplete(out, it->second.job, pos, it->second.old_tile, new_tile);
//if (worker && !worker->job.current_job)
// worker->counters.think_counter = worker->counters.job_counter = 0;
}
}
auto cur = it; ++it; diggers.erase(cur);
}
else
++it;
}
std::vector<df::job*> jvec;
if (Job::listNewlyCreated(&jvec, &next_job_id_save))
{
for (size_t i = 0; i < jvec.size(); i++)
{
auto job = jvec[i];
auto type = ENUM_ATTR(job_type, type, job->job_type);
if (type != job_type_class::Digging)
continue;
auto worker = Job::getWorker(job);
if (!worker)
continue;
df::coord pos = job->pos;
auto block = Maps::getTileBlock(pos);
if (!block)
continue;
auto &info = diggers[worker->id];
//out.print("New dig job %d.\n", job->id);
info.id = job->id;
info.job = job->job_type;
info.pos = pos;
info.old_tile = block->tiletype[pos.x&15][pos.y&15];
}
}
}
static bool active = false;
static bool auto_grow = false;
static std::vector<int> grow_burrows;
DFhackCExport command_result plugin_onupdate(color_ostream &out)
{
if (!active)
return CR_OK;
detect_burrow_renames(out);
if (auto_grow)
detect_digging(out);
return CR_OK;
}
/*
* Config and processing
*/
static std::map<std::string,int> name_lookup;
static void parse_names()
{
auto &list = ui->burrows.list;
grow_burrows.clear();
name_lookup.clear();
for (size_t i = 0; i < list.size(); i++)
{
auto burrow = list[i];
std::string name = burrow->name;
if (!name.empty())
{
name_lookup[name] = burrow->id;
if (name[name.size()-1] == '+')
{
grow_burrows.push_back(burrow->id);
name.resize(name.size()-1);
}
if (!name.empty())
name_lookup[name] = burrow->id;
}
}
}
static void reset_tracking()
{
diggers.clear();
next_job_id_save = 0;
}
static void init_map(color_ostream &out)
{
auto config = Core::getInstance().getWorld()->GetPersistentData("burrows/config");
if (config.isValid())
{
auto_grow = !!(config.ival(0) & 1);
}
parse_names();
name_burrow_id = -1;
reset_tracking();
active = true;
if (auto_grow && !grow_burrows.empty())
out.print("Auto-growing %d burrows.\n", grow_burrows.size());
}
static void deinit_map(color_ostream &out)
{
active = false;
auto_grow = false;
reset_tracking();
}
static PersistentDataItem create_config(color_ostream &out)
{
bool created;
auto rv = Core::getInstance().getWorld()->GetPersistentData("burrows/config", &created);
if (created && rv.isValid())
rv.ival(0) = 0;
if (!rv.isValid())
out.printerr("Could not write configuration.");
return rv;
}
static void enable_auto_grow(color_ostream &out, bool enable)
{
if (enable == auto_grow)
return;
auto config = create_config(out);
if (!config.isValid())
return;
if (enable)
config.ival(0) |= 1;
else
config.ival(0) &= ~1;
auto_grow = enable;
if (enable)
reset_tracking();
}
static void handle_burrow_rename(color_ostream &out, df::burrow *burrow)
{
parse_names();
}
static void add_to_burrows(std::vector<df::burrow*> &burrows, df::coord pos)
{
for (size_t i = 0; i < burrows.size(); i++)
Maps::setBurrowTile(burrows[i], pos, true);
}
static void add_walls_to_burrows(color_ostream &out, std::vector<df::burrow*> &burrows,
MapExtras::MapCache &mc, df::coord pos1, df::coord pos2)
{
for (int x = pos1.x; x <= pos2.x; x++)
{
for (int y = pos1.y; y <= pos2.y; y++)
{
for (int z = pos1.z; z <= pos2.z; z++)
{
df::coord pos(x,y,z);
auto tile = mc.tiletypeAt(pos);
if (isWallTerrain(tile))
add_to_burrows(burrows, pos);
}
}
}
}
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos,
df::tiletype old_tile, df::tiletype new_tile)
{
if (!isWalkable(new_tile))
return;
std::vector<df::burrow*> to_grow;
for (size_t i = 0; i < grow_burrows.size(); i++)
{
auto b = df::burrow::find(grow_burrows[i]);
if (b && Maps::isBurrowTile(b, pos))
to_grow.push_back(b);
}
//out.print("%d to grow.\n", to_grow.size());
if (to_grow.empty())
return;
MapExtras::MapCache mc;
if (!isWalkable(old_tile))
{
add_walls_to_burrows(out, to_grow, mc, pos+df::coord(-1,-1,0), pos+df::coord(1,1,0));
if (isWalkableUp(new_tile))
add_to_burrows(to_grow, pos+df::coord(0,0,1));
if (tileShape(new_tile) == tiletype_shape::RAMP)
{
add_walls_to_burrows(out, to_grow, mc,
pos+df::coord(-1,-1,1), pos+df::coord(1,1,1));
}
}
if (LowPassable(new_tile) && !LowPassable(old_tile))
{
add_to_burrows(to_grow, pos-df::coord(0,0,1));
if (tileShape(new_tile) == tiletype_shape::RAMP_TOP)
{
add_walls_to_burrows(out, to_grow, mc,
pos+df::coord(-1,-1,-1), pos+df::coord(1,1,-1));
}
}
}
static void renameBurrow(color_ostream &out, df::burrow *burrow, std::string name)
{
CHECK_NULL_POINTER(burrow);
// The event makes this absolutely necessary
CoreSuspender suspend;
burrow->name = name;
onBurrowRename(out, burrow);
}
static df::burrow *findByName(color_ostream &out, std::string name, bool silent = false)
{
int id = -1;
if (name_lookup.count(name))
id = name_lookup[name];
auto rv = df::burrow::find(id);
if (!rv && !silent)
out.printerr("Burrow not found: '%s'\n", name.c_str());
return rv;
}
static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
{
CHECK_NULL_POINTER(target);
CHECK_NULL_POINTER(source);
if (source == target)
{
if (!enable)
Units::clearBurrowMembers(target);
return;
}
for (size_t i = 0; i < source->units.size(); i++)
{
auto unit = df::unit::find(source->units[i]);
if (unit)
Units::setInBurrow(unit, target, enable);
}
}
static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
{
CHECK_NULL_POINTER(target);
CHECK_NULL_POINTER(source);
if (source == target)
{
if (!enable)
Maps::clearBurrowTiles(target);
return;
}
std::vector<df::map_block*> pvec;
Maps::listBurrowBlocks(&pvec, source);
for (size_t i = 0; i < pvec.size(); i++)
{
auto block = pvec[i];
auto smask = Maps::getBlockBurrowMask(source, block);
if (!smask)
continue;
auto tmask = Maps::getBlockBurrowMask(target, block, enable);
if (!tmask)
continue;
if (enable)
{
for (int j = 0; j < 16; j++)
tmask->tile_bitmask[j] |= smask->tile_bitmask[j];
}
else
{
for (int j = 0; j < 16; j++)
tmask->tile_bitmask[j] &= ~smask->tile_bitmask[j];
if (!tmask->has_assignments())
Maps::deleteBlockBurrowMask(target, block, tmask);
}
}
}
static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mask,
df::tile_designation d_value, bool enable)
{
CHECK_NULL_POINTER(target);
auto &blocks = world->map.map_blocks;
for (size_t i = 0; i < blocks.size(); i++)
{
auto block = blocks[i];
df::block_burrow *mask = NULL;
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if ((block->designation[x][y].whole & d_mask.whole) != d_value.whole)
continue;
if (!mask)
mask = Maps::getBlockBurrowMask(target, block, enable);
if (!mask)
goto next_block;
mask->setassignment(x, y, enable);
}
}
if (mask && !enable && !mask->has_assignments())
Maps::deleteBlockBurrowMask(target, block, mask);
next_block:;
}
}
static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable)
{
CHECK_NULL_POINTER(target);
df::tile_designation mask(0);
df::tile_designation value(0);
if (name == "ABOVE_GROUND")
mask.bits.subterranean = true;
else if (name == "SUBTERRANEAN")
mask.bits.subterranean = value.bits.subterranean = true;
else if (name == "LIGHT")
mask.bits.light = value.bits.light = true;
else if (name == "DARK")
mask.bits.light = true;
else if (name == "OUTSIDE")
mask.bits.outside = value.bits.outside = true;
else if (name == "INSIDE")
mask.bits.outside = true;
else if (name == "HIDDEN")
mask.bits.hidden = value.bits.hidden = true;
else if (name == "REVEALED")
mask.bits.hidden = true;
else
return false;
setTilesByDesignation(target, mask, value, enable);
return true;
}
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(renameBurrow),
DFHACK_LUA_FUNCTION(findByName),
DFHACK_LUA_FUNCTION(copyUnits),
DFHACK_LUA_FUNCTION(copyTiles),
DFHACK_LUA_FUNCTION(setTilesByKeyword),
DFHACK_LUA_END
};
DFHACK_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onBurrowRename),
DFHACK_LUA_EVENT(onDigComplete),
DFHACK_LUA_END
};
static command_result burrow(color_ostream &out, vector <string> &parameters)
{
CoreSuspender suspend;
if (!active)
{
out.printerr("The plugin cannot be used without map.\n");
return CR_FAILURE;
}
string cmd;
if (!parameters.empty())
cmd = parameters[0];
if (cmd == "enable" || cmd == "disable")
{
if (parameters.size() < 2)
return CR_WRONG_USAGE;
bool state = (cmd == "enable");
for (int i = 1; i < parameters.size(); i++)
{
string &option = parameters[i];
if (option == "auto-grow")
enable_auto_grow(out, state);
else
return CR_WRONG_USAGE;
}
}
else if (cmd == "clear-units")
{
if (parameters.size() < 2)
return CR_WRONG_USAGE;
for (int i = 1; i < parameters.size(); i++)
{
auto target = findByName(out, parameters[i]);
if (!target)
return CR_WRONG_USAGE;
Units::clearBurrowMembers(target);
}
}
else if (cmd == "set-units" || cmd == "add-units" || cmd == "remove-units")
{
if (parameters.size() < 3)
return CR_WRONG_USAGE;
auto target = findByName(out, parameters[1]);
if (!target)
return CR_WRONG_USAGE;
if (cmd == "set-units")
Units::clearBurrowMembers(target);
bool enable = (cmd != "remove-units");
for (int i = 2; i < parameters.size(); i++)
{
auto source = findByName(out, parameters[i]);
if (!source)
return CR_WRONG_USAGE;
copyUnits(target, source, enable);
}
}
else if (cmd == "clear-tiles")
{
if (parameters.size() < 2)
return CR_WRONG_USAGE;
for (int i = 1; i < parameters.size(); i++)
{
auto target = findByName(out, parameters[i]);
if (!target)
return CR_WRONG_USAGE;
Maps::clearBurrowTiles(target);
}
}
else if (cmd == "set-tiles" || cmd == "add-tiles" || cmd == "remove-tiles")
{
if (parameters.size() < 3)
return CR_WRONG_USAGE;
auto target = findByName(out, parameters[1]);
if (!target)
return CR_WRONG_USAGE;
if (cmd == "set-tiles")
Maps::clearBurrowTiles(target);
bool enable = (cmd != "remove-tiles");
for (int i = 2; i < parameters.size(); i++)
{
if (setTilesByKeyword(target, parameters[i], enable))
continue;
auto source = findByName(out, parameters[i]);
if (!source)
return CR_WRONG_USAGE;
copyTiles(target, source, enable);
}
}
else
{
if (!parameters.empty() && cmd != "?")
out.printerr("Invalid command: %s\n", cmd.c_str());
return CR_WRONG_USAGE;
}
return CR_OK;
}

@ -347,6 +347,9 @@ command_result changeitem_execute(
// subtype and mode should match to avoid doing dumb stuff like changing boulders into meat whatever
// changing a stone cabinet to wood is fine, though. as well as lots of other combinations.
// still, it's better to make the user activate 'force' if he really wants to.
// fixme: changing material of cloth items needs more work...
// <_Q> cloth items have a "CLOTH" improvement which tells you about the cloth that was used to make it
if(force||(mat_old.subtype == mat_new.subtype && mat_old.mode==mat_new.mode))
{

@ -189,13 +189,13 @@ command_result changelayer (color_ostream &out, std::vector <std::string> & para
uint32_t tileY = cursorY % 16;
MapExtras::Block * b = mc.BlockAt(cursor/16);
if(!b && !b->valid)
if(!b || !b->is_valid())
{
out.printerr("No data.\n");
return CR_OK;
}
mapblock40d & block = b->raw;
df::tile_designation &des = block.designation[tileX][tileY];
df::tile_designation des = b->DesignationAt(cursor%16);
// get biome and geolayer at cursor position
uint32_t biome = des.bits.biome;

@ -50,7 +50,7 @@ command_result df_changevein (color_ostream &out, vector <string> & parameters)
return CR_FAILURE;
}
df::map_block *block = Maps::getBlockAbs(cursor->x, cursor->y, cursor->z);
df::map_block *block = Maps::getTileBlock(cursor->x, cursor->y, cursor->z);
if (!block)
{
out.printerr("Invalid tile selected.\n");

@ -127,7 +127,7 @@ command_result spotclean (color_ostream &out, vector <string> & parameters)
out.printerr("Map is not available.\n");
return CR_FAILURE;
}
df::map_block *block = Maps::getBlockAbs(cursor->x, cursor->y, cursor->z);
df::map_block *block = Maps::getTileBlock(cursor->x, cursor->y, cursor->z);
if (block == NULL)
{
out.printerr("Invalid map block selected!\n");

@ -98,8 +98,7 @@ command_result df_cleanowned (color_ostream &out, vector <string> & parameters)
if (!item->flags.bits.owned)
{
int32_t owner = Items::getItemOwnerID(item);
if (owner >= 0)
if (Items::getOwner(item))
{
out.print("Fixing a misflagged item: \t");
confiscate = true;
@ -168,14 +167,14 @@ command_result df_cleanowned (color_ostream &out, vector <string> & parameters)
item->getWear()
);
df::unit *owner = Items::getItemOwner(item);
df::unit *owner = Items::getOwner(item);
if (owner)
out.print(", owner %s", Translation::TranslateName(&owner->name,false).c_str());
if (!dry_run)
{
if (!Items::removeItemOwner(item))
if (!Items::setOwner(item,NULL))
out.print("(unsuccessfully) ");
if (dump)
item->flags.bits.dump = 1;

@ -38,7 +38,7 @@ command_result df_deramp (color_ostream &out, vector <string> & parameters)
for (int i = 0; i < blocks_total; i++)
{
df::map_block *block = world->map.map_blocks[i];
df::map_block *above = Maps::getBlockAbs(block->map_pos.x, block->map_pos.y, block->map_pos.z + 1);
df::map_block *above = Maps::getTileBlock(block->map_pos + df::coord(0,0,1));
for (int x = 0; x < 16; x++)
{

@ -8,19 +8,47 @@
#include "DataDefs.h"
#include "df/world.h"
#include "df/world_raws.h"
#include "df/plant_raw.h"
#include "df/map_block.h"
#include "df/block_square_event.h"
//#include "df/block_square_event_type.h"
#include "df/block_square_event_grassst.h"
#include "TileTypes.h"
using std::string;
using std::vector;
using namespace std;
using namespace DFHack;
using df::global::world;
DFHACK_PLUGIN("regrass");
command_result df_regrass (color_ostream &out, vector <string> & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector<PluginCommand> &commands)
{
commands.push_back(PluginCommand("regrass", "Regrows surface grass.", df_regrass));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
command_result df_regrass (color_ostream &out, vector <string> & parameters)
{
bool max = false;
if (!parameters.empty())
return CR_WRONG_USAGE;
{
if(parameters[0] == "max")
max = true;
else
return CR_WRONG_USAGE;
}
CoreSuspender suspend;
@ -28,19 +56,95 @@ command_result df_regrass (color_ostream &out, vector <string> & parameters)
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
{
df::map_block *cur = world->map.map_blocks[i];
for (int x = 0; x < 16; x++)
// check block for grass events before looking at 16x16 tiles
df::block_square_event_grassst * grev = NULL;
for(size_t e=0; e<cur->block_events.size(); e++)
{
df::block_square_event * blev = cur->block_events[e];
df::block_square_event_type blevtype = blev->getType();
if(blevtype == df::block_square_event_type::grass)
{
grev = (df::block_square_event_grassst *)blev;
break;
}
}
if(!grev)
{
for (int y = 0; y < 16; y++)
// in this worst case we should check other blocks, create a new event etc
// but looking at some maps that should happen very very rarely if at all
// a standard map block seems to always have up to 10 grass events we can refresh
continue;
}
for (int y = 0; y < 16; y++)
{
for (int x = 0; x < 16; x++)
{
if (tileShape(cur->tiletype[x][y]) != tiletype_shape::FLOOR)
continue;
if (tileMaterial(cur->tiletype[x][y]) != tiletype_material::SOIL)
if ( tileShape(cur->tiletype[x][y]) != tiletype_shape::FLOOR
|| cur->designation[x][y].bits.subterranean
|| cur->occupancy[x][y].bits.building)
continue;
if (cur->designation[x][y].bits.subterranean)
continue;
if (cur->occupancy[x][y].bits.building)
int mat = tileMaterial(cur->tiletype[x][y]);
if ( mat != tiletype_material::SOIL
&& mat != tiletype_material::GRASS_DARK // refill existing grass, too
&& mat != tiletype_material::GRASS_LIGHT // refill existing grass, too
)
continue;
// max = set amounts of all grass events on that tile to 100
if(max)
{
for(size_t e=0; e<cur->block_events.size(); e++)
{
df::block_square_event * blev = cur->block_events[e];
df::block_square_event_type blevtype = blev->getType();
if(blevtype == df::block_square_event_type::grass)
{
df::block_square_event_grassst * gr_ev = (df::block_square_event_grassst *)blev;
gr_ev->amount[x][y] = 100;
}
}
}
else
{
// try to find the 'original' event
bool regrew = false;
for(size_t e=0; e<cur->block_events.size(); e++)
{
df::block_square_event * blev = cur->block_events[e];
df::block_square_event_type blevtype = blev->getType();
if(blevtype == df::block_square_event_type::grass)
{
df::block_square_event_grassst * gr_ev = (df::block_square_event_grassst *)blev;
if(gr_ev->amount[x][y] > 0)
{
gr_ev->amount[x][y] = 100;
regrew = true;
break;
}
}
}
// if original could not be found (meaning it was already depleted):
// refresh random grass event in the map block
if(!regrew)
{
vector <df::block_square_event_grassst *> gr_evs;
for(size_t e=0; e<cur->block_events.size(); e++)
{
df::block_square_event * blev = cur->block_events[e];
df::block_square_event_type blevtype = blev->getType();
if(blevtype == df::block_square_event_type::grass)
{
df::block_square_event_grassst * gr_ev = (df::block_square_event_grassst *)blev;
gr_evs.push_back(gr_ev);
}
}
int r = rand() % gr_evs.size();
gr_evs[r]->amount[x][y]=100;
}
}
cur->tiletype[x][y] = findRandomVariant((rand() & 1) ? tiletype::GrassLightFloor1 : tiletype::GrassDarkFloor1);
count++;
}
@ -51,16 +155,3 @@ command_result df_regrass (color_ostream &out, vector <string> & parameters)
out.print("Regrew %d tiles of grass.\n", count);
return CR_OK;
}
DFHACK_PLUGIN("regrass");
DFhackCExport command_result plugin_init (color_ostream &out, std::vector<PluginCommand> &commands)
{
commands.push_back(PluginCommand("regrass", "Regrows all surface grass, restoring outdoor plant growth for pre-0.31.19 worlds.", df_regrass));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}

@ -90,7 +90,7 @@ bool dig (MapExtras::MapCache & MCache,
{
DFCoord at (x,y,z);
auto b = MCache.BlockAt(at/16);
if(!b || !b->valid)
if(!b || !b->is_valid())
return false;
if(x == 0 || x == x_max * 16 - 1)
{
@ -1027,6 +1027,8 @@ command_result digv (color_ostream &out, vector <string> & parameters)
{
DFHack::DFCoord current = flood.top();
flood.pop();
if (MCache->tagAt(current))
continue;
int16_t vmat2 = MCache->veinMaterialAt(current);
tt = MCache->tiletypeAt(current);
if(!DFHack::isWallTerrain(tt))
@ -1061,7 +1063,8 @@ command_result digv (color_ostream &out, vector <string> & parameters)
}
if(MCache->testCoord(current))
{
MCache->clearVeinMaterialAt(current);
MCache->setTagAt(current, 1);
if(current.x < tx_max - 2)
{
flood.push(DFHack::DFCoord(current.x + 1, current.y, current.z));
@ -1209,6 +1212,8 @@ command_result digl (color_ostream &out, vector <string> & parameters)
{
DFHack::DFCoord current = flood.top();
flood.pop();
if (MCache->tagAt(current))
continue;
int16_t vmat2 = MCache->veinMaterialAt(current);
int16_t bmat2 = MCache->baseMaterialAt(current);
tt = MCache->tiletypeAt(current);
@ -1239,7 +1244,7 @@ command_result digl (color_ostream &out, vector <string> & parameters)
if(MCache->testCoord(current))
{
MCache->clearBaseMaterialAt(current);
MCache->setTagAt(current, 1);
if(current.x < tx_max - 2)
{
flood.push(DFHack::DFCoord(current.x + 1, current.y, current.z));

@ -65,8 +65,7 @@ command_result df_fixveins (color_ostream &out, vector <string> & parameters)
has_mineral[k] |= mineral->tile_bitmask[k];
}
t_feature local, global;
Maps::GetGlobalFeature(global, block->global_feature);
Maps::GetLocalFeature(local, df::coord2d(block->map_pos.x / 16, block->map_pos.y / 16), block->local_feature);
Maps::ReadFeatures(block, &local, &global);
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)

@ -289,9 +289,9 @@ static command_result job_duplicate(color_ostream &out, vector <string> & parame
}
// Actually clone
df::job *pnew = cloneJobStruct(job);
df::job *pnew = Job::cloneJobStruct(job);
linkJobIntoWorld(pnew);
Job::linkIntoWorld(pnew);
vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew);
return CR_OK;
@ -325,14 +325,14 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
return CR_WRONG_USAGE;
if (cmd == "query") {
printJobDetails(out, job);
Job::printJobDetails(out, job);
} else {
if (!Gui::workshop_job_hotkey(Core::getTopViewscreen()))
return CR_WRONG_USAGE;
df::building *selected = world->selected_building;
for (size_t i = 0; i < selected->jobs.size(); i++)
printJobDetails(out, selected->jobs[i]);
Job::printJobDetails(out, selected->jobs[i]);
}
}
else if (cmd == "item-material")
@ -355,7 +355,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
if (minfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Material does not match the requirements.\n");
printJobDetails(out, job);
Job::printJobDetails(out, job);
return CR_FAILURE;
}
@ -376,7 +376,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
out.printerr("WARNING: Due to a probable bug, creature & plant material subtype\n"
" is ignored unless the item type is also specified.\n");
printJobDetails(out, job);
Job::printJobDetails(out, job);
return CR_OK;
}
else if (cmd == "item-type")
@ -399,7 +399,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
if (iinfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Item type does not match the requirements.\n");
printJobDetails(out, job);
Job::printJobDetails(out, job);
return CR_FAILURE;
}
@ -407,7 +407,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
item->item_subtype = iinfo.subtype;
out << "Job item updated." << endl;
printJobDetails(out, job);
Job::printJobDetails(out, job);
return CR_OK;
}
else

@ -413,8 +413,8 @@ command_result df_liquids_execute(color_ostream &out)
Block * b = mcache.BlockAt((*iter)/16);
DFHack::t_blockflags bf = b->BlockFlags();
bf.bits.liquid_1 = true;
bf.bits.liquid_2 = true;
bf.bits.update_liquid = true;
bf.bits.update_liquid_twice = true;
b->setBlockFlags(bf);
iter++;
@ -500,20 +500,20 @@ command_result df_liquids_execute(color_ostream &out)
DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+")
{
bflags.bits.liquid_1 = true;
bflags.bits.liquid_2 = true;
bflags.bits.update_liquid = true;
bflags.bits.update_liquid_twice = true;
(*biter)->setBlockFlags(bflags);
}
else if(flowmode == "f-")
{
bflags.bits.liquid_1 = false;
bflags.bits.liquid_2 = false;
bflags.bits.update_liquid = false;
bflags.bits.update_liquid_twice = false;
(*biter)->setBlockFlags(bflags);
}
else
{
out << "flow bit 1 = " << bflags.bits.liquid_1 << endl;
out << "flow bit 2 = " << bflags.bits.liquid_2 << endl;
out << "flow bit 1 = " << bflags.bits.update_liquid << endl;
out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl;
}
biter ++;
}

@ -0,0 +1,33 @@
local _ENV = mkmodule('plugins.burrows')
--[[
Native functions:
* findByName(name) -> burrow
* copyUnits(dest,src,enable)
* copyTiles(dest,src,enable)
* setTilesByKeyword(dest,kwd,enable) -> success
'enable' selects between add and remove modes
--]]
clearUnits = dfhack.units.clearBurrowMembers
function isBurrowUnit(burrow,unit)
return dfhack.units.isInBurrow(unit,burrow)
end
function setBurrowUnit(burrow,unit,enable)
return dfhack.units.setInBurrow(unit,burrow,enable)
end
clearTiles = dfhack.maps.clearBurrowTiles
listBlocks = dfhack.maps.listBurrowBlocks
isBurrowTile = dfhack.maps.isBurrowTile
setBurrowTile = dfhack.maps.setBurrowTile
isBlockBurrowTile = dfhack.maps.isBlockBurrowTile
setBlockBurrowTile = dfhack.maps.setBlockBurrowTile
return _ENV

@ -151,7 +151,7 @@ command_result mapexport (color_ostream &out, std::vector <std::string> & parame
// Get the map block
df::coord2d blockCoord(b_x, b_y);
MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z));
if (!b || !b->valid)
if (!b || !b->is_valid())
{
continue;
}
@ -161,15 +161,9 @@ command_result mapexport (color_ostream &out, std::vector <std::string> & parame
protoblock.set_y(b_y);
protoblock.set_z(z);
{ // Find features
uint32_t index = b->raw.global_feature;
if (index != -1)
Maps::GetGlobalFeature(blockFeatureGlobal, index);
index = b->raw.local_feature;
if (index != -1)
Maps::GetLocalFeature(blockFeatureLocal, blockCoord, index);
}
// Find features
b->GetGlobalFeature(&blockFeatureGlobal);
b->GetLocalFeature(&blockFeatureLocal);
int global_z = df::global::world->map.region_z + z;
@ -247,9 +241,9 @@ command_result mapexport (color_ostream &out, std::vector <std::string> & parame
}
}
PlantList *plants;
if (Maps::ReadVegetation(b_x, b_y, z, plants))
if (b->getRaw())
{
PlantList *plants = &b->getRaw()->plants;
for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++)
{
const df::plant & plant = *(*it);

@ -125,8 +125,9 @@ static command_result immolations (color_ostream &out, do_what what, bool shrubs
int32_t x,y,z;
if(Gui::getCursorCoords(x,y,z))
{
vector<df::plant *> * alltrees;
if(Maps::ReadVegetation(x/16,y/16,z,alltrees))
auto block = Maps::getTileBlock(x,y,z);
vector<df::plant *> *alltrees = block ? &block->plants : NULL;
if(alltrees)
{
bool didit = false;
for(size_t i = 0 ; i < alltrees->size(); i++)
@ -206,8 +207,9 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
int32_t x,y,z;
if(Gui::getCursorCoords(x,y,z))
{
vector<df::plant *> * alltrees;
if(Maps::ReadVegetation(x/16,y/16,z,alltrees))
auto block = Maps::getTileBlock(x,y,z);
vector<df::plant *> *alltrees = block ? &block->plants : NULL;
if(alltrees)
{
for(size_t i = 0 ; i < alltrees->size(); i++)
{

@ -15,6 +15,8 @@ using namespace std;
#include "Export.h"
#include "PluginManager.h"
#include "modules/Units.h"
#include "df/unit_inventory_item.h"
#include "df/building_nest_boxst.h"
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "modules/Materials.h"
@ -75,7 +77,26 @@ command_result df_cprobe (color_ostream &out, vector <string> & parameters)
if(unit->pos.x == cursorX && unit->pos.y == cursorY && unit->pos.z == cursorZ)
{
out.print("Creature %d, race %d (%x), civ %d (%x)\n", unit->id, unit->race, unit->race, unit->civ_id, unit->civ_id);
break;
for(size_t j=0; j<unit->inventory.size(); j++)
{
df::unit_inventory_item* inv_item = unit->inventory[j];
df::item* item = inv_item->item;
if(inv_item->mode == df::unit_inventory_item::T_mode::Worn)
{
out << " wears item: #" << item->id;
if(item->flags.bits.owned)
out << " (owned)";
else
out << " (not owned)";
if(item->getEffectiveArmorLevel() != 0)
out << ", armor";
out << endl;
}
}
// don't leave loop, there may be more than 1 creature at the cursor position
//break;
}
}
}
@ -127,13 +148,14 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
uint32_t tileY = cursorY % 16;
MapExtras::Block * b = mc.BlockAt(cursor/16);
if(!b && !b->valid)
if(!b || !b->is_valid())
{
out.printerr("No data.\n");
return CR_OK;
}
mapblock40d & block = b->raw;
out.print("block addr: 0x%x\n\n", block.origin);
auto &block = *b->getRaw();
out.print("block addr: 0x%x\n\n", &block);
/*
if (showBlock)
{
@ -250,7 +272,7 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
t_feature local;
t_feature global;
Maps::ReadFeatures(&(b->raw),&local,&global);
Maps::ReadFeatures(&block,&local,&global);
PRINT_FLAG( des, feature_local );
if(local.type != -1)
{
@ -273,7 +295,6 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
<< endl;
out << "global feature idx: " << block.global_feature
<< endl;
out << "mystery: " << block.mystery << endl;
out << std::endl;
return CR_OK;
}
@ -350,11 +371,21 @@ command_result df_bprobe (color_ostream &out, vector <string> & parameters)
ENUM_KEY_STR(trap_type, building.trap_type).c_str(),
building.trap_type);
break;
case building_type::NestBox:
{
df::building_nest_boxst* nestbox = (df::building_nest_boxst*) building.origin;
out.print(", claimed:(%i), items:%i", nestbox->claimed_by, nestbox->contained_items.size());
break;
}
default:
if (building.subtype != -1)
out.print(", subtype %i", building.subtype);
break;
}
if(building.origin->isRoom())
out << ", is room";
else
out << ", not a room";
out.print("\n");
}

@ -426,20 +426,14 @@ command_result prospector (color_ostream &con, vector <string> & parameters)
// Get the map block
df::coord2d blockCoord(b_x, b_y);
MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z));
if (!b || !b->valid)
if (!b || !b->is_valid())
{
continue;
}
{ // Find features
uint32_t index = b->raw.global_feature;
if (index != -1)
Maps::GetGlobalFeature(blockFeatureGlobal, index);
index = b->raw.local_feature;
if (index != -1)
Maps::GetLocalFeature(blockFeatureLocal, blockCoord, index);
}
// Find features
b->GetGlobalFeature(&blockFeatureGlobal);
b->GetLocalFeature(&blockFeatureLocal);
int global_z = world->map.region_z + z;
@ -550,8 +544,9 @@ command_result prospector (color_ostream &con, vector <string> & parameters)
// and we can check visibility more easily here
if (showPlants)
{
PlantList * plants;
if (Maps::ReadVegetation(b_x, b_y, z, plants))
auto block = Maps::getBlock(b_x,b_y,z);
vector<df::plant *> *plants = block ? &block->plants : NULL;
if(plants)
{
for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++)
{

@ -4,6 +4,8 @@
#include "PluginManager.h"
#include "modules/Gui.h"
#include "modules/Translation.h"
#include "modules/Units.h"
#include "DataDefs.h"
#include "df/ui.h"
@ -20,6 +22,8 @@
#include "RemoteServer.h"
#include "rename.pb.h"
#include "MiscUtils.h"
#include <stdlib.h>
using std::vector;
@ -57,19 +61,6 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
static void set_nickname(df::language_name *name, std::string nick)
{
if (!name->has_name)
{
*name = df::language_name();
name->language = 0;
name->has_name = true;
}
name->nickname = nick;
}
static df::squad *getSquadByIndex(unsigned idx)
{
auto entity = df::historical_entity::find(ui->group_id);
@ -82,45 +73,6 @@ static df::squad *getSquadByIndex(unsigned idx)
return df::squad::find(entity->squads[idx]);
}
void setUnitNickname(df::unit *unit, const std::string &nick)
{
// There are >=3 copies of the name, and the one
// in the unit is not the authoritative one.
// This is the reason why military units often
// lose nicknames set from Dwarf Therapist.
set_nickname(&unit->name, nick);
if (unit->status.current_soul)
set_nickname(&unit->status.current_soul->name, nick);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
set_nickname(&figure->name, nick);
// v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation)
{
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity);
if (identity)
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
if (id_hfig)
{
// Even DF doesn't do this bit, because it's apparently
// only used for demons masquerading as gods, so you
// can't ever change their nickname in-game.
set_nickname(&id_hfig->name, nick);
}
else
set_nickname(&identity->name, nick);
}
}
}
}
static command_result RenameSquad(color_ostream &stream, const RenameSquadIn *in)
{
df::squad *squad = df::squad::find(in->squad_id());
@ -128,9 +80,9 @@ static command_result RenameSquad(color_ostream &stream, const RenameSquadIn *in
return CR_NOT_FOUND;
if (in->has_nickname())
set_nickname(&squad->name, in->nickname());
Translation::setNickname(&squad->name, UTF2DF(in->nickname()));
if (in->has_alias())
squad->alias = in->alias();
squad->alias = UTF2DF(in->alias());
return CR_OK;
}
@ -142,9 +94,9 @@ static command_result RenameUnit(color_ostream &stream, const RenameUnitIn *in)
return CR_NOT_FOUND;
if (in->has_nickname())
setUnitNickname(unit, in->nickname());
Units::setNickname(unit, UTF2DF(in->nickname()));
if (in->has_profession())
unit->custom_profession = in->profession();
unit->custom_profession = UTF2DF(in->profession());
return CR_OK;
}
@ -202,7 +154,7 @@ static command_result rename(color_ostream &out, vector <string> &parameters)
if (!unit)
return CR_WRONG_USAGE;
setUnitNickname(unit, parameters[1]);
Units::setNickname(unit, parameters[1]);
}
else if (cmd == "unit-profession")
{

@ -290,7 +290,7 @@ command_result unreveal(color_ostream &out, vector<string> & params)
for(size_t i = 0; i < hidesaved.size();i++)
{
hideblock & hb = hidesaved[i];
df::map_block * b = Maps::getBlockAbs(hb.c.x,hb.c.y,hb.c.z);
df::map_block * b = Maps::getTileBlock(hb.c.x,hb.c.y,hb.c.z);
for (uint32_t x = 0; x < 16;x++) for (uint32_t y = 0; y < 16;y++)
{
b->designation[x][y].bits.hidden = hb.hiddens[x][y];

@ -1 +1 @@
Subproject commit d1c87ec273ef5c928989437379943a9a71a1d4e0
Subproject commit cddc64777b8368e15c1a820ccd1cf56e11267e52

File diff suppressed because it is too large Load Diff

@ -59,12 +59,9 @@ command_result tubefill(color_ostream &out, std::vector<std::string> & params)
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
{
df::map_block *block = world->map.map_blocks[i];
df::map_block *above = Maps::getBlockAbs(block->map_pos.x, block->map_pos.y, block->map_pos.z + 1);
if (block->local_feature == -1)
continue;
df::map_block *above = Maps::getTileBlock(block->map_pos + df::coord(0,0,1));
DFHack::t_feature feature;
DFCoord coord(block->map_pos.x >> 4, block->map_pos.y >> 4, block->map_pos.z);
if (!Maps::GetLocalFeature(feature, coord, block->local_feature))
if (!Maps::ReadFeatures(block, &feature, NULL))
continue;
if (feature.type != feature_type::deep_special_tube)
continue;

@ -7,6 +7,10 @@
#include "PluginManager.h"
#include "modules/Gui.h"
#include "modules/Units.h"
#include "modules/Items.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/ui.h"
@ -21,6 +25,7 @@
#include "df/language_name.h"
#include "df/death_info.h"
#include "df/criminal_case.h"
#include "df/unit_inventory_item.h"
#include <stdlib.h>
@ -50,6 +55,11 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" Intended to fix the case where you can't engrave memorials for ghosts.\n"
" Note that this is very dirty and possibly dangerous!\n"
" Most probably does not have the positive effect of a proper burial.\n"
" tweak fixmigrant\n"
" Forces the selected unit to become a member or your fortress.\n"
" Intended to fix bugged migrants and merchants who stay at the map edge.\n"
" Only works for units of your own race. Can be used for stealing caravan\n"
" traders and guards, but might result into weirdness during trading.\n"
));
return CR_OK;
}
@ -61,6 +71,64 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out)
static command_result lair(color_ostream &out, std::vector<std::string> & params);
// to be called by tweak-fixmigrant
// units forced into the fort by removing the flags do not own their clothes
// which has the result that they drop all their clothes and become unhappy because they are naked
// so we need to make them own their clothes and add them to their uniform
command_result fix_clothing_ownership(color_ostream &out, df::unit* unit)
{
// first, find one owned item to initialize the vtable
bool vt_initialized = false;
size_t numItems = world->items.all.size();
for(size_t i=0; i< numItems; i++)
{
df::item * item = world->items.all[i];
if(Items::getOwner(item))
{
vt_initialized = true;
break;
}
}
if(!vt_initialized)
{
out << "fix_clothing_ownership: could not initialize vtable!" << endl;
return CR_FAILURE;
}
int fixcount = 0;
for(size_t j=0; j<unit->inventory.size(); j++)
{
df::unit_inventory_item* inv_item = unit->inventory[j];
df::item* item = inv_item->item;
// unforbid items (for the case of kidnapping caravan escorts who have their stuff forbidden by default)
inv_item->item->flags.bits.forbid = 0;
if(inv_item->mode == df::unit_inventory_item::T_mode::Worn)
{
// ignore armor?
// it could be leather boots, for example, in which case it would not be nice to forbid ownership
//if(item->getEffectiveArmorLevel() != 0)
// continue;
if(!Items::getOwner(item))
{
if(Items::setOwner(item, unit))
{
// add to uniform, so they know they should wear their clothes
insert_into_vector(unit->military.uniforms[0], item->id);
fixcount++;
}
else
out << "could not change ownership for item!" << endl;
}
}
}
// clear uniform_drop (without this they would drop their clothes and pick them up some time later)
unit->military.uniform_drop.clear();
out << "ownership for " << fixcount << " clothes fixed" << endl;
return CR_OK;
}
static command_result tweak(color_ostream &out, vector <string> &parameters)
{
CoreSuspender suspend;
@ -106,7 +174,67 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
return CR_FAILURE;
}
}
else return CR_WRONG_USAGE;
else if (cmd == "fixmigrant")
{
df::unit *unit = getSelectedUnit(out);
if (!unit)
{
out << "No unit selected!" << endl;
return CR_FAILURE;
}
if(unit->race != df::global::ui->race_id)
{
out << "Selected unit does not belong to your race!" << endl;
return CR_FAILURE;
}
// case #1: migrants who have the resident flag set
// see http://dffd.wimbli.com/file.php?id=6139 for a save
if (unit->flags2.bits.resident)
unit->flags2.bits.resident = 0;
// case #2: migrants who have the merchant flag
// happens on almost all maps after a few migrant waves
if(unit->flags1.bits.merchant)
unit->flags1.bits.merchant = 0;
// this one is a cheat, but bugged migrants usually have the same civ_id
// so it should not be triggered in most cases
// if it happens that the player has 'foreign' units of the same race
// (vanilla df: dwarves not from mountainhome) on his map, just grab them
if(unit->civ_id != df::global::ui->civ_id)
unit->civ_id = df::global::ui->civ_id;
return fix_clothing_ownership(out, unit);
}
else if (cmd == "makeown")
{
// force a unit into your fort, regardless of civ or race
// allows to "steal" caravan guards etc
df::unit *unit = getSelectedUnit(out);
if (!unit)
{
out << "No unit selected!" << endl;
return CR_FAILURE;
}
if (unit->flags2.bits.resident)
unit->flags2.bits.resident = 0;
if(unit->flags1.bits.merchant)
unit->flags1.bits.merchant = 0;
if(unit->flags1.bits.forest)
unit->flags1.bits.forest = 0;
if(unit->civ_id != df::global::ui->civ_id)
unit->civ_id = df::global::ui->civ_id;
if(unit->profession == df::profession::MERCHANT)
unit->profession = df::profession::TRADER;
if(unit->profession2 == df::profession::MERCHANT)
unit->profession2 = df::profession::TRADER;
return fix_clothing_ownership(out, unit);
}
else
return CR_WRONG_USAGE;
return CR_OK;
}

@ -184,9 +184,9 @@ public:
ProtectedJob(df::job *job) : id(job->id)
{
tick_idx = cur_tick_idx;
holder = getJobHolder(job);
holder = Job::getHolder(job);
building_id = holder ? holder->id : -1;
job_copy = cloneJobStruct(job);
job_copy = Job::cloneJobStruct(job);
actual_job = job;
reaction_id = -1;
@ -196,7 +196,7 @@ public:
~ProtectedJob()
{
deleteJobStruct(job_copy);
Job::deleteJobStruct(job_copy);
}
bool isActuallyResumed() {
@ -214,8 +214,8 @@ public:
return;
reaction_id = -1;
deleteJobStruct(job_copy);
job_copy = cloneJobStruct(job);
Job::deleteJobStruct(job_copy);
job_copy = Job::cloneJobStruct(job);
}
void tick_job(df::job *job, int ticks)
@ -365,7 +365,7 @@ static ProtectedJob *get_known(int id)
static bool isSupportedJob(df::job *job)
{
return job->misc_links.empty() &&
getJobHolder(job) &&
Job::getHolder(job) &&
(!job->job_items.empty() ||
job->job_type == job_type::CollectClay ||
job->job_type == job_type::CollectSand);
@ -526,11 +526,11 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
}
// Create and link in the actual job structure
df::job *recovered = cloneJobStruct(pj->job_copy);
df::job *recovered = Job::cloneJobStruct(pj->job_copy);
if (!linkJobIntoWorld(recovered, false)) // reuse same id
if (!Job::linkIntoWorld(recovered, false)) // reuse same id
{
deleteJobStruct(recovered);
Job::deleteJobStruct(recovered);
out.printerr("Inconsistency: job %d (%s) already in list.\n",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
@ -808,7 +808,7 @@ static void compute_custom_job(ProtectedJob *pj, df::job *job)
using namespace df::enums::reaction_product_item_flags;
VIRTUAL_CAST_VAR(prod, df::reaction_product_itemst, r->products[i]);
if (!prod || prod->item_type < 0)
if (!prod || (prod->item_type < 0 && !prod->flags.is_set(CRAFTS)))
continue;
MaterialInfo mat(prod);
@ -854,7 +854,7 @@ static void compute_custom_job(ProtectedJob *pj, df::job *job)
}
link_job_constraint(pj, prod->item_type, prod->item_subtype,
mat_mask, mat.type, mat.index);
mat_mask, mat.type, mat.index, prod->flags.is_set(CRAFTS));
}
}
@ -1128,6 +1128,10 @@ static void map_job_items(color_ostream &out)
bool is_invalid = false;
// don't count worn items
if (item->getWear() >= 1)
is_invalid = true;
// Special handling
switch (itype) {
case item_type::BUCKET:
@ -1431,7 +1435,7 @@ static void print_job(color_ostream &out, ProtectedJob *pj)
df::job *job = pj->isLive() ? pj->actual_job : pj->job_copy;
printJobDetails(out, job);
Job::printJobDetails(out, job);
if (job->job_type == job_type::MeltMetalObject &&
isOptionEnabled(CF_AUTOMELT))
@ -1556,7 +1560,7 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
pending = true;
}
printJobDetails(out, pending_recover[i]->job_copy);
Job::printJobDetails(out, pending_recover[i]->job_copy);
}
}

File diff suppressed because it is too large Load Diff