Merge remote-tracking branch 'upstream/master'

develop
Kelly Martin 2012-05-12 19:43:02 -05:00
commit bd2b55c490
77 changed files with 5093 additions and 712 deletions

@ -108,7 +108,7 @@ OPTION(BUILD_PLUGINS "Build the plugins." ON)
# enable C++11 features
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable")
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x")
SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic")
ENDIF()

@ -594,41 +594,52 @@ C++ function wrappers
=====================
Thin wrappers around C++ functions, similar to the ones for virtual methods.
One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments
can be omitted.
* ``dfhack.TranslateName(name,in_english,only_last_name)``
* ``dfhack.isWorldLoaded()``
Checks if the world is loaded.
* ``dfhack.isMapLoaded()``
Checks if the world and map are loaded.
* ``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)``
* ``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)``
* ``dfhack.gui.getSelectedJob([silent])``
Returns the job selected in a workshop or unit/jobs screen.
* ``dfhack.gui.getSelectedUnit(silent)``
* ``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)``
* ``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)``
* ``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)``
* ``dfhack.gui.showPopupAnnouncement(text,color[,is_bright])``
Pops up a titan-style modal announcement window.
@ -688,6 +699,10 @@ Units module
Returns the language_name object visible in game, accounting for false identities.
* ``dfhack.units.getIdentity(unit)``
Returns the false identity of the unit if it has one, or *nil*.
* ``dfhack.units.getNemesis(unit)``
Returns the nemesis record of the unit if it has one, or *nil*.
@ -704,17 +719,24 @@ Units module
The unit is capable of rational action, i.e. not dead, insane or zombie.
* ``dfhack.units.clearBurrowMembers(burrow)``
* ``dfhack.units.getAge(unit[,true_age])``
Removes all units from the burrow.
Returns the age of the unit in years as a floating-point value.
If ``true_age`` is true, ignores false identities.
* ``dfhack.units.isInBurrow(unit,burrow)``
* ``dfhack.units.getNoblePositions(unit)``
Checks if the unit is in the burrow.
Returns a list of tables describing noble position assignments, or *nil*.
Every table has fields ``entity``, ``assignment`` and ``position``.
* ``dfhack.units.setInBurrow(unit,burrow,enable)``
* ``dfhack.units.getProfessionName(unit[,ignore_noble,plural])``
Adds or removes the unit from the burrow.
Retrieves the profession name using custom profession, noble assignments
or raws. The ``ignore_noble`` boolean disables the use of noble positions.
* ``dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])``
Retrieves the profession name for the given race/caste using raws.
Items module
@ -724,6 +746,14 @@ Items module
Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory.
* ``dfhack.items.getGeneralRef(item, type)``
Searches for a general_ref with the given type.
* ``dfhack.items.getSpecificRef(item, type)``
Searches for a specific_ref with the given type.
* ``dfhack.items.getOwner(item)``
Returns the owner unit or *nil*.
@ -765,9 +795,9 @@ Maps module
Returns a map block object for given x,y,z in local block coordinates.
* ``dfhack.maps.getTileBlock(coords)``
* ``dfhack.maps.getTileBlock(coords)``, or ``getTileBlock(x,y,z)``
Returns a map block object for given df::coord in local tile coordinates.
Returns a map block object for given df::coord or x,y,z in local tile coordinates.
* ``dfhack.maps.getRegionBiome(region_coord2d)``
@ -781,35 +811,167 @@ Maps module
Returns the local feature object with the given region coords and index.
* ``dfhack.maps.findBurrowByName(name)``
* ``dfhack.maps.canWalkBetween(pos1, pos2)``
Checks if a dwarf may be able to walk between the two tiles,
using a pathfinding cache maintained by the game. Note that
this cache is only updated when the game is unpaused, and thus
can get out of date if doors are forbidden or unforbidden, or
tools like liquids or tiletypes are used. It also cannot possibly
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.
Burrows module
--------------
* ``dfhack.burrows.findByName(name)``
Returns the burrow pointer or *nil*.
* ``dfhack.maps.listBurrowBlocks(burrow)``
* ``dfhack.burrows.clearUnits(burrow)``
Returns a table of map block pointers.
Removes all units from the burrow.
* ``dfhack.burrows.isAssignedUnit(burrow,unit)``
Checks if the unit is in the burrow.
* ``dfhack.maps.clearBurrowTiles(burrow)``
* ``dfhack.burrows.setAssignedUnit(burrow,unit,enable)``
Adds or removes the unit from the burrow.
* ``dfhack.burrows.clearTiles(burrow)``
Removes all tiles from the burrow.
* ``dfhack.maps.isBurrowTile(burrow,tile_coord)``
* ``dfhack.burrows.listBlocks(burrow)``
Returns a table of map block pointers.
* ``dfhack.burrows.isAssignedTile(burrow,tile_coord)``
Checks if the tile is in burrow.
* ``dfhack.maps.setBurrowTile(burrow,tile_coord,enable)``
* ``dfhack.burrows.setAssignedTile(burrow,tile_coord,enable)``
Adds or removes the tile from the burrow. Returns *false* if invalid coords.
* ``dfhack.maps.isBlockBurrowTile(burrow,block,x,y)``
* ``dfhack.burrows.isAssignedBlockTile(burrow,block,x,y)``
Checks if the tile within the block is in burrow.
* ``dfhack.maps.setBlockBurrowTile(burrow,block,x,y,enable)``
* ``dfhack.burrows.setAssignedBlockTile(burrow,block,x,y,enable)``
Adds or removes the tile from the burrow. Returns *false* if invalid coords.
Buildings module
----------------
* ``dfhack.buildings.getSize(building)``
Returns *width, height, centerx, centery*.
* ``dfhack.buildings.findAtTile(pos)``, or ``findAtTile(x,y,z)``
Scans the buildings for the one located at the given tile.
Does not work on civzones. Warning: linear scan if the map
tile indicates there are buildings at it.
* ``dfhack.buildings.findCivzonesAt(pos)``, or ``findCivzonesAt(x,y,z)``
Scans civzones, and returns a lua sequence of those that touch
the given tile, or *nil* if none.
* ``dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)``
Computes correct dimensions for the specified building type and orientation,
using width and height for flexible dimensions.
Returns *is_flexible, width, height, center_x, center_y*.
* ``dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])``
Checks if the rectangle defined by ``pos`` and ``size``, and possibly extents,
can be used for placing a building. If ``change_extents`` is true, bad tiles
are removed from extents. If ``allow_occupied``, the occupancy test is skipped.
* ``dfhack.buildings.countExtentTiles(extents,defval)``
Returns the number of tiles included by extents, or defval.
* ``dfhack.buildings.containsTile(building, x, y[, room])``
Checks if the building contains the specified tile, either directly, or as room.
* ``dfhack.buildings.hasSupport(pos,size)``
Checks if a bridge constructed at specified position would have
support from terrain, and thus won't collapse if retracted.
Low-level building creation functions;
* ``dfhack.buildings.allocInstance(pos, type, subtype, custom)``
Creates a new building instance of given type, subtype and custom type,
at specified position. Returns the object, or *nil* in case of an error.
* ``dfhack.buildings.setSize(building, width, height, direction)``
Configures an object returned by ``allocInstance``, using specified
parameters wherever appropriate. If the building has fixed size along
any dimension, the corresponding input parameter will be ignored.
Returns *false* if the building cannot be placed, or *true, width,
height, rect_area, true_area*. Returned width and height are the
final values used by the building; true_area is less than rect_area
if any tiles were removed from designation.
* ``dfhack.buildings.constructAbstract(building)``
Links a fully configured object created by ``allocInstance`` into the
world. The object must be an abstract building, i.e. a stockpile or civzone.
Returns *true*, or *false* if impossible.
* ``dfhack.buildings.constructWithItems(building, items)``
Links a fully configured object created by ``allocInstance`` into the
world for construction, using a list of specific items as material.
Returns *true*, or *false* if impossible.
* ``dfhack.buildings.constructWithFilters(building, job_items)``
Links a fully configured object created by ``allocInstance`` into the
world for construction, using a list of job_item filters as inputs.
Returns *true*, or *false* if impossible. Filter objects are claimed
and possibly destroyed in any case.
Use a negative ``quantity`` field value to auto-compute the amount
from the size of the building.
* ``dfhack.buildings.deconstruct(building)``
Destroys the building, or queues a deconstruction job.
Returns *true* if the building was destroyed and deallocated immediately.
More high-level functions are implemented in lua and can be loaded by
``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``.
Constructions module
--------------------
* ``dfhack.constructions.designateNew(pos,type,item_type,mat_index)``
Designates a new construction at given position. If there already is
a planned but not completed construction there, changes its type.
Returns *true*, or *false* if obstructed.
Note that designated constructions are technically buildings.
* ``dfhack.constructions.designateRemove(pos)``, or ``designateRemove(x,y,z)``
If there is a construction or a planned construction at the specified
coordinates, designates it for removal, or instantly cancels the planned one.
Returns *true, was_only_planned* if removed; or *false* if none found.
Core interpreter context
========================
@ -823,6 +985,18 @@ Core context specific functions:
Boolean value; *true* in the core context.
* ``dfhack.timeout(time,mode,callback)``
Arranges for the callback to be called once the specified
period of time passes. The ``mode`` argument specifies the
unit of time used, and may be one of ``'frames'`` (raw FPS),
``'ticks'`` (unpaused FPS), ``'days'``, ``'months'``,
``'years'`` (in-game time). All timers other than
``'frames'`` are cancelled when the world is unloaded,
and cannot be queued until it is loaded again.
Returns the timer id, or *nil* if unsuccessful due to
world being unloaded.
* ``dfhack.onStateChange.foo = function(code)``
Event. Receives the same codes as plugin_onstatechange in C++.
@ -856,3 +1030,65 @@ Features:
Invokes all listeners contained in the event in an arbitrary
order using ``dfhack.safecall``.
=======
Plugins
=======
DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
``mkmodule('plugins.<name>')``; this means that a lua
module file is still necessary for ``require`` to read.
The following plugins have lua support.
burrows
=======
Implements extended burrow manipulations.
Events:
* ``onBurrowRename.foo = function(burrow)``
Emitted when a burrow might have been renamed either through
the game UI, or ``renameBurrow()``.
* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)``
Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled.
Native functions:
* ``renameBurrow(burrow,name)``
Renames the burrow, emitting ``onBurrowRename`` and updating auto-grow state properly.
* ``findByName(burrow,name)``
Finds a burrow by name, using the same rules as the plugin command line interface.
Namely, trailing ``'+'`` characters marking auto-grow burrows are ignored.
* ``copyUnits(target,source,enable)``
Applies units from ``source`` burrow to ``target``. The ``enable``
parameter specifies if they are to be added or removed.
* ``copyTiles(target,source,enable)``
Applies tiles from ``source`` burrow to ``target``. The ``enable``
parameter specifies if they are to be added or removed.
* ``setTilesByKeyword(target,keyword,enable)``
Adds or removes tiles matching a predefined keyword. The keyword
set is the same as used by the command line.
The lua module file also re-exports functions from ``dfhack.burrows``.
sort
====
Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.

@ -342,14 +342,22 @@ ul.auto-toc {
<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>
<li><a class="reference internal" href="#burrows-module" id="id19">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id20">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id21">Constructions module</a></li>
</ul>
</li>
<li><a class="reference internal" href="#core-interpreter-context" id="id19">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id20">Event type</a></li>
<li><a class="reference internal" href="#core-interpreter-context" id="id22">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id23">Event type</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#plugins" id="id24">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id25">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id26">sort</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="df-structure-wrapper">
@ -842,37 +850,46 @@ Accept dfhack_material_category auto-assign table.</p>
</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>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.
One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments
can be omitted.</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.TranslateName(name,in_english,only_last_name)</tt></p>
<li><p class="first"><tt class="docutils literal">dfhack.isWorldLoaded()</tt></p>
<p>Checks if the world is loaded.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.isMapLoaded()</tt></p>
<p>Checks if the world and map are loaded.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.TranslateName(name[,in_english,only_last_name])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedWorkshopJob([silent])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedJob([silent])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedUnit([silent])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedItem([silent])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.showAnnouncement(text,color[,is_bright])</span></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>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.showPopupAnnouncement(text,color[,is_bright])</span></tt></p>
<p>Pops up a titan-style modal announcement window.</p>
</li>
</ul>
@ -923,6 +940,9 @@ a lua list containing them.</p>
<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.getIdentity(unit)</tt></p>
<p>Returns the false identity of the unit if it has one, or <em>nil</em>.</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>
@ -935,14 +955,20 @@ a lua list containing them.</p>
<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><p class="first"><tt class="docutils literal"><span class="pre">dfhack.units.getAge(unit[,true_age])</span></tt></p>
<p>Returns the age of the unit in years as a floating-point value.
If <tt class="docutils literal">true_age</tt> is true, ignores false identities.</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><p class="first"><tt class="docutils literal">dfhack.units.getNoblePositions(unit)</tt></p>
<p>Returns a list of tables describing noble position assignments, or <em>nil</em>.
Every table has fields <tt class="docutils literal">entity</tt>, <tt class="docutils literal">assignment</tt> and <tt class="docutils literal">position</tt>.</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><p class="first"><tt class="docutils literal"><span class="pre">dfhack.units.getProfessionName(unit[,ignore_noble,plural])</span></tt></p>
<p>Retrieves the profession name using custom profession, noble assignments
or raws. The <tt class="docutils literal">ignore_noble</tt> boolean disables the use of noble positions.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])</span></tt></p>
<p>Retrieves the profession name for the given race/caste using raws.</p>
</li>
</ul>
</div>
@ -952,6 +978,12 @@ a lua list containing them.</p>
<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.getGeneralRef(item, type)</tt></p>
<p>Searches for a general_ref with the given type.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getSpecificRef(item, type)</tt></p>
<p>Searches for a specific_ref with the given type.</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>
@ -985,8 +1017,8 @@ Returns <em>false</em> in case of error.</p>
<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><p class="first"><tt class="docutils literal">dfhack.maps.getTileBlock(coords)</tt>, or <tt class="docutils literal">getTileBlock(x,y,z)</tt></p>
<p>Returns a map block object for given df::coord or x,y,z 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>
@ -997,32 +1029,148 @@ Returns <em>false</em> in case of error.</p>
<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>
<li><p class="first"><tt class="docutils literal">dfhack.maps.canWalkBetween(pos1, pos2)</tt></p>
<p>Checks if a dwarf may be able to walk between the two tiles,
using a pathfinding cache maintained by the game. Note that
this cache is only updated when the game is unpaused, and thus
can get out of date if doors are forbidden or unforbidden, or
tools like liquids or tiletypes are used. It also cannot possibly
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.</p>
</li>
</ul>
</div>
<div class="section" id="burrows-module">
<h3><a class="toc-backref" href="#id19">Burrows module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.findByName(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><p class="first"><tt class="docutils literal">dfhack.burrows.clearUnits(burrow)</tt></p>
<p>Removes all units from the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.isAssignedUnit(burrow,unit)</tt></p>
<p>Checks if the unit is in the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.clearBurrowTiles(burrow)</tt></p>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.setAssignedUnit(burrow,unit,enable)</tt></p>
<p>Adds or removes the unit from the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.clearTiles(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>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.listBlocks(burrow)</tt></p>
<p>Returns a table of map block pointers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.isAssignedTile(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>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.setAssignedTile(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>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.isAssignedBlockTile(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>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.setAssignedBlockTile(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 class="section" id="buildings-module">
<h3><a class="toc-backref" href="#id20">Buildings module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getSize(building)</tt></p>
<p>Returns <em>width, height, centerx, centery</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.findAtTile(pos)</tt>, or <tt class="docutils literal">findAtTile(x,y,z)</tt></p>
<p>Scans the buildings for the one located at the given tile.
Does not work on civzones. Warning: linear scan if the map
tile indicates there are buildings at it.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.findCivzonesAt(pos)</tt>, or <tt class="docutils literal">findCivzonesAt(x,y,z)</tt></p>
<p>Scans civzones, and returns a lua sequence of those that touch
the given tile, or <em>nil</em> if none.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)</tt></p>
<p>Computes correct dimensions for the specified building type and orientation,
using width and height for flexible dimensions.
Returns <em>is_flexible, width, height, center_x, center_y</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])</span></tt></p>
<p>Checks if the rectangle defined by <tt class="docutils literal">pos</tt> and <tt class="docutils literal">size</tt>, and possibly extents,
can be used for placing a building. If <tt class="docutils literal">change_extents</tt> is true, bad tiles
are removed from extents. If <tt class="docutils literal">allow_occupied</tt>, the occupancy test is skipped.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.countExtentTiles(extents,defval)</tt></p>
<p>Returns the number of tiles included by extents, or defval.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.containsTile(building, x, y[, room])</tt></p>
<p>Checks if the building contains the specified tile, either directly, or as room.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.hasSupport(pos,size)</tt></p>
<p>Checks if a bridge constructed at specified position would have
support from terrain, and thus won't collapse if retracted.</p>
</li>
</ul>
<p>Low-level building creation functions;</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.allocInstance(pos, type, subtype, custom)</tt></p>
<p>Creates a new building instance of given type, subtype and custom type,
at specified position. Returns the object, or <em>nil</em> in case of an error.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.setSize(building, width, height, direction)</tt></p>
<p>Configures an object returned by <tt class="docutils literal">allocInstance</tt>, using specified
parameters wherever appropriate. If the building has fixed size along
any dimension, the corresponding input parameter will be ignored.
Returns <em>false</em> if the building cannot be placed, or <em>true, width,
height, rect_area, true_area</em>. Returned width and height are the
final values used by the building; true_area is less than rect_area
if any tiles were removed from designation.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructAbstract(building)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world. The object must be an abstract building, i.e. a stockpile or civzone.
Returns <em>true</em>, or <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithItems(building, items)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world for construction, using a list of specific items as material.
Returns <em>true</em>, or <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithFilters(building, job_items)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world for construction, using a list of job_item filters as inputs.
Returns <em>true</em>, or <em>false</em> if impossible. Filter objects are claimed
and possibly destroyed in any case.
Use a negative <tt class="docutils literal">quantity</tt> field value to auto-compute the amount
from the size of the building.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.deconstruct(building)</tt></p>
<p>Destroys the building, or queues a deconstruction job.
Returns <em>true</em> if the building was destroyed and deallocated immediately.</p>
</li>
</ul>
<p>More high-level functions are implemented in lua and can be loaded by
<tt class="docutils literal"><span class="pre">require('dfhack.buildings')</span></tt>. See <tt class="docutils literal">hack/lua/dfhack/buildings.lua</tt>.</p>
</div>
<div class="section" id="constructions-module">
<h3><a class="toc-backref" href="#id21">Constructions module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateNew(pos,type,item_type,mat_index)</tt></p>
<p>Designates a new construction at given position. If there already is
a planned but not completed construction there, changes its type.
Returns <em>true</em>, or <em>false</em> if obstructed.
Note that designated constructions are technically buildings.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateRemove(pos)</tt>, or <tt class="docutils literal">designateRemove(x,y,z)</tt></p>
<p>If there is a construction or a planned construction at the specified
coordinates, designates it for removal, or instantly cancels the planned one.
Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none found.</p>
</li>
</ul>
</div>
</div>
<div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id19">Core interpreter context</a></h2>
<h2><a class="toc-backref" href="#id22">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>
@ -1031,12 +1179,23 @@ only context that can receive events from DF and plugins.</p>
<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>
<li><p class="first"><tt class="docutils literal">dfhack.timeout(time,mode,callback)</tt></p>
<p>Arranges for the callback to be called once the specified
period of time passes. The <tt class="docutils literal">mode</tt> argument specifies the
unit of time used, and may be one of <tt class="docutils literal">'frames'</tt> (raw FPS),
<tt class="docutils literal">'ticks'</tt> (unpaused FPS), <tt class="docutils literal">'days'</tt>, <tt class="docutils literal">'months'</tt>,
<tt class="docutils literal">'years'</tt> (in-game time). All timers other than
<tt class="docutils literal">'frames'</tt> are cancelled when the world is unloaded,
and cannot be queued until it is loaded again.
Returns the timer id, or <em>nil</em> if unsuccessful due to
world being unloaded.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.onStateChange.foo = function(code)</tt></p>
<p>Event. Receives the same codes as plugin_onstatechange in C++.</p>
</li>
</ul>
<div class="section" id="event-type">
<h3><a class="toc-backref" href="#id20">Event type</a></h3>
<h3><a class="toc-backref" href="#id23">Event type</a></h3>
<p>An event is just a lua table with a predefined metatable that
contains a __call metamethod. When it is invoked, it loops
through the table with next and calls all contained values.
@ -1061,6 +1220,57 @@ order using <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div>
</div>
</div>
<div class="section" id="plugins">
<h1><a class="toc-backref" href="#id24">Plugins</a></h1>
<p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p>
<div class="section" id="burrows">
<h2><a class="toc-backref" href="#id25">burrows</a></h2>
<p>Implements extended burrow manipulations.</p>
<p>Events:</p>
<ul>
<li><p class="first"><tt class="docutils literal">onBurrowRename.foo = function(burrow)</tt></p>
<p>Emitted when a burrow might have been renamed either through
the game UI, or <tt class="docutils literal">renameBurrow()</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)</tt></p>
<p>Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled.</p>
</li>
</ul>
<p>Native functions:</p>
<ul>
<li><p class="first"><tt class="docutils literal">renameBurrow(burrow,name)</tt></p>
<p>Renames the burrow, emitting <tt class="docutils literal">onBurrowRename</tt> and updating auto-grow state properly.</p>
</li>
<li><p class="first"><tt class="docutils literal">findByName(burrow,name)</tt></p>
<p>Finds a burrow by name, using the same rules as the plugin command line interface.
Namely, trailing <tt class="docutils literal">'+'</tt> characters marking auto-grow burrows are ignored.</p>
</li>
<li><p class="first"><tt class="docutils literal">copyUnits(target,source,enable)</tt></p>
<p>Applies units from <tt class="docutils literal">source</tt> burrow to <tt class="docutils literal">target</tt>. The <tt class="docutils literal">enable</tt>
parameter specifies if they are to be added or removed.</p>
</li>
<li><p class="first"><tt class="docutils literal">copyTiles(target,source,enable)</tt></p>
<p>Applies tiles from <tt class="docutils literal">source</tt> burrow to <tt class="docutils literal">target</tt>. The <tt class="docutils literal">enable</tt>
parameter specifies if they are to be added or removed.</p>
</li>
<li><p class="first"><tt class="docutils literal">setTilesByKeyword(target,keyword,enable)</tt></p>
<p>Adds or removes tiles matching a predefined keyword. The keyword
set is the same as used by the command line.</p>
</li>
</ul>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div>
<div class="section" id="sort">
<h2><a class="toc-backref" href="#id26">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p>
</div>
</div>
</div>
</body>
</html>

@ -57,7 +57,7 @@ IF(CMAKE_COMPILER_IS_GNUCC)
ENDIF()
IF (HAVE_HASH_MAP EQUAL 0)
MESSAGE(SEND_ERROR "Could not find a working hash map implementation. Please update GCC.")
MESSAGE(SEND_ERROR "Could not find a working hash map implementation. Please install GCC >= 4.4, and all necessary 32-bit C++ development libraries.")
ENDIF()
FIND_PACKAGE(Threads)
@ -200,6 +200,8 @@ google/protobuf/compiler/zip_writer.cc
LIST(APPEND LIBPROTOBUF_FULL_SRCS ${LIBPROTOBUF_LITE_SRCS})
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Wno-sign-compare")
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
SET(PROTOBUF_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})

@ -64,6 +64,7 @@ DataStatics.cpp
DataStaticsCtor.cpp
DataStaticsFields.cpp
MiscUtils.cpp
Types.cpp
PluginManager.cpp
TileTypes.cpp
VersionInfoFactory.cpp
@ -100,6 +101,7 @@ Process-linux.cpp
SET(MODULE_HEADERS
include/modules/Buildings.h
include/modules/Burrows.h
include/modules/Constructions.h
include/modules/Units.h
include/modules/Engravings.h
@ -120,6 +122,7 @@ include/modules/Graphic.h
SET( MODULE_SOURCES
modules/Buildings.cpp
modules/Burrows.cpp
modules/Constructions.cpp
modules/Units.cpp
modules/Engravings.cpp
@ -298,6 +301,9 @@ install(DIRECTORY lua/
DESTINATION ${DFHACK_LUA_DESTINATION}
FILES_MATCHING PATTERN "*.lua")
install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts
DESTINATION ${DFHACK_DATA_DESTINATION})
# Unused for so long that it's not even relevant now...
if(BUILD_DEVEL)
if(WIN32)

@ -163,6 +163,7 @@ namespace DFHack
return false;
return true;
}
return false;
}
public:
@ -489,7 +490,7 @@ namespace DFHack
{
right_arrow:
/* right arrow */
if (raw_cursor != raw_buffer.size())
if (size_t(raw_cursor) != raw_buffer.size())
{
raw_cursor++;
prompt_refresh();
@ -510,7 +511,7 @@ namespace DFHack
history_index = 0;
break;
}
else if (history_index >= history.size())
else if (size_t(history_index) >= history.size())
{
history_index = history.size()-1;
break;
@ -545,7 +546,7 @@ namespace DFHack
if (seq[1] == '3' && seq2 == '~' )
{
// delete
if (raw_buffer.size() > 0 && raw_cursor < raw_buffer.size())
if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size())
{
raw_buffer.erase(raw_cursor,1);
prompt_refresh();
@ -555,11 +556,11 @@ namespace DFHack
}
break;
default:
if (raw_buffer.size() == raw_cursor)
if (raw_buffer.size() == size_t(raw_cursor))
{
raw_buffer.append(1,c);
raw_cursor++;
if (plen+raw_buffer.size() < get_columns())
if (plen+raw_buffer.size() < size_t(get_columns()))
{
/* Avoid a full update of the line in the
* trivial case. */

@ -51,6 +51,8 @@ using namespace std;
#include "RemoteServer.h"
#include "LuaTools.h"
#include "MiscUtils.h"
using namespace DFHack;
#include "df/ui.h"
@ -72,8 +74,6 @@ using df::global::init;
// FIXME: A lot of code in one file, all doing different things... there's something fishy about it.
static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent);
static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command);
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod);
struct Core::Cond
@ -178,21 +178,10 @@ void fHKthread(void * iodata)
{
color_ostream_proxy out(core->getConsole());
vector <string> args;
Core::cheap_tokenise(stuff, args);
if (args.empty()) {
out.printerr("Empty hotkey command.\n");
continue;
}
auto rv = core->runCommand(out, stuff);
string first = args[0];
args.erase(args.begin());
command_result cr = plug_mgr->InvokeCommand(out, first, args);
if(cr == CR_NEEDS_CONSOLE)
{
out.printerr("It isn't possible to run an interactive command outside the console.\n");
}
if (rv == CR_NOT_IMPLEMENTED)
out.printerr("Invalid hotkey command: '%s'\n", stuff.c_str());
}
}
}
@ -212,38 +201,122 @@ struct sortable
};
};
static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command)
static std::string getLuaHelp(std::string path)
{
Console & con = core->getConsole();
ifstream script(path);
if (script.good())
{
std::string help;
if (getline(script, help) &&
help.substr(0,3) == "-- ")
return help.substr(3);
}
return "Lua script.";
}
static std::map<string,string> listLuaScripts(std::string path)
{
std::vector<string> files;
getdir(path, files);
std::map<string,string> pset;
for (size_t i = 0; i < files.size(); i++)
{
if (hasEnding(files[i], ".lua"))
{
std::string help = getLuaHelp(path + files[i]);
pset[files[i].substr(0, files[i].size()-4)] = help;
}
}
return pset;
}
static bool fileExists(std::string path)
{
ifstream script(path);
return script.good();
}
namespace {
struct ScriptArgs {
const string *pcmd;
vector<string> *pargs;
};
}
static bool init_run_script(color_ostream &out, lua_State *state, void *info)
{
auto args = (ScriptArgs*)info;
if (!lua_checkstack(state, args->pargs->size()+10))
return false;
Lua::PushDFHack(state);
lua_getfield(state, -1, "run_script");
lua_remove(state, -2);
lua_pushstring(state, args->pcmd->c_str());
for (size_t i = 0; i < args->pargs->size(); i++)
lua_pushstring(state, (*args->pargs)[i].c_str());
return true;
}
static command_result runLuaScript(color_ostream &out, std::string filename, vector<string> &args)
{
ScriptArgs data;
data.pcmd = &filename;
data.pargs = &args;
#ifndef LINUX_BUILD
filename = toLower(filename);
#endif
bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data);
return ok ? CR_OK : CR_FAILURE;
}
command_result Core::runCommand(color_ostream &out, const std::string &command)
{
if (!command.empty())
{
// cut the input into parts
vector <string> parts;
Core::cheap_tokenise(command,parts);
if(parts.size() == 0)
{
clueless_counter ++;
return;
}
return CR_NOT_IMPLEMENTED;
string first = parts[0];
parts.erase(parts.begin());
if (first[0] == '#') return;
if (first[0] == '#')
return CR_OK;
cerr << "Invoking: " << command << endl;
return runCommand(out, first, parts);
}
else
return CR_NOT_IMPLEMENTED;
}
command_result Core::runCommand(color_ostream &con, const std::string &first, vector<string> &parts)
{
if (!first.empty())
{
// let's see what we actually got
if(first=="help" || first == "?" || first == "man")
{
if(!parts.size())
{
if (con.is_console())
{
con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n"
"Some basic editing capabilities are included (single-line text editing).\n"
"The console also has a command history - you can navigate it with Up and Down keys.\n"
"On Windows, you may have to resize your console window. The appropriate menu is accessible\n"
"by clicking on the program icon in the top bar of the window.\n\n"
"Basic commands:\n"
"by clicking on the program icon in the top bar of the window.\n\n");
}
con.print("Basic commands:\n"
" help|?|man - This text.\n"
" help COMMAND - Usage help for the given command.\n"
" ls|dir [PLUGIN] - List available commands. Optionally for single plugin.\n"
@ -274,9 +347,16 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
con.reset_color();
if (!pcmd.usage.empty())
con << "Usage:\n" << pcmd.usage << flush;
return;
return CR_OK;
}
}
auto filename = getHackPath() + "scripts/" + parts[0] + ".lua";
if (fileExists(filename))
{
string help = getLuaHelp(filename);
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK;
}
con.printerr("Unknown command: %s\n", parts[0].c_str());
}
else
@ -421,6 +501,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str());
con.reset_color();
}
auto scripts = listLuaScripts(getHackPath() + "scripts/");
if (!scripts.empty())
{
con.print("\nscripts:\n");
for (auto iter = scripts.begin(); iter != scripts.end(); ++iter)
con.print(" %-22s- %s\n", iter->first.c_str(), iter->second.c_str());
}
}
}
else if(first == "plug")
@ -439,10 +526,10 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
std::string keystr = parts[1];
if (parts[0] == "set")
core->ClearKeyBindings(keystr);
ClearKeyBindings(keystr);
for (int i = parts.size()-1; i >= 2; i--)
{
if (!core->AddKeyBinding(keystr, parts[i])) {
if (!AddKeyBinding(keystr, parts[i])) {
con.printerr("Invalid key spec: %s\n", keystr.c_str());
break;
}
@ -452,7 +539,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
for (size_t i = 1; i < parts.size(); i++)
{
if (!core->ClearKeyBindings(parts[i])) {
if (!ClearKeyBindings(parts[i])) {
con.printerr("Invalid key spec: %s\n", parts[i].c_str());
break;
}
@ -460,7 +547,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
}
else if (parts.size() == 2 && parts[0] == "list")
{
std::vector<std::string> list = core->ListKeyBindings(parts[1]);
std::vector<std::string> list = ListKeyBindings(parts[1]);
if (list.empty())
con << "No bindings." << endl;
for (size_t i = 0; i < list.size(); i++)
@ -478,13 +565,19 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
}
else if(first == "fpause")
{
World * w = core->getWorld();
World * w = getWorld();
w->SetPauseState(true);
con.print("The game was forced to pause!");
con.print("The game was forced to pause!\n");
}
else if(first == "cls")
{
con.clear();
if (con.is_console())
((Console&)con).clear();
else
{
con.printerr("No console to clear.\n");
return CR_NEEDS_CONSOLE;
}
}
else if(first == "die")
{
@ -494,12 +587,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
if(parts.size() == 1)
{
loadScriptFile(core, plug_mgr, parts[0], false);
loadScriptFile(con, parts[0], false);
}
else
{
con << "Usage:" << endl
<< " script <filename>" << endl;
return CR_WRONG_USAGE;
}
}
else
@ -507,35 +601,44 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
command_result res = plug_mgr->InvokeCommand(con, first, parts);
if(res == CR_NOT_IMPLEMENTED)
{
auto filename = getHackPath() + "scripts/" + first + ".lua";
if (fileExists(filename))
res = runLuaScript(con, filename, parts);
else
con.printerr("%s is not a recognized command.\n", first.c_str());
clueless_counter ++;
}
else if (res == CR_NEEDS_CONSOLE)
con.printerr("%s needs interactive console to work.\n", first.c_str());
return res;
}
return CR_OK;
}
return CR_NOT_IMPLEMENTED;
}
static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent)
bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
{
if(!silent)
core->getConsole() << "Loading script at " << fname << std::endl;
out << "Loading script at " << fname << std::endl;
ifstream script(fname);
if (script.good())
{
int tmp = 0;
string command;
while (getline(script, command))
{
if (!command.empty())
runInteractiveCommand(core, plug_mgr, tmp, command);
runCommand(out, command);
}
return true;
}
else
{
if(!silent)
core->getConsole().printerr("Error loading script\n");
out.printerr("Error loading script\n");
return false;
}
script.close();
}
// A thread function... for the interactive console.
@ -555,7 +658,7 @@ void fIOthread(void * iodata)
return;
}
loadScriptFile(core, plug_mgr, "dfhack.init", true);
core->loadScriptFile(con, "dfhack.init", true);
con.print("DFHack is ready. Have a nice day!\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
@ -582,7 +685,10 @@ void fIOthread(void * iodata)
main_history.save("dfhack.history");
}
runInteractiveCommand(core, plug_mgr, clueless_counter, command);
auto rv = core->runCommand(con, command);
if (rv == CR_NOT_IMPLEMENTED)
clueless_counter++;
if(clueless_counter == 3)
{
@ -636,6 +742,15 @@ void Core::fatal (std::string output, bool deactivate)
#endif
}
std::string Core::getHackPath()
{
#ifdef LINUX_BUILD
return p->getPath() + "/hack/";
#else
return p->getPath() + "\\hack\\";
#endif
}
bool Core::Init()
{
if(started)
@ -963,6 +1078,9 @@ int Core::Update()
// notify all the plugins that a game tick is finished
plug_mgr->OnUpdate(out);
// process timers in lua
Lua::Core::onUpdate(out);
// Release the fake suspend lock
{
lock_guard<mutex> lock(d->AccessMutex);

@ -42,13 +42,14 @@ using namespace DFHack;
void *type_identity::do_allocate_pod() {
void *p = malloc(size);
memset(p, 0, size);
size_t sz = byte_size();
void *p = malloc(sz);
memset(p, 0, sz);
return p;
}
void type_identity::do_copy_pod(void *tgt, const void *src) {
memmove(tgt, src, size);
memmove(tgt, src, byte_size());
};
bool type_identity::do_destroy_pod(void *obj) {
@ -81,8 +82,9 @@ bool type_identity::destroy(void *obj) {
}
void *enum_identity::do_allocate() {
void *p = malloc(byte_size());
memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t)));
size_t sz = byte_size();
void *p = malloc(sz);
memcpy(p, &first_item_value, std::min(sz, sizeof(int64_t)));
return p;
}
@ -96,7 +98,7 @@ std::vector<compound_identity*> compound_identity::top_scope;
compound_identity::compound_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name)
: constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name)
: constructed_identity(size, alloc), dfhack_name(dfhack_name), scope_parent(scope_parent)
{
next = list; list = this;
}
@ -144,8 +146,8 @@ enum_identity::enum_identity(size_t size,
const char *const *keys,
const void *attrs, struct_identity *attr_type)
: compound_identity(size, NULL, scope_parent, dfhack_name),
first_item_value(first_item_value), last_item_value(last_item_value),
keys(keys), base_type(base_type), attrs(attrs), attr_type(attr_type)
keys(keys), first_item_value(first_item_value), last_item_value(last_item_value),
base_type(base_type), attrs(attrs), attr_type(attr_type)
{
}
@ -230,15 +232,26 @@ void virtual_identity::doInit(Core *core)
known[vtable_ptr] = this;
}
virtual_identity *virtual_identity::find(const std::string &name)
{
auto name_it = name_lookup.find(name);
return (name_it != name_lookup.end()) ? name_it->second : NULL;
}
virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
{
if (!instance_ptr) return NULL;
return find(get_vtable(instance_ptr));
}
virtual_identity *virtual_identity::find(void *vtable)
{
// Actually, a reader/writer lock would be sufficient,
// since the table is only written once per class.
tthread::lock_guard<tthread::mutex> lock(*known_mutex);
void *vtable = get_vtable(instance_ptr);
std::map<void*, virtual_identity*>::iterator it = known.find(vtable);
if (it != known.end())
@ -380,7 +393,7 @@ int DFHack::findEnumItem(const std::string &name, int size, const char *const *i
void DFHack::flagarrayToString(std::vector<std::string> *pvec, const void *p,
int bytes, int base, int size, const char *const *items)
{
for (unsigned i = 0; i < bytes*8; i++) {
for (int i = 0; i < bytes*8; i++) {
int value = getBitfieldField(p, i, 1);
if (value)

@ -28,6 +28,7 @@ namespace df {
NUMBER_IDENTITY_TRAITS(int64_t);
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
NUMBER_IDENTITY_TRAITS(double);
bool_identity identity_traits<bool>::identity;
stl_string_identity identity_traits<std::string>::identity;

@ -46,6 +46,9 @@ distribution.
#include "modules/Materials.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "modules/Burrows.h"
#include "modules/Buildings.h"
#include "modules/Constructions.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
@ -58,14 +61,20 @@ distribution.
#include "df/unit.h"
#include "df/item.h"
#include "df/material.h"
#include "df/assumed_identity.h"
#include "df/nemesis_record.h"
#include "df/historical_figure.h"
#include "df/historical_entity.h"
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.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 "df/building_civzonest.h"
#include <lua.h>
#include <lauxlib.h>
@ -74,6 +83,17 @@ distribution.
using namespace DFHack;
using namespace DFHack::LuaWrapper;
void Lua::Push(lua_State *state, const Units::NoblePosition &pos)
{
lua_createtable(state, 0, 3);
Lua::PushDFObject(state, pos.entity);
lua_setfield(state, -2, "entity");
Lua::PushDFObject(state, pos.assignment);
lua_setfield(state, -2, "assignment");
Lua::PushDFObject(state, pos.position);
lua_setfield(state, -2, "position");
}
int Lua::PushPosXYZ(lua_State *state, df::coord pos)
{
if (!pos.isValid())
@ -90,6 +110,37 @@ int Lua::PushPosXYZ(lua_State *state, df::coord pos)
}
}
static df::coord2d CheckCoordXY(lua_State *state, int base, bool vararg = false)
{
df::coord2d p;
if (vararg && lua_gettop(state) <= base)
Lua::CheckDFAssign(state, &p, base);
else
{
p = df::coord2d(
luaL_checkint(state, base),
luaL_checkint(state, base+1)
);
}
return p;
}
static df::coord CheckCoordXYZ(lua_State *state, int base, bool vararg = false)
{
df::coord p;
if (vararg && lua_gettop(state) <= base)
Lua::CheckDFAssign(state, &p, base);
else
{
p = df::coord(
luaL_checkint(state, base),
luaL_checkint(state, base+1),
luaL_checkint(state, base+2)
);
}
return p;
}
/**************************************************
* Per-world persistent configuration storage API *
**************************************************/
@ -353,6 +404,7 @@ static void push_matinfo(lua_State *state, MaterialInfo &info)
case MaterialInfo::Plant: id = "plant"; break;
case MaterialInfo::Creature: id = "creature"; break;
case MaterialInfo::Inorganic: id = "inorganic"; break;
default: break;
}
lua_pushstring(state, id);
@ -500,8 +552,7 @@ static int dfhack_matinfo_matches(lua_State *state)
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::CheckDFAssign(state, &tmp, 2, false);
lua_pushboolean(state, info.matches(tmp));
}
else
@ -551,11 +602,20 @@ static void OpenModule(lua_State *state, const char *mname,
#define WRAP(function) { #function, df::wrap_function(function,true) }
#define WRAPN(name, function) { #name, df::wrap_function(function,true) }
/***** DFHack module *****/
static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); }
static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(isWorldLoaded),
WRAP(isMapLoaded),
WRAPM(Translation, TranslateName),
{ NULL, NULL }
};
/***** Gui module *****/
static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getSelectedWorkshopJob),
WRAPM(Gui, getSelectedJob),
@ -566,6 +626,8 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
{ NULL, NULL }
};
/***** Job module *****/
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; }
@ -602,18 +664,20 @@ static const luaL_Reg dfhack_job_funcs[] = {
{ NULL, NULL }
};
/***** Units module *****/
static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getContainer),
WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName),
WRAPM(Units, getIdentity),
WRAPM(Units, getNemesis),
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
WRAPM(Units, clearBurrowMembers),
WRAPM(Units, isInBurrow),
WRAPM(Units, setInBurrow),
WRAPM(Units, getAge),
WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName),
{ NULL, NULL }
};
@ -622,11 +686,26 @@ static int units_getPosition(lua_State *state)
return Lua::PushPosXYZ(state, Units::getPosition(Lua::CheckDFObject<df::unit>(state,1)));
}
static int units_getNoblePositions(lua_State *state)
{
std::vector<Units::NoblePosition> np;
if (Units::getNoblePositions(&np, Lua::CheckDFObject<df::unit>(state,1)))
Lua::PushVector(state, np);
else
lua_pushnil(state);
return 1;
}
static const luaL_Reg dfhack_units_funcs[] = {
{ "getPosition", units_getPosition },
{ "getNoblePositions", units_getNoblePositions },
{ NULL, NULL }
};
/***** Items module *****/
static bool items_moveToGround(df::item *item, df::coord pos)
{
MapExtras::MapCache mc;
@ -640,6 +719,8 @@ static bool items_moveToContainer(df::item *item, df::item *container)
}
static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getGeneralRef),
WRAPM(Items, getSpecificRef),
WRAPM(Items, getOwner),
WRAPM(Items, setOwner),
WRAPM(Items, getContainer),
@ -667,42 +748,171 @@ static const luaL_Reg dfhack_items_funcs[] = {
{ NULL, NULL }
};
/***** Maps module *****/
static bool maps_isBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y)
static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, getRegionBiome),
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween),
{ NULL, NULL }
};
static int maps_getTileBlock(lua_State *L)
{
return Maps::isBlockBurrowTile(burrow, block, df::coord2d(x,y));
auto pos = CheckCoordXYZ(L, 1, true);
Lua::PushDFObject(L, Maps::getTileBlock(pos));
return 1;
}
static bool maps_setBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y, bool enable)
static const luaL_Reg dfhack_maps_funcs[] = {
{ "getTileBlock", maps_getTileBlock },
{ NULL, NULL }
};
/***** Burrows module *****/
static bool burrows_isAssignedBlockTile(df::burrow *burrow, df::map_block *block, int x, int y)
{
return Maps::setBlockBurrowTile(burrow, block, df::coord2d(x,y), enable);
return Burrows::isAssignedBlockTile(burrow, block, df::coord2d(x,y));
}
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),
static bool burrows_setAssignedBlockTile(df::burrow *burrow, df::map_block *block, int x, int y, bool enable)
{
return Burrows::setAssignedBlockTile(burrow, block, df::coord2d(x,y), enable);
}
static const LuaWrapper::FunctionReg dfhack_burrows_module[] = {
WRAPM(Burrows, findByName),
WRAPM(Burrows, clearUnits),
WRAPM(Burrows, isAssignedUnit),
WRAPM(Burrows, setAssignedUnit),
WRAPM(Burrows, clearTiles),
WRAPN(isAssignedBlockTile, burrows_isAssignedBlockTile),
WRAPN(setAssignedBlockTile, burrows_setAssignedBlockTile),
WRAPM(Burrows, isAssignedTile),
WRAPM(Burrows, setAssignedTile),
{ NULL, NULL }
};
static int maps_listBurrowBlocks(lua_State *state)
static int burrows_listBlocks(lua_State *state)
{
std::vector<df::map_block*> pvec;
Maps::listBurrowBlocks(&pvec, Lua::CheckDFObject<df::burrow>(state,1));
Burrows::listBlocks(&pvec, Lua::CheckDFObject<df::burrow>(state,1));
Lua::PushVector(state, pvec);
return 1;
}
static const luaL_Reg dfhack_maps_funcs[] = {
{ "listBurrowBlocks", maps_listBurrowBlocks },
static const luaL_Reg dfhack_burrows_funcs[] = {
{ "listBlocks", burrows_listBlocks },
{ NULL, NULL }
};
/***** Buildings module *****/
static bool buildings_containsTile(df::building *bld, int x, int y, bool room) {
return Buildings::containsTile(bld, df::coord2d(x,y), room);
}
static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, allocInstance),
WRAPM(Buildings, checkFreeTiles),
WRAPM(Buildings, countExtentTiles),
WRAPN(containsTile, buildings_containsTile),
WRAPM(Buildings, hasSupport),
WRAPM(Buildings, constructAbstract),
WRAPM(Buildings, constructWithItems),
WRAPM(Buildings, constructWithFilters),
WRAPM(Buildings, deconstruct),
{ NULL, NULL }
};
static int buildings_findAtTile(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
Lua::PushDFObject(L, Buildings::findAtTile(pos));
return 1;
}
static int buildings_findCivzonesAt(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
std::vector<df::building_civzonest*> pvec;
if (Buildings::findCivzonesAt(&pvec, pos))
Lua::PushVector(L, pvec);
else
lua_pushnil(L);
return 1;
}
static int buildings_getCorrectSize(lua_State *state)
{
df::coord2d size(luaL_optint(state, 1, 1), luaL_optint(state, 2, 1));
auto t = (df::building_type)luaL_optint(state, 3, -1);
int st = luaL_optint(state, 4, -1);
int cu = luaL_optint(state, 5, -1);
int d = luaL_optint(state, 6, 0);
df::coord2d center;
bool flexible = Buildings::getCorrectSize(size, center, t, st, cu, d);
lua_pushboolean(state, flexible);
lua_pushinteger(state, size.x);
lua_pushinteger(state, size.y);
lua_pushinteger(state, center.x);
lua_pushinteger(state, center.y);
return 5;
}
static int buildings_setSize(lua_State *state)
{
auto bld = Lua::CheckDFObject<df::building>(state, 1);
df::coord2d size(luaL_optint(state, 2, 1), luaL_optint(state, 3, 1));
int dir = luaL_optint(state, 4, 0);
bool ok = Buildings::setSize(bld, size, dir);
lua_pushboolean(state, ok);
if (ok)
{
auto size = Buildings::getSize(bld).second;
int area = size.x*size.y;
lua_pushinteger(state, size.x);
lua_pushinteger(state, size.y);
lua_pushinteger(state, area);
lua_pushinteger(state, Buildings::countExtentTiles(&bld->room, area));
return 5;
}
else
return 1;
}
static const luaL_Reg dfhack_buildings_funcs[] = {
{ "findAtTile", buildings_findAtTile },
{ "findCivzonesAt", buildings_findCivzonesAt },
{ "getCorrectSize", buildings_getCorrectSize },
{ "setSize", buildings_setSize },
{ NULL, NULL }
};
/***** Constructions module *****/
static const LuaWrapper::FunctionReg dfhack_constructions_module[] = {
WRAPM(Constructions, designateNew),
{ NULL, NULL }
};
static int constructions_designateRemove(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
bool imm = false;
lua_pushboolean(L, Constructions::designateRemove(pos, &imm));
lua_pushboolean(L, imm);
return 2;
}
static const luaL_Reg dfhack_constructions_funcs[] = {
{ "designateRemove", constructions_designateRemove },
{ NULL, NULL }
};
@ -722,4 +932,7 @@ void OpenDFHackApi(lua_State *state)
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);
OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs);
OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs);
OpenModule(state, "constructions", dfhack_constructions_module);
}

@ -53,6 +53,7 @@ distribution.
#include "df/building.h"
#include "df/unit.h"
#include "df/item.h"
#include "df/world.h"
#include <lua.h>
#include <lauxlib.h>
@ -84,7 +85,7 @@ void *DFHack::Lua::GetDFObject(lua_State *state, type_identity *type, int val_in
return get_object_internal(state, type, val_index, exact_type, false);
}
void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type)
static void check_valid_ptr_index(lua_State *state, int val_index)
{
if (lua_type(state, val_index) == LUA_TNONE)
{
@ -93,23 +94,47 @@ void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_
else
luaL_error(state, "at index %d: pointer expected", val_index);
}
}
if (lua_isnil(state, val_index))
return NULL;
static void dfhack_printerr(lua_State *S, const std::string &str);
void *rv = get_object_internal(state, type, val_index, exact_type, false);
static void signal_typeid_error(color_ostream *out, lua_State *state,
type_identity *type, const char *msg,
int val_index, bool perr, bool signal)
{
std::string error = stl_sprintf(msg, type->getFullName().c_str());
if (!rv)
if (signal)
{
std::string error = "invalid pointer type";
if (type)
error += "; expected: " + type->getFullName();
if (val_index > 0)
luaL_argerror(state, val_index, error.c_str());
else
luaL_error(state, "at index %d: %s", val_index, error.c_str());
}
else if (perr)
{
if (out)
out->printerr("%s", error.c_str());
else
dfhack_printerr(state, error);
}
else
lua_pushstring(state, error.c_str());
}
void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type)
{
check_valid_ptr_index(state, val_index);
if (lua_isnil(state, val_index))
return NULL;
void *rv = get_object_internal(state, type, val_index, exact_type, false);
if (!rv)
signal_typeid_error(NULL, state, type, "invalid pointer type; expected: %s",
val_index, false, true);
return rv;
}
@ -162,11 +187,13 @@ static Console *get_console(lua_State *state)
return static_cast<Console*>(pstream);
}
static int DFHACK_TOSTRING_TOKEN = 0;
static std::string lua_print_fmt(lua_State *L)
{
/* Copied from lua source to fully replicate builtin print */
int n = lua_gettop(L); /* number of arguments */
lua_getglobal(L, "tostring");
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN);
std::stringstream ss;
@ -319,7 +346,7 @@ static int DFHACK_EXCEPTION_META_TOKEN = 0;
static void error_tostring(lua_State *L, bool keep_old = false)
{
lua_getglobal(L, "tostring");
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN);
if (keep_old)
lua_pushvalue(L, -2);
else
@ -341,7 +368,7 @@ static void error_tostring(lua_State *L, bool keep_old = false)
}
}
static void report_error(lua_State *L, color_ostream *out = NULL)
static void report_error(lua_State *L, color_ostream *out = NULL, bool pop = false)
{
error_tostring(L, true);
@ -353,7 +380,7 @@ static void report_error(lua_State *L, color_ostream *out = NULL)
else
dfhack_printerr(L, msg);
lua_pop(L, 1);
lua_pop(L, pop?2:1);
}
static bool convert_to_exception(lua_State *L, int slevel, lua_State *thread = NULL)
@ -542,10 +569,7 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres
bool ok = lua_pcall(L, nargs, nres, base) == LUA_OK;
if (!ok && perr)
{
report_error(L, &out);
lua_pop(L, 1);
}
report_error(L, &out, true);
lua_remove(L, base);
set_dfhack_output(L, cur_out);
@ -656,10 +680,7 @@ int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, lua_State *thre
int rv = resume_helper(from, thread, nargs, nres);
if (!Lua::IsSuccess(rv) && perr)
{
report_error(from, &out);
lua_pop(from, 1);
}
report_error(from, &out, true);
set_dfhack_output(from, cur_out);
@ -682,6 +703,19 @@ int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, int nargs, int
*/
static int DFHACK_LOADED_TOKEN = 0;
static int DFHACK_DFHACK_TOKEN = 0;
static int DFHACK_BASE_G_TOKEN = 0;
static int DFHACK_REQUIRE_TOKEN = 0;
void Lua::PushDFHack(lua_State *state)
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN);
}
void Lua::PushBaseGlobals(lua_State *state)
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN);
}
bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module)
{
@ -699,7 +733,7 @@ bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *m
}
lua_pop(state, 2);
lua_getglobal(state, "require");
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_REQUIRE_TOKEN);
lua_pushstring(state, module);
return Lua::SafeCall(out, state, 1, 1);
@ -730,21 +764,75 @@ bool DFHack::Lua::Require(color_ostream &out, lua_State *state,
return false;
if (setglobal)
lua_setglobal(state, module.c_str());
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN);
lua_swap(state);
lua_setfield(state, -2, module.c_str());
}
else
lua_pop(state, 1);
return true;
}
bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr)
static bool doAssignDFObject(color_ostream *out, lua_State *state,
type_identity *type, void *target, int val_index,
bool exact, bool perr, bool signal)
{
if (signal)
check_valid_ptr_index(state, val_index);
if (lua_istable(state, val_index))
{
val_index = lua_absindex(state, val_index);
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
PushDFObject(state, type, target);
Lua::PushDFObject(state, type, target);
lua_pushvalue(state, val_index);
return Lua::SafeCall(out, state, 2, 0, perr);
if (signal)
{
lua_call(state, 2, 0);
return true;
}
else
return Lua::SafeCall(*out, state, 2, 0, perr);
}
else if (!lua_isuserdata(state, val_index))
{
signal_typeid_error(out, state, type, "pointer to %s expected",
val_index, perr, signal);
return false;
}
else
{
void *in_ptr = Lua::GetDFObject(state, type, val_index, exact);
if (!in_ptr)
{
signal_typeid_error(out, state, type, "incompatible pointer type: %s expected",
val_index, perr, signal);
return false;
}
if (!type->copy(target, in_ptr))
{
signal_typeid_error(out, state, type, "no copy support for %s",
val_index, perr, signal);
return false;
}
return true;
}
}
bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index,
bool exact_type, bool perr)
{
return doAssignDFObject(&out, state, type, target, val_index, exact_type, perr, false);
}
void DFHack::Lua::CheckDFAssign(lua_State *state, type_identity *type,
void *target, int val_index, bool exact_type)
{
doAssignDFObject(NULL, state, type, target, val_index, exact_type, false, true);
}
bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
@ -764,10 +852,7 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
if (luaL_loadbuffer(state, code.data(), code.size(), debug_tag) != LUA_OK)
{
if (perr)
{
report_error(state, &out);
lua_pop(state, 1);
}
report_error(state, &out, true);
return false;
}
@ -810,13 +895,9 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
bool (*init)(color_ostream&, lua_State*, void*),
void *arg)
{
if (!out.is_console())
return false;
if (!lua_checkstack(state, 20))
return false;
Console &con = static_cast<Console&>(out);
lua_State *thread;
int rv;
std::string prompt;
@ -836,6 +917,10 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
return false;
}
// If not interactive, run without coroutine and bail out
if (!out.is_console())
return SafeCall(out, state, lua_gettop(state)-base-1, 0);
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
lua_pushvalue(state, base+1);
lua_remove(state, base+1);
@ -846,6 +931,8 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
rv = resume_query_loop(out, thread, state, lua_gettop(state)-base, prompt, histfile);
}
Console &con = static_cast<Console&>(out);
while (rv == LUA_YIELD)
{
if (histfile != histname)
@ -900,7 +987,7 @@ namespace {
static bool init_interpreter(color_ostream &out, lua_State *state, void *info)
{
auto args = (InterpreterArgs*)info;
lua_getglobal(state, "dfhack");
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN);
lua_getfield(state, -1, "interpreter");
lua_remove(state, -2);
lua_pushstring(state, args->prompt);
@ -1104,10 +1191,7 @@ static void do_invoke_event(lua_State *L, int argbase, int num_args, int errorfu
lua_pushvalue(L, argbase+i);
if (lua_pcall(L, num_args, 0, errorfun) != LUA_OK)
{
report_error(L);
lua_pop(L, 1);
}
report_error(L, NULL, true);
}
static void dfhack_event_invoke(lua_State *L, int base, bool from_c)
@ -1252,9 +1336,22 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_pushcfunction(state, lua_dfhack_println);
lua_setglobal(state, "print");
lua_getglobal(state, "require");
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_REQUIRE_TOKEN);
lua_getglobal(state, "tostring");
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN);
// Create the dfhack global
lua_newtable(state);
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN);
lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN);
lua_setfield(state, -2, "BASE_G");
lua_pushboolean(state, IsCoreContext(state));
lua_setfield(state, -2, "is_core_context");
@ -1295,6 +1392,17 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
luaL_setfuncs(state, dfhack_coro_funcs, 0);
lua_pop(state, 1);
// split the global environment
lua_newtable(state);
lua_newtable(state);
lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
lua_setfield(state, -2, "__index");
lua_setmetatable(state, -2);
lua_dup(state);
lua_setglobal(state, "_G");
lua_dup(state);
lua_rawseti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
// load dfhack.lua
Require(out, state, "dfhack");
@ -1305,13 +1413,135 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
return state;
}
static int next_timeout_id = 0;
static int frame_idx = 0;
static std::multimap<int,int> frame_timers;
static std::multimap<int,int> tick_timers;
int DFHACK_TIMEOUTS_TOKEN = 0;
static const char *const timeout_modes[] = {
"frames", "ticks", "days", "months", "years", NULL
};
int dfhack_timeout(lua_State *L)
{
using df::global::world;
using df::global::enabler;
lua_Number time = luaL_checknumber(L, 1);
int mode = luaL_checkoption(L, 2, NULL, timeout_modes);
luaL_checktype(L, 3, LUA_TFUNCTION);
lua_settop(L, 3);
if (mode > 0 && !Core::getInstance().isWorldLoaded())
{
lua_pushnil(L);
return 1;
}
switch (mode)
{
case 2:
time *= 1200;
break;
case 3:
time *= 33600;
break;
case 4:
time *= 403200;
break;
default:;
}
int id = next_timeout_id++;
int delta = time;
if (delta <= 0)
luaL_error(L, "Invalid timeout: %d", delta);
if (mode)
tick_timers.insert(std::pair<int,int>(world->frame_counter+delta, id));
else
frame_timers.insert(std::pair<int,int>(frame_idx+delta, id));
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
lua_swap(L);
lua_rawseti(L, -2, id);
lua_pushinteger(L, id);
return 1;
}
static void cancel_tick_timers()
{
using Lua::Core::State;
Lua::StackUnwinder frame(State);
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
for (auto it = tick_timers.begin(); it != tick_timers.end(); ++it)
{
lua_pushnil(State);
lua_rawseti(State, frame[1], it->second);
}
tick_timers.clear();
}
void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) {
if (!State) return;
switch (code)
{
case SC_MAP_UNLOADED:
case SC_WORLD_UNLOADED:
cancel_tick_timers();
break;
default:;
}
Lua::Push(State, code);
Lua::InvokeEvent(out, State, (void*)onStateChange, 1);
}
void DFHack::Lua::Core::onUpdate(color_ostream &out)
{
using df::global::world;
Lua::StackUnwinder frame(State);
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
frame_idx++;
while (!frame_timers.empty() &&
frame_timers.begin()->first <= frame_idx)
{
int id = frame_timers.begin()->second;
frame_timers.erase(frame_timers.begin());
lua_rawgeti(State, frame[1], id);
lua_pushnil(State);
lua_rawseti(State, frame[1], id);
Lua::SafeCall(out, State, 0, 0);
}
while (!tick_timers.empty() &&
tick_timers.begin()->first <= world->frame_counter)
{
int id = tick_timers.begin()->second;
tick_timers.erase(tick_timers.begin());
lua_rawgeti(State, frame[1], id);
lua_pushnil(State);
lua_rawseti(State, frame[1], id);
Lua::SafeCall(out, State, 0, 0);
}
}
void DFHack::Lua::Core::Init(color_ostream &out)
{
if (State)
@ -1321,12 +1551,18 @@ void DFHack::Lua::Core::Init(color_ostream &out)
Lua::Open(out, State);
lua_newtable(State);
lua_rawsetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
// Register events
lua_getglobal(State, "dfhack");
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN);
MakeEvent(State, (void*)onStateChange);
lua_setfield(State, -2, "onStateChange");
lua_pushcfunction(State, dfhack_timeout);
lua_setfield(State, -2, "timeout");
lua_pop(State, 1);
}

@ -576,35 +576,6 @@ static void write_field(lua_State *state, const struct_field_info *field, void *
}
}
/**
* Metamethod: represent a type node as string.
*/
static int meta_type_tostring(lua_State *state)
{
if (!lua_getmetatable(state, 1))
return 0;
lua_getfield(state, -1, "__metatable");
const char *cname = lua_tostring(state, -1);
lua_pushstring(state, stl_sprintf("<type: %s>", cname).c_str());
return 1;
}
/**
* Metamethod: represent a DF object reference as string.
*/
static int meta_ptr_tostring(lua_State *state)
{
uint8_t *ptr = get_object_addr(state, 1, 0, "access");
lua_getfield(state, UPVAL_METATABLE, "__metatable");
const char *cname = lua_tostring(state, -1);
lua_pushstring(state, stl_sprintf("<%s: 0x%08x>", cname, (unsigned)ptr).c_str());
return 1;
}
/**
* Metamethod: __index for structures.
*/
@ -1058,6 +1029,11 @@ int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id
std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?");
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
}
catch (Error::InvalidArgument &e) {
const char *vn = e.expr();
std::string tmp = stl_sprintf("Invalid argument; expected: %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");
@ -1127,26 +1103,39 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo
name = pstruct->getName() + ("." + name);
lua_pop(state, 1);
bool add_to_enum = true;
// Handle the field
switch (fields[i].mode)
{
case struct_field_info::OBJ_METHOD:
AddMethodWrapper(state, base+1, base+2, name.c_str(),
(function_identity_base*)fields[i].type);
break;
continue;
case struct_field_info::CLASS_METHOD:
continue;
case struct_field_info::POINTER:
// Skip class-typed pointers within unions
if ((fields[i].count & 2) != 0 && fields[i].type &&
fields[i].type->type() == IDTYPE_CLASS)
add_to_enum = false;
break;
default:
break;
}
// Do not add invalid globals to the enumeration order
if (!globals || *(void**)fields[i].offset)
if (globals && !*(void**)fields[i].offset)
add_to_enum = false;
if (add_to_enum)
AssociateId(state, base+3, ++cnt, name.c_str());
lua_pushlightuserdata(state, (void*)&fields[i]);
lua_setfield(state, base+2, name.c_str());
break;
}
}
}

@ -542,10 +542,23 @@ static int meta_sizeof(lua_State *state)
return 2;
}
type_identity *id = get_object_identity(state, 1, "df.sizeof()", true);
type_identity *id = get_object_identity(state, 1, "df.sizeof()", true, true);
// Static arrays need special handling
if (id->type() == IDTYPE_BUFFER)
{
auto buf = (df::buffer_container_identity*)id;
type_identity *item = buf->getItemType();
int count = buf->getSize();
fetch_container_details(state, lua_gettop(state), &item, &count);
lua_pushinteger(state, item->byte_size() * count);
}
else
lua_pushinteger(state, id->byte_size());
// Add the address
if (lua_isuserdata(state, 1))
{
lua_pushnumber(state, (size_t)get_object_ref(state, 1));

@ -41,7 +41,11 @@ distribution.
#include <map>
const char *DFHack::Error::NullPointer::what() const throw() {
return "NULL pointer access";
return "DFHack::Error::NullPointer";
}
const char *DFHack::Error::InvalidArgument::what() const throw() {
return "DFHack::Error::InvalidArgument";
}
std::string stl_sprintf(const char *fmt, ...) {

@ -28,6 +28,7 @@ distribution.
#include "PluginManager.h"
#include "RemoteServer.h"
#include "Console.h"
#include "Types.h"
#include "DataDefs.h"
#include "MiscUtils.h"
@ -45,41 +46,8 @@ using namespace std;
#include "tinythread.h"
using namespace tthread;
#ifdef LINUX_BUILD
#include <dirent.h>
#include <errno.h>
#else
#include "wdirent.h"
#endif
#include <assert.h>
static int getdir (string dir, vector<string> &files)
{
DIR *dp;
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL)
{
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
files.push_back(string(dirp->d_name));
}
closedir(dp);
return 0;
}
bool hasEnding (std::string const &fullString, std::string const &ending)
{
if (fullString.length() > ending.length())
{
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
}
else
{
return false;
}
}
struct Plugin::RefLock
{
RefLock()
@ -563,10 +531,10 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn)
PluginManager::PluginManager(Core * core)
{
#ifdef LINUX_BUILD
string path = core->p->getPath() + "/hack/plugins/";
string path = core->getHackPath() + "plugins/";
const string searchstr = ".plug.so";
#else
string path = core->p->getPath() + "\\hack\\plugins\\";
string path = core->getHackPath() + "plugins\\";
const string searchstr = ".plug.dll";
#endif
cmdlist_mutex = new mutex();
@ -624,7 +592,7 @@ command_result PluginManager::InvokeCommand(color_ostream &out, const std::strin
bool PluginManager::CanInvokeHotkey(const std::string &command, df::viewscreen *top)
{
Plugin *plugin = getPluginByCommand(command);
return plugin ? plugin->can_invoke_hotkey(command, top) : false;
return plugin ? plugin->can_invoke_hotkey(command, top) : true;
}
void PluginManager::OnUpdate(color_ostream &out)

@ -216,6 +216,9 @@ void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat,
case MaterialInfo::Plant:
info->set_plant_id(mat.index);
break;
default:
break;
}
}
@ -298,7 +301,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (mask && mask->labors())
{
for (int i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++)
for (size_t i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++)
if (unit->status.labors[i])
info->add_labors(i);
}
@ -399,7 +402,7 @@ static command_result GetWorldInfo(color_ostream &stream,
case GAMETYPE_ADVENTURE_MAIN:
out->set_mode(GetWorldInfoOut::MODE_ADVENTURE);
if (auto unit = vector_get(world->units.other[0], 0))
if (auto unit = vector_get(world->units.active, 0))
out->set_player_unit_id(unit->id);
if (!ui_advmode)
@ -630,7 +633,7 @@ static command_result ListSquads(color_ostream &stream,
static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn *in)
{
for (size_t i = 0; i < in->change_size(); i++)
for (int i = 0; i < in->change_size(); i++)
{
auto change = in->change(i);
auto unit = df::unit::find(change.unit_id());
@ -708,7 +711,7 @@ command_result CoreService::RunCommand(color_ostream &stream,
for (int i = 0; i < in->arguments_size(); i++)
args.push_back(in->arguments(i));
return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args);
return Core::getInstance().runCommand(stream, cmd, args);
}
command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)

@ -68,6 +68,8 @@ namespace DFHack
return tiletype::LavaPillar;
case tiletype_material::STONE:
return tiletype::StonePillar;
default:
break;
}
}

@ -0,0 +1,129 @@
/*
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 "Export.h"
#include "MiscUtils.h"
#include "Error.h"
#include "Types.h"
#ifndef LINUX_BUILD
#include <Windows.h>
#include "wdirent.h"
#else
#include <sys/time.h>
#include <ctime>
#include <dirent.h>
#include <errno.h>
#endif
#include <ctype.h>
#include <stdarg.h>
#include <sstream>
#include <map>
int DFHack::getdir(std::string dir, std::vector<std::string> &files)
{
DIR *dp;
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL)
{
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
files.push_back(std::string(dirp->d_name));
}
closedir(dp);
return 0;
}
bool DFHack::hasEnding (std::string const &fullString, std::string const &ending)
{
if (fullString.length() > ending.length())
{
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
}
else
{
return false;
}
}
df::general_ref *DFHack::findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
for (int i = vec.size()-1; i >= 0; i--)
{
df::general_ref *ref = vec[i];
if (ref->getType() == type)
return ref;
}
return NULL;
}
bool DFHack::removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id)
{
for (int i = vec.size()-1; i >= 0; i--)
{
df::general_ref *ref = vec[i];
if (ref->getType() != type || ref->getID() != id)
continue;
vector_erase_at(vec, i);
delete ref;
return true;
}
return false;
}
df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type)
{
for (int i = vec.size()-1; i >= 0; i--)
{
df::specific_ref *ref = vec[i];
if (ref->type == type)
return ref;
}
return NULL;
}
bool DFHack::removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr)
{
for (int i = vec.size()-1; i >= 0; i--)
{
df::specific_ref *ref = vec[i];
if (ref->type != type || ref->object != ptr)
continue;
vector_erase_at(vec, i);
delete ref;
return true;
}
return false;
}

@ -35,6 +35,8 @@ distribution.
#include "modules/Graphic.h"
#include "SDL_events.h"
#include "RemoteClient.h"
struct WINDOW;
namespace tthread
@ -117,10 +119,16 @@ namespace DFHack
/// returns a named pointer.
void *GetData(std::string key);
command_result runCommand(color_ostream &out, const std::string &command, std::vector <std::string> &parameters);
command_result runCommand(color_ostream &out, const std::string &command);
bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false);
bool ClearKeyBindings(std::string keyspec);
bool AddKeyBinding(std::string keyspec, std::string cmdline);
std::vector<std::string> ListKeyBindings(std::string keyspec);
std::string getHackPath();
bool isWorldLoaded() { return (last_world_data_ptr != NULL); }
bool isMapLoaded() { return (last_local_map_ptr != NULL && last_world_data_ptr != NULL); }

@ -83,7 +83,7 @@ namespace DFHack
public:
virtual ~type_identity() {}
size_t byte_size() { return size; }
virtual size_t byte_size() { return size; }
virtual identity_type type() = 0;
@ -318,6 +318,9 @@ namespace DFHack
public:
static virtual_identity *get(virtual_ptr instance_ptr);
static virtual_identity *find(void *vtable);
static virtual_identity *find(const std::string &name);
bool is_instance(virtual_ptr instance_ptr) {
if (!instance_ptr) return false;
if (vtable_ptr) {
@ -372,14 +375,14 @@ namespace DFHack
template<class T>
int linear_index(const DFHack::enum_list_attr<T> &lst, T val) {
for (int i = 0; i < lst.size; i++)
for (size_t i = 0; i < lst.size; i++)
if (lst.items[i] == val)
return i;
return -1;
}
inline int linear_index(const DFHack::enum_list_attr<const char*> &lst, const std::string &val) {
for (int i = 0; i < lst.size; i++)
for (size_t i = 0; i < lst.size; i++)
if (lst.items[i] == val)
return i;
return -1;

@ -289,9 +289,11 @@ namespace df
{}
buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL)
: container_identity(item->byte_size()*size, NULL, item, ienum), size(size)
: container_identity(0, NULL, item, ienum), size(size)
{}
size_t byte_size() { return getItemType()->byte_size()*size; }
std::string getFullName(type_identity *item);
int getSize() { return size; }
@ -445,6 +447,7 @@ namespace df
NUMBER_IDENTITY_TRAITS(int64_t);
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
NUMBER_IDENTITY_TRAITS(double);
template<> struct DFHACK_EXPORT identity_traits<bool> {
static bool_identity identity;

@ -51,6 +51,18 @@ namespace DFHack
#define CHECK_NULL_POINTER(var) \
{ if (var == NULL) throw DFHack::Error::NullPointer(#var); }
class DFHACK_EXPORT InvalidArgument : public All {
const char *expr_;
public:
InvalidArgument(const char *expr_ = NULL) : expr_(expr_) {}
const char *expr() const { return expr_; }
virtual const char *what() const throw();
};
#define CHECK_INVALID_ARGUMENT(expr) \
{ if (!(expr)) throw DFHack::Error::InvalidArgument(#expr); }
class DFHACK_EXPORT AllSymbols : public All{};
// Syntax errors and whatnot, the xml can't be read
class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols

@ -36,6 +36,10 @@ distribution.
namespace DFHack {
class function_identity_base;
namespace Units {
struct NoblePosition;
}
}
namespace DFHack {namespace Lua {
@ -44,6 +48,9 @@ namespace DFHack {namespace Lua {
*/
DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL);
DFHACK_EXPORT void PushDFHack(lua_State *state);
DFHACK_EXPORT void PushBaseGlobals(lua_State *state);
/**
* Load a module using require(). Leaves the stack as is.
*/
@ -105,7 +112,15 @@ namespace DFHack {namespace Lua {
* Return behavior is of SafeCall below.
*/
DFHACK_EXPORT bool AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr = true);
type_identity *type, void *target, int val_index,
bool exact_type = false, bool perr = true);
/**
* Assign the value at val_index to the target of given identity using df.assign().
* Otherwise throws an error.
*/
DFHACK_EXPORT void CheckDFAssign(lua_State *state, type_identity *type,
void *target, int val_index, bool exact_type = false);
/**
* Push the pointer onto the stack as a wrapped DF object of a specific type.
@ -135,8 +150,19 @@ namespace DFHack {namespace Lua {
* Assign the value at val_index to the target using df.assign().
*/
template<class T>
bool AssignDFObject(color_ostream &out, lua_State *state, T *target, int val_index, bool perr = true) {
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
bool AssignDFObject(color_ostream &out, lua_State *state, T *target,
int val_index, bool exact_type = false, bool perr = true) {
return AssignDFObject(out, state, df::identity_traits<T>::get(),
target, val_index, exact_type, perr);
}
/**
* Assign the value at val_index to the target using df.assign().
* Throws in case of an error.
*/
template<class T>
void CheckDFAssign(lua_State *state, T *target, int val_index, bool exact_type = false) {
CheckDFAssign(state, df::identity_traits<T>::get(), target, val_index, exact_type);
}
/**
@ -243,14 +269,21 @@ namespace DFHack {namespace Lua {
}
inline void Push(lua_State *state, df::coord &obj) { PushDFObject(state, &obj); }
inline void Push(lua_State *state, df::coord2d &obj) { PushDFObject(state, &obj); }
void Push(lua_State *state, const Units::NoblePosition &pos);
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)
void PushVector(lua_State *state, const T &pvec, bool addn = false)
{
lua_createtable(state,pvec.size(), addn?1:0);
if (addn)
{
lua_createtable(state,pvec.size(),0);
lua_pushinteger(state, pvec.size());
lua_setfield(state, -2, "n");
}
for (size_t i = 0; i < pvec.size(); i++)
{
@ -267,6 +300,26 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void MakeEvent(lua_State *state, void *key);
DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args);
class StackUnwinder {
lua_State *state;
int top;
public:
StackUnwinder(lua_State *state, int bias = 0) : state(state), top(0) {
if (state) top = lua_gettop(state) - bias;
}
~StackUnwinder() {
if (state) lua_settop(state, top);
}
operator int () { return top; }
int operator+ (int v) { return top + v; }
int operator- (int v) { return top + v; }
int operator[] (int v) { return top + v; }
StackUnwinder &operator += (int v) { top += v; return *this; }
StackUnwinder &operator -= (int v) { top += v; return *this; }
StackUnwinder &operator ++ () { top++; return *this; }
StackUnwinder &operator -- () { top--; return *this; }
};
/**
* Namespace for the common lua interpreter state.
* All accesses must be done under CoreSuspender.
@ -280,6 +333,8 @@ namespace DFHack {namespace Lua {
// Events signalled by the core
void onStateChange(color_ostream &out, int code);
// Signals timers
void onUpdate(color_ostream &out);
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); }

@ -73,7 +73,7 @@ namespace DFHack
*/
template<class T>
void flagarray_to_ints(RepeatedField<google::protobuf::int32> *pf, const BitArray<T> &val) {
for (int i = 0; i < val.size*8; i++)
for (size_t i = 0; i < val.size*8; i++)
if (val.is_set(T(i)))
pf->Add(i);
}

@ -28,6 +28,10 @@ distribution.
#include "Pragma.h"
#include "Export.h"
#include "DataDefs.h"
#include "df/general_ref.h"
#include "df/specific_ref.h"
namespace DFHack
{
struct t_matglossPair
@ -69,4 +73,13 @@ namespace DFHack
std::string name;
uint32_t xpNxtLvl;
};
DFHACK_EXPORT int getdir(std::string dir, std::vector<std::string> &files);
DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending);
DFHACK_EXPORT df::general_ref *findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id);
DFHACK_EXPORT df::specific_ref *findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr);
}// namespace DFHack

@ -34,6 +34,14 @@ distribution.
#include "df/siegeengine_type.h"
#include "df/trap_type.h"
namespace df
{
struct job_item;
struct item;
struct building_extents;
struct building_civzonest;
}
namespace DFHack
{
namespace Buildings
@ -84,5 +92,86 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building);
*/
DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map <uint32_t, std::string> & btypes);
/**
* Find the building located at the specified tile.
* Does not work on civzones.
*/
DFHACK_EXPORT df::building *findAtTile(df::coord pos);
/**
* Find civzones located at the specified tile.
*/
DFHACK_EXPORT bool findCivzonesAt(std::vector<df::building_civzonest*> *pvec, df::coord pos);
/**
* Allocates a building object using this type and position.
*/
DFHACK_EXPORT df::building *allocInstance(df::coord pos, df::building_type type, int subtype = -1, int custom = -1);
/**
* Sets size and center to the correct dimensions of the building.
* If part of the original size value was used, returns true.
*/
DFHACK_EXPORT bool getCorrectSize(df::coord2d &size, df::coord2d &center,
df::building_type type, int subtype = -1, int custom = -1,
int direction = 0);
/**
* Checks if the tiles are free to be built upon.
*/
DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size,
df::building_extents *ext = NULL,
bool create_ext = false, bool allow_occupied = false);
/**
* Returns the number of tiles included by the extent, or defval.
*/
DFHACK_EXPORT int countExtentTiles(df::building_extents *ext, int defval = -1);
/**
* Checks if the building contains the specified tile.
*/
DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile, bool room = false);
/**
* Checks if the area has support from the terrain.
*/
DFHACK_EXPORT bool hasSupport(df::coord pos, df::coord2d size);
/**
* Sets the size of the building, using size and direction as guidance.
* Returns true if the building can be created at its position, using that size.
*/
DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction = 0);
/**
* Decodes the size of the building into pos and size.
*/
DFHACK_EXPORT std::pair<df::coord,df::coord2d> getSize(df::building *bld);
/**
* Constructs an abstract building, i.e. stockpile or civzone.
*/
DFHACK_EXPORT bool constructAbstract(df::building *bld);
/**
* Initiates construction of the building, using specified items as inputs.
* Returns true if success.
*/
DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector<df::item*> items);
/**
* Initiates construction of the building, using specified item filters.
* Assumes immediate ownership of the item objects, and deletes them in case of error.
* Returns true if success.
*/
DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector<df::job_item*> items);
/**
* Deconstructs or queues deconstruction of a building.
* Returns true if the building has been destroyed instantly.
*/
DFHACK_EXPORT bool deconstruct(df::building *bld);
}
}

@ -0,0 +1,78 @@
/*
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.
*/
#pragma once
#include "Export.h"
#include "DataDefs.h"
#include "modules/Maps.h"
#include <vector>
/**
* \defgroup grp_burrows Burrows module and its types
* @ingroup grp_modules
*/
namespace df
{
struct unit;
struct burrow;
struct block_burrow;
}
namespace DFHack
{
namespace Burrows
{
DFHACK_EXPORT df::burrow *findByName(std::string name);
// Units
DFHACK_EXPORT void clearUnits(df::burrow *burrow);
DFHACK_EXPORT bool isAssignedUnit(df::burrow *burrow, df::unit *unit);
DFHACK_EXPORT void setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable);
// Tiles
DFHACK_EXPORT void clearTiles(df::burrow *burrow);
DFHACK_EXPORT void listBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow);
DFHACK_EXPORT bool isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile);
DFHACK_EXPORT bool setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable);
inline bool isAssignedTile(df::burrow *burrow, df::coord tile) {
return isAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile);
}
inline bool setAssignedTile(df::burrow *burrow, df::coord tile, bool enable) {
return setAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile, enable);
}
DFHACK_EXPORT df::block_burrow *getBlockMask(df::burrow *burrow, df::map_block *block, bool create = false);
DFHACK_EXPORT bool deleteBlockMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask);
inline bool deleteBlockMask(df::burrow *burrow, df::map_block *block) {
return deleteBlockMask(burrow, block, getBlockMask(burrow, block));
}
}
}

@ -31,6 +31,8 @@ distribution.
#include "Export.h"
#include "DataDefs.h"
#include "df/construction.h"
#include "df/construction_type.h"
#include "df/item_type.h"
/**
* \defgroup grp_constructions Construction module parts
@ -57,6 +59,12 @@ DFHACK_EXPORT bool isValid();
DFHACK_EXPORT uint32_t getCount();
DFHACK_EXPORT bool copyConstruction (const int32_t index, t_construction &out);
DFHACK_EXPORT df::construction * getConstruction (const int32_t index);
DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type,
df::item_type item = df::item_type::NONE, int mat_index = -1);
DFHACK_EXPORT bool designateRemove(df::coord pos, bool *immediate = NULL);
}
}
#endif

@ -36,6 +36,7 @@ distribution.
#include "df/item.h"
#include "df/item_type.h"
#include "df/general_ref.h"
#include "df/specific_ref.h"
namespace df
{
@ -126,6 +127,10 @@ 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);
/// Retrieve refs
DFHACK_EXPORT df::general_ref *getGeneralRef(df::item *item, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::item *item, df::specific_ref_type type);
/// 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.

@ -30,6 +30,9 @@ distribution.
#include "Module.h"
#include <ostream>
#include "DataDefs.h"
#include "df/job_item_ref.h"
namespace df
{
struct job;
@ -58,6 +61,10 @@ namespace DFHack
// 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 attachJobItem(df::job *job, df::item *item,
df::job_item_ref::T_role role,
int filter_idx = -1, int insert_idx = -1);
}
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);

@ -262,10 +262,13 @@ public:
private:
friend class MapCache;
friend class BlockInfo;
MapCache *parent;
df::map_block *block;
int biomeIndexAt(df::coord2d p);
bool valid;
bool dirty_designations:1;
bool dirty_tiles:1;
@ -521,6 +524,8 @@ private:
uint32_t y_tmax;
uint32_t z_max;
std::vector<df::coord2d> geoidx;
std::vector<int> default_soil;
std::vector<int> default_stone;
std::vector< std::vector <int16_t> > layer_mats;
std::map<df::coord2d, df::world_region_details*> region_details;
std::map<DFCoord, Block *> blocks;

@ -241,6 +241,20 @@ extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t
inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); }
inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); }
extern DFHACK_EXPORT df::tiletype *getTileType(int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::tile_designation *getTileDesignation(int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::tile_occupancy *getTileOccupancy(int32_t x, int32_t y, int32_t z);
inline df::tiletype *getTileType(df::coord pos) {
return getTileType(pos.x, pos.y, pos.z);
}
inline df::tile_designation *getTileDesignation(df::coord pos) {
return getTileDesignation(pos.x, pos.y, pos.z);
}
inline df::tile_occupancy *getTileOccupancy(df::coord pos) {
return getTileOccupancy(pos.x, pos.y, pos.z);
}
DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos);
/// sorts the block event vector into multiple vectors by type
@ -256,34 +270,7 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block,
/// 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 );
/*
* 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);
}
DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2);
}
}
#endif

@ -52,6 +52,8 @@ DFHACK_EXPORT bool copyName(df::language_name * address, df::language_name * tar
DFHACK_EXPORT void setNickname(df::language_name *name, std::string nick);
DFHACK_EXPORT std::string capitalize(const std::string &str, bool all_words = false);
// translate a name using the loaded dictionaries
DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true,
bool onlyLastPart = false);

@ -37,6 +37,10 @@ namespace df
{
struct nemesis_record;
struct burrow;
struct assumed_identity;
struct historical_entity;
struct entity_position_assignment;
struct entity_position;
}
/**
@ -201,6 +205,7 @@ DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit);
DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit);
DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isDead(df::unit *unit);
@ -209,11 +214,18 @@ 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 double getAge(df::unit *unit, bool true_age = false);
DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow);
DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable);
struct NoblePosition {
df::historical_entity *entity;
df::entity_position_assignment *assignment;
df::entity_position *position;
};
DFHACK_EXPORT bool getNoblePositions(std::vector<NoblePosition> *pvec, df::unit *unit);
DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool ignore_noble = false, bool plural = false);
DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false);
}
}
#endif

@ -117,11 +117,11 @@ namespace Windows
for ( auto iter = str.begin(); iter != str.end(); iter++)
{
auto elem = *iter;
if(cursor_y >= height)
if(cursor_y >= (int)height)
break;
if(wrap)
{
if(cursor_x >= width)
if(cursor_x >= (int)width)
cursor_x = wrap_column;
}
df_screentile & tile = buffer[cursor_x * height + cursor_y];
@ -224,12 +224,12 @@ namespace Windows
virtual void blit_to_parent ()
{
df_tilebuf par = parent->getBuffer();
for(int xi = 0; xi < width; xi++)
for(unsigned xi = 0; xi < width; xi++)
{
for(int yi = 0; yi < height; yi++)
for(unsigned yi = 0; yi < height; yi++)
{
int parx = left + xi;
int pary = top + yi;
unsigned parx = left + xi;
unsigned pary = top + yi;
if(pary >= par.height) continue;
if(parx >= par.width) continue;
par.data[parx * par.height + pary] = buffer[xi * height + yi];

@ -1,6 +1,15 @@
-- Common startup file for all dfhack plugins with lua support
-- The global dfhack table is already created by C++ init code.
-- Setup the global environment.
-- BASE_G is the original lua global environment,
-- preserved as a common denominator for all modules.
-- This file uses it instead of the new default one.
local dfhack = dfhack
local base_env = dfhack.BASE_G
local _ENV = base_env
-- Console color constants
COLOR_RESET = -1
@ -70,7 +79,7 @@ function mkmodule(module,env)
if plugname then
dfhack.open_plugin(pkg,plugname)
end
setmetatable(pkg, { __index = (env or _G) })
setmetatable(pkg, { __index = (env or base_env) })
return pkg
end
@ -116,6 +125,31 @@ function xyz2pos(x,y,z)
end
end
function rawset_default(target,source)
for k,v in pairs(source) do
if rawget(target,k) == nil then
rawset(target,k,v)
end
end
end
function safe_index(obj,idx,...)
if obj == nil or idx == nil then
return nil
end
if type(idx) == 'number' and (idx < 0 or idx >= #obj) then
return nil
end
obj = obj[idx]
if select('#',...) > 0 then
return safe_index(obj,...)
else
return obj
end
end
-- String conversions
function dfhack.event:__tostring()
return "<event>"
end
@ -139,6 +173,11 @@ function dfhack.maps.getTileSize()
return map.x_count, map.y_count, map.z_count
end
function dfhack.buildings.getSize(bld)
local x, y = bld.x1, bld.y1
return bld.x2+1-x, bld.y2+1-y, bld.centerx-x, bld.centery-y
end
-- Interactive
local print_banner = true
@ -159,12 +198,33 @@ function dfhack.interpreter(prompt,hfile,env)
end
local prompt_str = "["..(prompt or 'lua').."]# "
local prompt_cont = string.rep(' ',#prompt_str-4)..">>> "
local prompt_env = {}
local t_prompt
local cmdlinelist = {}
local t_prompt = nil
local vcnt = 1
local pfix_handlers = {
['!'] = function(data)
print(table.unpack(data,2,data.n))
end,
['~'] = function(data)
print(table.unpack(data,2,data.n))
printall(data[2])
end,
['='] = function(data)
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
}
setmetatable(prompt_env, { __index = env or _G })
local cmdlinelist={}
while true do
local cmdline = dfhack.lineedit(t_prompt or prompt_str, hfile)
@ -173,23 +233,26 @@ function dfhack.interpreter(prompt,hfile,env)
elseif cmdline ~= '' then
local pfix = string.sub(cmdline,1,1)
if pfix == '!' or pfix == '=' then
if not t_prompt and pfix_handlers[pfix] then
cmdline = 'return '..string.sub(cmdline,2)
else
pfix = nil
end
table.insert(cmdlinelist,cmdline)
local code,err = load(table.concat(cmdlinelist,'\n'), '=(interactive)', 't', prompt_env)
cmdline = table.concat(cmdlinelist,'\n')
if code == nil then
if err:sub(-5)=="<eof>" then
t_prompt="[cont]"
local code,err = load(cmdline, '=(interactive)', 't', prompt_env)
if code == nil then
if not pfix and err:sub(-5)=="<eof>" then
t_prompt=prompt_cont
else
dfhack.printerr(err)
cmdlinelist={}
t_prompt=nil
end
else
cmdlinelist={}
t_prompt=nil
@ -197,18 +260,7 @@ function dfhack.interpreter(prompt,hfile,env)
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
safecall(pfix_handlers[pfix], data)
end
end
end
@ -217,5 +269,23 @@ function dfhack.interpreter(prompt,hfile,env)
return true
end
-- Command scripts
dfhack.scripts = dfhack.scripts or {}
function dfhack.run_script(file,...)
local env = dfhack.scripts[file]
if env == nil then
env = {}
setmetatable(env, { __index = base_env })
dfhack.scripts[file] = env
end
local f,perr = loadfile(file, 't', env)
if f == nil then
error(perr)
end
return f(...)
end
-- Feed the table back to the require() mechanism.
return dfhack

@ -0,0 +1,437 @@
local dfhack = dfhack
local _ENV = dfhack.BASE_G
local buildings = dfhack.buildings
local utils = require 'utils'
--[[ Building input material tables. ]]
local building_inputs = {
[df.building_type.Chair] = { { item_type=df.item_type.CHAIR, vector_id=df.job_item_vector_id.CHAIR } },
[df.building_type.Bed] = { { item_type=df.item_type.BED, vector_id=df.job_item_vector_id.BED } },
[df.building_type.Table] = { { item_type=df.item_type.TABLE, vector_id=df.job_item_vector_id.TABLE } },
[df.building_type.Coffin] = { { item_type=df.item_type.COFFIN, vector_id=df.job_item_vector_id.COFFIN } },
[df.building_type.FarmPlot] = { },
[df.building_type.Furnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
[df.building_type.TradeDepot] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } },
[df.building_type.Door] = { { item_type=df.item_type.DOOR, vector_id=df.job_item_vector_id.DOOR } },
[df.building_type.Floodgate] = {
{
item_type=df.item_type.FLOODGATE,
vector_id=df.job_item_vector_id.FLOODGATE
}
},
[df.building_type.Box] = {
{
flags1={ empty=true },
item_type=df.item_type.BOX,
vector_id=df.job_item_vector_id.BOX
}
},
[df.building_type.Weaponrack] = {
{
item_type=df.item_type.WEAPONRACK,
vector_id=df.job_item_vector_id.WEAPONRACK
}
},
[df.building_type.Armorstand] = {
{
item_type=df.item_type.ARMORSTAND,
vector_id=df.job_item_vector_id.ARMORSTAND
}
},
[df.building_type.Workshop] = { { flags2={ building_material=true, non_economic=true } } },
[df.building_type.Cabinet] = {
{ item_type=df.item_type.CABINET, vector_id=df.job_item_vector_id.CABINET }
},
[df.building_type.Statue] = { { item_type=df.item_type.STATUE, vector_id=df.job_item_vector_id.STATUE } },
[df.building_type.WindowGlass] = { { item_type=df.item_type.WINDOW, vector_id=df.job_item_vector_id.WINDOW } },
[df.building_type.WindowGem] = {
{
item_type=df.item_type.SMALLGEM,
quantity=3,
vector_id=df.job_item_vector_id.ANY_GENERIC35
}
},
[df.building_type.Well] = {
{
item_type=df.item_type.BLOCKS,
vector_id=df.job_item_vector_id.ANY_GENERIC35
},
{
name='bucket',
flags2={ lye_milk_free=true },
item_type=df.item_type.BUCKET,
vector_id=df.job_item_vector_id.BUCKET
},
{
name='chain',
item_type=df.item_type.CHAIN,
vector_id=df.job_item_vector_id.CHAIN
},
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
},
[df.building_type.Bridge] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } },
[df.building_type.RoadDirt] = { },
[df.building_type.RoadPaved] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } },
[df.building_type.AnimalTrap] = {
{
flags1={ empty=true },
item_type=df.item_type.ANIMALTRAP,
vector_id=df.job_item_vector_id.ANIMALTRAP
}
},
[df.building_type.Support] = { { flags2={ building_material=true, non_economic=true } } },
[df.building_type.ArcheryTarget] = { { flags2={ building_material=true, non_economic=true } } },
[df.building_type.Chain] = { { item_type=df.item_type.CHAIN, vector_id=df.job_item_vector_id.CHAIN } },
[df.building_type.Cage] = { { item_type=df.item_type.CAGE, vector_id=df.job_item_vector_id.CAGE } },
[df.building_type.Weapon] = { { vector_id=df.job_item_vector_id.ANY_SPIKE } },
[df.building_type.ScrewPump] = {
{
item_type=df.item_type.BLOCKS,
vector_id=df.job_item_vector_id.ANY_GENERIC35
},
{
name='screw',
flags2={ screw=true },
item_type=df.item_type.TRAPCOMP,
vector_id=df.job_item_vector_id.ANY_WEAPON
},
{
name='pipe',
item_type=df.item_type.PIPE_SECTION,
vector_id=df.job_item_vector_id.PIPE_SECTION
}
},
[df.building_type.Construction] = { { flags2={ building_material=true, non_economic=true } } },
[df.building_type.Hatch] = {
{
item_type=df.item_type.HATCH_COVER,
vector_id=df.job_item_vector_id.HATCH_COVER
}
},
[df.building_type.GrateWall] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } },
[df.building_type.GrateFloor] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } },
[df.building_type.BarsVertical] = {
{ item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 }
},
[df.building_type.BarsFloor] = {
{ item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 }
},
[df.building_type.GearAssembly] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
},
[df.building_type.AxleHorizontal] = {
{ item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD, quantity=-1 }
},
[df.building_type.AxleVertical] = { { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD } },
[df.building_type.WaterWheel] = {
{
item_type=df.item_type.WOOD,
quantity=3,
vector_id=df.job_item_vector_id.WOOD
}
},
[df.building_type.Windmill] = {
{
item_type=df.item_type.WOOD,
quantity=4,
vector_id=df.job_item_vector_id.WOOD
}
},
[df.building_type.TractionBench] = {
{
item_type=df.item_type.TRACTION_BENCH,
vector_id=df.job_item_vector_id.TRACTION_BENCH
}
},
[df.building_type.Slab] = { { item_type=df.item_type.SLAB } },
[df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } },
[df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } }
}
local furnace_inputs = {
[df.furnace_type.WoodFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
[df.furnace_type.Smelter] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
[df.furnace_type.GlassFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
[df.furnace_type.Kiln] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
[df.furnace_type.MagmaSmelter] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } },
[df.furnace_type.MagmaGlassFurnace] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } },
[df.furnace_type.MagmaKiln] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }
}
local workshop_inputs = {
[df.workshop_type.Carpenters] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Farmers] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Masons] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Craftsdwarfs] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Jewelers] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.MetalsmithsForge] = {
{
name='anvil',
flags2={ fire_safe=true },
item_type=df.item_type.ANVIL,
vector_id=df.job_item_vector_id.ANVIL
},
{ flags2={ building_material=true, fire_safe=true, non_economic=true } }
},
[df.workshop_type.MagmaForge] = {
{
name='anvil',
flags2={ magma_safe=true },
item_type=df.item_type.ANVIL,
vector_id=df.job_item_vector_id.ANVIL
},
{ flags2={ building_material=true, magma_safe=true, non_economic=true } }
},
[df.workshop_type.Bowyers] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Mechanics] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Siege] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } },
[df.workshop_type.Butchers] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Leatherworks] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Tanners] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Clothiers] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Fishery] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Still] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Loom] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Quern] = { { item_type=df.item_type.QUERN, vector_id=df.job_item_vector_id.QUERN } },
[df.workshop_type.Kennels] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Kitchen] = { { flags2={ building_material=true, non_economic=true } } },
[df.workshop_type.Ashery] = {
{
item_type=df.item_type.BLOCKS,
vector_id=df.job_item_vector_id.ANY_GENERIC35
},
{
name='barrel',
flags1={ empty=true },
item_type=df.item_type.BARREL,
vector_id=df.job_item_vector_id.BARREL
},
{
name='bucket',
flags2={ lye_milk_free=true },
item_type=df.item_type.BUCKET,
vector_id=df.job_item_vector_id.BUCKET
}
},
[df.workshop_type.Dyers] = {
{
name='barrel',
flags1={ empty=true },
item_type=df.item_type.BARREL,
vector_id=df.job_item_vector_id.BARREL
},
{
name='bucket',
flags2={ lye_milk_free=true },
item_type=df.item_type.BUCKET,
vector_id=df.job_item_vector_id.BUCKET
}
},
[df.workshop_type.Millstone] = {
{
item_type=df.item_type.MILLSTONE,
vector_id=df.job_item_vector_id.MILLSTONE
},
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
}
}
local trap_inputs = {
[df.trap_type.StoneFallTrap] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
},
[df.trap_type.WeaponTrap] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
},
{ vector_id=df.job_item_vector_id.ANY_WEAPON }
},
[df.trap_type.Lever] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
},
[df.trap_type.PressurePlate] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
},
[df.trap_type.CageTrap] = {
{
name='mechanism',
item_type=df.item_type.TRAPPARTS,
vector_id=df.job_item_vector_id.TRAPPARTS
}
}
}
local function get_inputs_by_type(type,subtype,custom)
if type == df.building_type.Workshop then
return workshop_inputs[subtype]
elseif type == df.building_type.Furnace then
return furnace_inputs[subtype]
elseif type == df.building_type.Trap then
return trap_inputs[subtype]
else
return building_inputs[type]
end
end
local function augment_input(input, argtable)
local rv = {}
local arg = argtable[input.name or 'material']
if arg then
utils.assign(rv, arg)
end
utils.assign(rv, input)
if rv.mat_index and safe_index(rv, 'flags2', 'non_economic') then
rv.flags2.non_economic = false
end
rv.new = true
rv.name = nil
return rv
end
function buildings.getFiltersByType(argtable,type,subtype,custom)
local inputs = get_inputs_by_type(type,subtype,custom)
if not inputs then
return nil
end
local rv = {}
for i,v in ipairs(inputs) do
rv[i] = augment_input(v, argtable)
end
return rv
end
--[[
Wraps all steps necessary to create a building with
a construct job into one function.
dfhack.buildings.constructBuilding{
-- Position:
pos = { x = ..., y = ..., z = ... },
-- OR
x = ..., y = ..., z = ...,
-- Type:
type = df.building_type.FOO, subtype = ..., custom = ...,
-- Field initialization:
fields = { ... },
-- Size and orientation:
width = ..., height = ..., direction = ...,
-- Abort if not all tiles in the rectangle are available:
full_rectangle = true,
-- Materials:
items = { item, item ... },
-- OR
filter = { { ... }, { ... }... }
-- OR
abstract = true
-- OR
material = { filter_properties... }
mechanism = { filter_properties... }
barrel, bucket, chain, anvil, screw, pipe
}
Returns: the created building, or 'nil, error'
--]]
function buildings.constructBuilding(info)
local btype = info.type
local subtype = info.subtype or -1
local custom = info.custom or -1
local filters = info.filters
if not (info.pos or info.x) then
error('position is required')
end
if not (info.abstract or info.items or filters) then
filters = buildings.getFiltersByType(info,btype,subtype,custom)
if not filters then
error('one of items, filters or abstract is required')
end
elseif filters then
for _,v in ipairs(filters) do
v.new = true
end
end
if type(btype) ~= 'number' or not df.building_type[btype] then
error('Invalid building type: '..tostring(btype))
end
local pos = info.pos or xyz2pos(info.x, info.y, info.z)
local instance = buildings.allocInstance(pos, btype, subtype, custom)
if not instance then
error('Could not create building of type '..df.building_type[btype])
end
local to_delete = instance
return dfhack.with_finalize(
function()
df.delete(to_delete)
end,
function()
if info.fields then
instance:assign(info.fields)
end
local ok,w,h,area,r_area = buildings.setSize(
instance,info.width,info.height,info.direction
)
if not ok then
return nil, "cannot place at this position"
end
if info.full_rectangle and area ~= r_area then
return nil, "not all tiles can be used"
end
if info.abstract then
ok = buildings.constructAbstract(instance)
elseif info.items then
ok = buildings.constructWithItems(instance, info.items)
else
ok = buildings.constructWithFilters(instance, filters)
end
if not ok then
return nil, "could not construct the building"
end
-- Success
to_delete = nil
return instance
end
)
end
return buildings

@ -0,0 +1,139 @@
local _ENV = mkmodule('utils')
-- Comparator function
function compare(a,b)
if a < b then
return -1
elseif a > b then
return 1
else
return 0
end
end
-- Sort strings; compare empty last
function compare_name(a,b)
if a == '' then
if b == '' then
return 0
else
return 1
end
elseif b == '' then
return -1
else
return compare(a,b)
end
end
--[[
Sort items in data according to ordering.
Each ordering spec is a table with possible fields:
* key = function(value)
Computes comparison key from a data value. Not called on nil.
* key_table = function(data)
Computes a key table from the data table in one go.
* compare = function(a,b)
Comparison function. Defaults to compare above.
Called on non-nil keys; nil sorts last.
* nil_first
If true, nil keys are sorted first instead of last.
* reverse
If true, sort non-nil keys in descending order.
Returns a table of integer indices into data.
--]]
function make_sort_order(data,ordering)
-- Compute sort keys and comparators
local keys = {}
local cmps = {}
local size = data.n or #data
for i=1,#ordering do
local order = ordering[i]
if order.key_table then
keys[i] = order.key_table(data)
elseif order.key then
local kt = {}
local kf = order.key
for j=1,size do
if data[j] == nil then
kt[j] = nil
else
kt[j] = kf(data[j])
end
end
keys[i] = kt
else
keys[i] = data
end
cmps[i] = order.compare or compare
end
-- Make an order table
local index = {}
for i=1,size do
index[i] = i
end
-- Sort the ordering table
table.sort(index, function(ia,ib)
for i=1,#keys do
local ka = keys[i][ia]
local kb = keys[i][ib]
-- Sort nil keys to the end
if ka == nil then
if kb ~= nil then
return ordering[i].nil_first
end
elseif kb == nil then
return not ordering[i].nil_first
else
local cmpv = cmps[i](ka,kb)
if ordering[i].reverse then
cmpv = -cmpv
end
if cmpv < 0 then
return true
elseif cmpv > 0 then
return false
end
end
end
return ia < ib -- this should ensure stable sort
end)
return index
end
--[[
Recursively assign data into a table.
--]]
function assign(tgt,src)
if df.isvalid(tgt) == 'ref' then
df.assign(tgt, src)
elseif type(tgt) == 'table' then
for k,v in pairs(src) do
if type(v) == 'table' or df.isvalid(v) == 'ref' then
local cv = tgt[k]
if cv == nil then
cv = {}
tgt[k] = cv
end
assign(cv, v)
else
tgt[k] = v
end
end
else
error('Invalid assign target type: '..tostring(tgt))
end
return tgt
end
return _ENV

@ -35,19 +35,57 @@ using namespace std;
#include "Types.h"
#include "Error.h"
#include "modules/Buildings.h"
#include "modules/Maps.h"
#include "modules/Job.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "TileTypes.h"
#include "MiscUtils.h"
using namespace DFHack;
#include "DataDefs.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/ui_look_list.h"
#include "df/d_init.h"
#include "df/item.h"
#include "df/job.h"
#include "df/job_item.h"
#include "df/general_ref_building_holderst.h"
#include "df/buildings_other_id.h"
#include "df/building_design.h"
#include "df/building_def.h"
#include "df/building_axle_horizontalst.h"
#include "df/building_trapst.h"
#include "df/building_bridgest.h"
#include "df/building_coffinst.h"
#include "df/building_civzonest.h"
#include "df/building_stockpilest.h"
#include "df/building_furnacest.h"
#include "df/building_workshopst.h"
#include "df/building_screw_pumpst.h"
#include "df/building_water_wheelst.h"
#include "df/building_wellst.h"
using namespace df::enums;
using df::global::ui;
using df::global::world;
using df::global::d_init;
using df::global::building_next_id;
using df::global::process_jobs;
using df::building_def;
static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile)
{
if (!extent.extents)
return NULL;
int dx = tile.x - extent.x;
int dy = tile.y - extent.y;
if (dx < 0 || dy < 0 || dx >= extent.width || dy >= extent.height)
return NULL;
return &extent.extents[dx + dy*extent.width];
}
uint32_t Buildings::getNumBuildings()
{
return world->buildings.all.size();
@ -86,3 +124,836 @@ bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes)
return true;
}
df::building *Buildings::findAtTile(df::coord pos)
{
auto occ = Maps::getTileOccupancy(pos);
if (!occ || !occ->bits.building)
return NULL;
auto &vec = df::building::get_vector();
for (size_t i = 0; i < vec.size(); i++)
{
auto bld = vec[i];
if (pos.z != bld->z ||
pos.x < bld->x1 || pos.x > bld->x2 ||
pos.y < bld->y1 || pos.y > bld->y2)
continue;
if (!bld->isSettingOccupancy())
continue;
if (bld->room.extents && bld->isExtentShaped())
{
auto etile = getExtentTile(bld->room, pos);
if (!etile || !*etile)
continue;
}
return bld;
}
return NULL;
}
bool Buildings::findCivzonesAt(std::vector<df::building_civzonest*> *pvec, df::coord pos)
{
pvec->clear();
auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE];
for (size_t i = 0; i < vec.size(); i++)
{
auto bld = strict_virtual_cast<df::building_civzonest>(vec[i]);
if (!bld || bld->z != pos.z || !containsTile(bld, pos))
continue;
pvec->push_back(bld);
}
return !pvec->empty();
}
df::building *Buildings::allocInstance(df::coord pos, df::building_type type, int subtype, int custom)
{
if (!building_next_id)
return NULL;
// Allocate object
const char *classname = ENUM_ATTR(building_type, classname, type);
if (!classname)
return NULL;
auto id = virtual_identity::find(classname);
if (!id)
return NULL;
df::building *bld = (df::building*)id->allocate();
if (!bld)
return NULL;
// Init base fields
bld->x1 = bld->x2 = bld->centerx = pos.x;
bld->y1 = bld->y2 = bld->centery = pos.y;
bld->z = pos.z;
bld->race = ui->race_id;
if (subtype != -1)
bld->setSubtype(subtype);
if (custom != -1)
bld->setCustomType(custom);
bld->setMaterialAmount(1);
// Type specific init
switch (type)
{
case building_type::Well:
{
auto obj = (df::building_wellst*)bld;
obj->bucket_z = bld->z;
break;
}
case building_type::Furnace:
{
auto obj = (df::building_furnacest*)bld;
obj->melt_remainder.resize(df::inorganic_raw::get_vector().size(), 0);
break;
}
case building_type::Coffin:
{
auto obj = (df::building_coffinst*)bld;
obj->initBurialFlags(); // DF has this copy&pasted
break;
}
case building_type::Trap:
{
auto obj = (df::building_trapst*)bld;
if (obj->trap_type == trap_type::PressurePlate)
obj->unk_cc = 500;
break;
}
default:
break;
}
return bld;
}
static void makeOneDim(df::coord2d &size, df::coord2d &center, bool vertical)
{
if (vertical)
size.x = 1;
else
size.y = 1;
center = size/2;
}
bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d &center,
df::building_type type, int subtype, int custom, int direction)
{
using namespace df::enums::building_type;
if (size.x <= 0)
size.x = 1;
if (size.y <= 0)
size.y = 1;
switch (type)
{
case FarmPlot:
case Bridge:
case RoadDirt:
case RoadPaved:
case Stockpile:
case Civzone:
center = size/2;
return true;
case TradeDepot:
case Shop:
size = df::coord2d(5,5);
center = df::coord2d(2,2);
return false;
case SiegeEngine:
case Windmill:
case Wagon:
size = df::coord2d(3,3);
center = df::coord2d(1,1);
return false;
case AxleHorizontal:
makeOneDim(size, center, direction);
return true;
case WaterWheel:
size = df::coord2d(3,3);
makeOneDim(size, center, direction);
return false;
case Workshop:
{
using namespace df::enums::workshop_type;
switch ((df::workshop_type)subtype)
{
case Quern:
case Millstone:
case Tool:
size = df::coord2d(1,1);
center = df::coord2d(0,0);
break;
case Siege:
case Kennels:
size = df::coord2d(5,5);
center = df::coord2d(2,2);
break;
case Custom:
if (auto def = df::building_def::find(custom))
{
size = df::coord2d(def->dim_x, def->dim_y);
center = df::coord2d(def->workloc_x, def->workloc_y);
break;
}
default:
size = df::coord2d(3,3);
center = df::coord2d(1,1);
}
return false;
}
case Furnace:
{
using namespace df::enums::furnace_type;
switch ((df::furnace_type)subtype)
{
case Custom:
if (auto def = df::building_def::find(custom))
{
size = df::coord2d(def->dim_x, def->dim_y);
center = df::coord2d(def->workloc_x, def->workloc_y);
break;
}
default:
size = df::coord2d(3,3);
center = df::coord2d(1,1);
}
return false;
}
case ScrewPump:
{
using namespace df::enums::screw_pump_direction;
switch ((df::screw_pump_direction)direction)
{
case FromEast:
size = df::coord2d(2,1);
center = df::coord2d(1,0);
break;
case FromSouth:
size = df::coord2d(1,2);
center = df::coord2d(0,1);
break;
case FromWest:
size = df::coord2d(2,1);
center = df::coord2d(0,0);
break;
default:
size = df::coord2d(1,2);
center = df::coord2d(0,0);
}
return false;
}
default:
size = df::coord2d(1,1);
center = df::coord2d(0,0);
return false;
}
}
bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size,
df::building_extents *ext,
bool create_ext, bool allow_occupied)
{
bool found_any = false;
for (int dx = 0; dx < size.x; dx++)
{
for (int dy = 0; dy < size.y; dy++)
{
df::coord tile = pos + df::coord(dx,dy,0);
uint8_t *etile = NULL;
// Exclude using extents
if (ext && ext->extents)
{
etile = getExtentTile(*ext, tile);
if (!etile || !*etile)
continue;
}
// Look up map block
df::map_block *block = Maps::getTileBlock(tile);
if (!block)
return false;
df::coord2d btile = df::coord2d(tile) & 15;
bool allowed = true;
// Check occupancy and tile type
if (!allow_occupied &&
block->occupancy[btile.x][btile.y].bits.building)
allowed = false;
else
{
auto tile = block->tiletype[btile.x][btile.y];
if (!HighPassable(tile))
allowed = false;
}
// Create extents if requested
if (allowed)
found_any = true;
else
{
if (!ext || !create_ext)
return false;
if (!ext->extents)
{
ext->extents = new uint8_t[size.x*size.y];
ext->x = pos.x;
ext->y = pos.y;
ext->width = size.x;
ext->height = size.y;
memset(ext->extents, 1, size.x*size.y);
etile = getExtentTile(*ext, tile);
}
if (!etile)
return false;
*etile = 0;
}
}
}
return found_any;
}
std::pair<df::coord,df::coord2d> Buildings::getSize(df::building *bld)
{
CHECK_NULL_POINTER(bld);
df::coord pos(bld->x1,bld->y1,bld->z);
return std::pair<df::coord,df::coord2d>(pos, df::coord2d(bld->x2+1,bld->y2+1) - pos);
}
static bool checkBuildingTiles(df::building *bld, bool can_change)
{
auto psize = Buildings::getSize(bld);
return Buildings::checkFreeTiles(psize.first, psize.second, &bld->room,
can_change && bld->isExtentShaped(),
!bld->isSettingOccupancy());
}
int Buildings::countExtentTiles(df::building_extents *ext, int defval)
{
if (!ext || !ext->extents)
return defval;
int cnt = 0;
for (int i = 0; i < ext->width * ext->height; i++)
if (ext->extents[i])
cnt++;
return cnt;
}
bool Buildings::containsTile(df::building *bld, df::coord2d tile, bool room)
{
CHECK_NULL_POINTER(bld);
if (room)
{
if (!bld->is_room || !bld->room.extents)
return false;
}
else
{
if (tile.x < bld->x1 || tile.x > bld->x2 || tile.y < bld->y1 || tile.y >= bld->y2)
return false;
}
if (bld->room.extents && (room || bld->isExtentShaped()))
{
uint8_t *etile = getExtentTile(bld->room, tile);
if (!etile || !*etile)
return false;
}
return true;
}
bool Buildings::hasSupport(df::coord pos, df::coord2d size)
{
for (int dx = -1; dx <= size.x; dx++)
{
for (int dy = -1; dy <= size.y; dy++)
{
// skip corners
if ((dx < 0 || dx == size.x) && (dy < 0 || dy == size.y))
continue;
df::coord tile = pos + df::coord(dx,dy,0);
df::map_block *block = Maps::getTileBlock(tile);
if (!block)
continue;
df::coord2d btile = df::coord2d(tile) & 15;
if (!isOpenTerrain(block->tiletype[btile.x][btile.y]))
return true;
}
}
return false;
}
static int computeMaterialAmount(df::building *bld)
{
auto size = Buildings::getSize(bld).second;
int cnt = size.x * size.y;
if (bld->room.extents && bld->isExtentShaped())
cnt = Buildings::countExtentTiles(&bld->room, cnt);
return cnt/4 + 1;
}
bool Buildings::setSize(df::building *bld, df::coord2d size, int direction)
{
CHECK_NULL_POINTER(bld);
CHECK_INVALID_ARGUMENT(bld->id == -1);
// Delete old extents
if (bld->room.extents)
{
delete[] bld->room.extents;
bld->room.extents = NULL;
}
// Compute correct size and apply it
df::coord2d center;
getCorrectSize(size, center, bld->getType(), bld->getSubtype(),
bld->getCustomType(), direction);
bld->x2 = bld->x1 + size.x - 1;
bld->y2 = bld->y1 + size.y - 1;
bld->centerx = bld->x1 + center.x;
bld->centery = bld->y1 + center.y;
auto type = bld->getType();
using namespace df::enums::building_type;
switch (type)
{
case WaterWheel:
{
auto obj = (df::building_water_wheelst*)bld;
obj->is_vertical = !!direction;
break;
}
case AxleHorizontal:
{
auto obj = (df::building_axle_horizontalst*)bld;
obj->is_vertical = !!direction;
break;
}
case ScrewPump:
{
auto obj = (df::building_screw_pumpst*)bld;
obj->direction = (df::screw_pump_direction)direction;
break;
}
case Bridge:
{
auto obj = (df::building_bridgest*)bld;
auto psize = getSize(bld);
obj->gate_flags.bits.has_support = hasSupport(psize.first, psize.second);
obj->direction = (df::building_bridgest::T_direction)direction;
break;
}
default:
break;
}
bool ok = checkBuildingTiles(bld, true);
if (type != Construction)
bld->setMaterialAmount(computeMaterialAmount(bld));
return ok;
}
static void markBuildingTiles(df::building *bld, bool remove)
{
bool use_extents = bld->room.extents && bld->isExtentShaped();
bool stockpile = (bld->getType() == building_type::Stockpile);
bool complete = (bld->getBuildStage() >= bld->getMaxBuildStage());
if (remove)
stockpile = complete = false;
for (int tx = bld->x1; tx <= bld->x2; tx++)
{
for (int ty = bld->y1; ty <= bld->y2; ty++)
{
df::coord tile(tx,ty,bld->z);
if (use_extents)
{
uint8_t *etile = getExtentTile(bld->room, tile);
if (!etile || !*etile)
continue;
}
df::map_block *block = Maps::getTileBlock(tile);
df::coord2d btile = df::coord2d(tile) & 15;
auto &des = block->designation[btile.x][btile.y];
des.bits.pile = stockpile;
if (!remove)
des.bits.dig = tile_dig_designation::No;
if (complete)
bld->updateOccupancy(tx, ty);
else
{
auto &occ = block->occupancy[btile.x][btile.y];
if (remove)
occ.bits.building = tile_building_occ::None;
else
occ.bits.building = tile_building_occ::Planned;
}
}
}
}
static void linkRooms(df::building *bld)
{
auto &vec = world->buildings.other[buildings_other_id::ANY_FREE];
bool changed = false;
for (size_t i = 0; i < vec.size(); i++)
{
auto room = vec[i];
if (!room->is_room || room->z != bld->z)
continue;
uint8_t *pext = getExtentTile(room->room, df::coord2d(bld->x1, bld->y1));
if (!pext || !*pext)
continue;
changed = true;
room->children.push_back(bld);
bld->parents.push_back(room);
// TODO: the game updates room rent here if economy is enabled
}
if (changed)
df::global::ui->equipment.update.bits.buildings = true;
}
static void unlinkRooms(df::building *bld)
{
for (size_t i = 0; i < bld->parents.size(); i++)
{
auto parent = bld->parents[i];
int idx = linear_index(parent->children, bld);
vector_erase_at(parent->children, idx);
}
bld->parents.clear();
}
static void linkBuilding(df::building *bld)
{
bld->id = (*building_next_id)++;
world->buildings.all.push_back(bld);
bld->categorize(true);
if (bld->isSettingOccupancy())
markBuildingTiles(bld, false);
linkRooms(bld);
if (process_jobs)
*process_jobs = true;
}
static void createDesign(df::building *bld, bool rough)
{
auto job = bld->jobs[0];
job->mat_type = bld->mat_type;
job->mat_index = bld->mat_index;
if (bld->needsDesign())
{
auto act = (df::building_actual*)bld;
act->design = new df::building_design();
act->design->flags.bits.rough = rough;
}
}
static int getMaxStockpileId()
{
auto &vec = world->buildings.other[buildings_other_id::STOCKPILE];
int max_id = 0;
for (size_t i = 0; i < vec.size(); i++)
{
auto bld = strict_virtual_cast<df::building_stockpilest>(vec[i]);
if (bld)
max_id = std::max(max_id, bld->stockpile_number);
}
return max_id;
}
bool Buildings::constructAbstract(df::building *bld)
{
CHECK_NULL_POINTER(bld);
CHECK_INVALID_ARGUMENT(bld->id == -1);
CHECK_INVALID_ARGUMENT(!bld->isActual());
if (!checkBuildingTiles(bld, false))
return false;
switch (bld->getType())
{
case building_type::Stockpile:
if (auto stock = strict_virtual_cast<df::building_stockpilest>(bld))
stock->stockpile_number = getMaxStockpileId() + 1;
break;
default:
break;
}
linkBuilding(bld);
if (!bld->flags.bits.exists)
{
bld->flags.bits.exists = true;
bld->initFarmSeasons();
}
return true;
}
static bool linkForConstruct(df::job* &job, df::building *bld)
{
if (!checkBuildingTiles(bld, false))
return false;
auto ref = df::allocate<df::general_ref_building_holderst>();
if (!ref)
{
Core::printerr("Could not allocate general_ref_building_holderst\n");
return false;
}
linkBuilding(bld);
ref->building_id = bld->id;
job = new df::job();
job->job_type = df::job_type::ConstructBuilding;
job->pos = df::coord(bld->centerx, bld->centery, bld->z);
job->references.push_back(ref);
bld->jobs.push_back(job);
Job::linkIntoWorld(job);
return true;
}
static bool needsItems(df::building *bld)
{
if (!bld->isActual())
return false;
switch (bld->getType())
{
case building_type::FarmPlot:
case building_type::RoadDirt:
return false;
default:
return true;
}
}
bool Buildings::constructWithItems(df::building *bld, std::vector<df::item*> items)
{
CHECK_NULL_POINTER(bld);
CHECK_INVALID_ARGUMENT(bld->id == -1);
CHECK_INVALID_ARGUMENT(bld->isActual());
CHECK_INVALID_ARGUMENT(!items.empty() == needsItems(bld));
for (size_t i = 0; i < items.size(); i++)
{
CHECK_NULL_POINTER(items[i]);
if (items[i]->flags.bits.in_job)
return false;
}
df::job *job = NULL;
if (!linkForConstruct(job, bld))
return false;
bool rough = false;
for (size_t i = 0; i < items.size(); i++)
{
Job::attachJobItem(job, items[i], df::job_item_ref::Hauled);
if (items[i]->getType() == item_type::BOULDER)
rough = true;
if (bld->mat_type == -1)
bld->mat_type = items[i]->getMaterial();
if (bld->mat_index == -1)
bld->mat_index = items[i]->getMaterialIndex();
}
createDesign(bld, rough);
return true;
}
bool Buildings::constructWithFilters(df::building *bld, std::vector<df::job_item*> items)
{
CHECK_NULL_POINTER(bld);
CHECK_INVALID_ARGUMENT(bld->id == -1);
CHECK_INVALID_ARGUMENT(bld->isActual());
CHECK_INVALID_ARGUMENT(!items.empty() == needsItems(bld));
for (size_t i = 0; i < items.size(); i++)
CHECK_NULL_POINTER(items[i]);
df::job *job = NULL;
if (!linkForConstruct(job, bld))
{
for (size_t i = 0; i < items.size(); i++)
delete items[i];
return false;
}
bool rough = false;
for (size_t i = 0; i < items.size(); i++)
{
if (items[i]->quantity < 0)
items[i]->quantity = computeMaterialAmount(bld);
job->job_items.push_back(items[i]);
if (items[i]->item_type == item_type::BOULDER)
rough = true;
if (bld->mat_type == -1)
bld->mat_type = items[i]->mat_type;
if (bld->mat_index == -1)
bld->mat_index = items[i]->mat_index;
}
createDesign(bld, rough);
return true;
}
bool Buildings::deconstruct(df::building *bld)
{
using df::global::ui;
using df::global::world;
using df::global::process_jobs;
using df::global::process_dig;
using df::global::ui_look_list;
CHECK_NULL_POINTER(bld);
if (bld->isActual() && bld->getBuildStage() > 0)
{
bld->queueDestroy();
return false;
}
/* Immediate destruction code path.
Should only happen for abstract and unconstructed buildings.*/
if (bld->isSettingOccupancy())
{
markBuildingTiles(bld, true);
bld->cleanupMap();
}
bld->removeUses(false, false);
// Assume: no parties.
unlinkRooms(bld);
// Assume: not unit destroy target
vector_erase_at(ui->unk8.unk10, linear_index(ui->unk8.unk10, bld));
// Assume: not used in punishment
// Assume: not used in non-own jobs
// Assume: does not affect pathfinding
bld->deconstructItems(false, false);
// Don't clear arrows.
bld->uncategorize();
delete bld;
if (world->selected_building == bld)
{
world->selected_building = NULL;
world->update_selected_building = true;
}
for (int i = ui_look_list->items.size()-1; i >= 0; i--)
{
auto item = ui_look_list->items[i];
if (item->type == df::ui_look_list::T_items::Building &&
item->building == bld)
{
vector_erase_at(ui_look_list->items, i);
delete item;
}
}
if (process_dig) *process_dig = true;
if (process_jobs) *process_jobs = true;
return true;
}

@ -0,0 +1,278 @@
/*
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 <vector>
#include <cstdlib>
using namespace std;
#include "Error.h"
#include "Core.h"
#include "modules/Burrows.h"
#include "modules/Maps.h"
#include "modules/Units.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/burrow.h"
#include "df/block_burrow.h"
#include "df/block_burrow_link.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
df::burrow *Burrows::findByName(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 Burrows::clearUnits(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 Burrows::isAssignedUnit(df::burrow *burrow, df::unit *unit)
{
CHECK_NULL_POINTER(unit);
CHECK_NULL_POINTER(burrow);
return binsearch_index(unit->burrows, burrow->id) >= 0;
}
void Burrows::setAssignedUnit(df::burrow *burrow, df::unit *unit, 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;
}
}
void Burrows::listBlocks(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 = Maps::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 Burrows::clearTiles(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 = Maps::getBlock(pos - base);
if (!block)
continue;
destroyBurrowMask(getBlockMask(burrow, block));
}
burrow->block_x.clear();
burrow->block_y.clear();
burrow->block_z.clear();
}
df::block_burrow *Burrows::getBlockMask(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;
link->item->tile_bitmask.clear();
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 Burrows::deleteBlockMask(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 Burrows::isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile)
{
CHECK_NULL_POINTER(burrow);
if (!block) return false;
auto mask = getBlockMask(burrow, block);
return mask ? mask->getassignment(tile & 15) : false;
}
bool Burrows::setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable)
{
CHECK_NULL_POINTER(burrow);
if (!block) return false;
auto mask = getBlockMask(burrow, block, enable);
if (mask)
{
mask->setassignment(tile & 15, enable);
if (!enable && !mask->has_assignments())
deleteBlockMask(burrow, block, mask);
}
return true;
}

@ -35,9 +35,20 @@ using namespace std;
#include "MemAccess.h"
#include "Types.h"
#include "Core.h"
#include "modules/Constructions.h"
#include "modules/Buildings.h"
#include "modules/Maps.h"
#include "TileTypes.h"
#include "df/world.h"
#include "df/job_item.h"
#include "df/building_type.h"
#include "df/building_constructionst.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
bool Constructions::isValid()
@ -52,14 +63,14 @@ uint32_t Constructions::getCount()
df::construction * Constructions::getConstruction(const int32_t index)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return NULL;
return world->constructions[index];
}
bool Constructions::copyConstruction(const int32_t index, t_construction &out)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return false;
out.origin = world->constructions[index];
@ -73,3 +84,88 @@ bool Constructions::copyConstruction(const int32_t index, t_construction &out)
out.original_tile = out.origin->original_tile;
return true;
}
bool Constructions::designateNew(df::coord pos, df::construction_type type,
df::item_type item, int mat_index)
{
auto ttype = Maps::getTileType(pos);
if (!ttype || tileMaterial(*ttype) == tiletype_material::CONSTRUCTION)
return false;
auto current = Buildings::findAtTile(pos);
if (current)
{
auto cons = strict_virtual_cast<df::building_constructionst>(current);
if (!cons)
return false;
cons->type = type;
return true;
}
auto newinst = Buildings::allocInstance(pos, building_type::Construction);
if (!newinst)
return false;
auto newcons = strict_virtual_cast<df::building_constructionst>(newinst);
newcons->type = type;
df::job_item *filter = new df::job_item();
filter->item_type = item;
filter->mat_index = mat_index;
filter->flags2.bits.building_material = true;
if (mat_index < 0)
filter->flags2.bits.non_economic = true;
std::vector<df::job_item*> filters;
filters.push_back(filter);
if (!Buildings::constructWithFilters(newinst, filters))
{
delete newinst;
return false;
}
return true;
}
bool Constructions::designateRemove(df::coord pos, bool *immediate)
{
using df::global::process_dig;
if (immediate)
*immediate = false;
if (auto current = Buildings::findAtTile(pos))
{
auto cons = strict_virtual_cast<df::building_constructionst>(current);
if (!cons)
return false;
if (Buildings::deconstruct(cons))
{
if (immediate)
*immediate = true;
}
return true;
}
auto block = Maps::getTileBlock(pos);
if (!block)
return false;
auto ttype = block->tiletype[pos.x&15][pos.y&15];
if (tileMaterial(ttype) == tiletype_material::CONSTRUCTION)
{
auto &dsgn = block->designation[pos.x&15][pos.y&15];
dsgn.bits.dig = tile_dig_designation::Default;
block->flags.bits.designated = true;
if (process_dig)
*process_dig = true;
return true;
}
return false;
}

@ -53,14 +53,14 @@ uint32_t Engravings::getCount()
df::engraving * Engravings::getEngraving(int index)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return NULL;
return world->engravings[index];
}
bool Engravings::copyEngraving(const int32_t index, t_engraving &out)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return false;
out.origin = world->engravings[index];

@ -50,6 +50,11 @@ using namespace DFHack;
#include "df/viewscreen_joblistst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_itemst.h"
#include "df/viewscreen_layerst.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_petst.h"
#include "df/ui_unit_view_mode.h"
#include "df/ui_sidebar_menus.h"
#include "df/ui_look_list.h"
@ -63,12 +68,18 @@ using namespace DFHack;
#include "df/popup_message.h"
#include "df/interfacest.h"
#include "df/graphic.h"
#include "df/layer_object_listst.h"
using namespace df::enums;
using df::global::gview;
using df::global::init;
using df::global::gps;
static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx)
{
return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx));
}
// Predefined common guard functions
bool Gui::default_hotkey(df::viewscreen *top)
@ -194,7 +205,7 @@ bool Gui::view_unit_hotkey(df::viewscreen *top)
if (!ui_selected_unit) // allow missing
return false;
return vector_get(world->units.other[0], *ui_selected_unit) != NULL;
return vector_get(world->units.active, *ui_selected_unit) != NULL;
}
bool Gui::unit_inventory_hotkey(df::viewscreen *top)
@ -223,7 +234,7 @@ df::job *Gui::getSelectedWorkshopJob(color_ostream &out, bool quiet)
df::building *selected = world->selected_building;
int idx = *ui_workshop_job_cursor;
if (idx < 0 || idx >= selected->jobs.size())
if (size_t(idx) >= selected->jobs.size())
{
out.printerr("Invalid job cursor index: %d\n", idx);
return NULL;
@ -294,6 +305,62 @@ static df::unit *getAnyUnit(df::viewscreen *top)
return ref ? ref->getUnit() : NULL;
}
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_workshop_profilest, top))
{
if (auto list1 = getLayerList(screen, 0))
return vector_get(screen->workers, list1->cursor);
return NULL;
}
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_noblelistst, top))
{
switch (screen->mode)
{
case df::viewscreen_layer_noblelistst::List:
if (auto list1 = getLayerList(screen, 0))
{
if (auto info = vector_get(screen->info, list1->cursor))
return info->unit;
}
return NULL;
case df::viewscreen_layer_noblelistst::Appoint:
if (auto list2 = getLayerList(screen, 1))
{
if (auto info = vector_get(screen->candidates, list2->cursor))
return info->unit;
}
return NULL;
default:
return NULL;
}
}
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_petst, top))
{
switch (screen->mode)
{
case df::viewscreen_petst::List:
if (!vector_get(screen->is_vermin, screen->cursor))
return (df::unit*)vector_get(screen->animal, screen->cursor);
return NULL;
case df::viewscreen_petst::SelectTrainer:
return vector_get(screen->trainer_unit, screen->trainer_cursor);
default:
return NULL;
}
}
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_overall_healthst, top))
{
if (auto list1 = getLayerList(screen, 0))
return vector_get(screen->unit, list1->cursor);
return NULL;
}
if (!Gui::dwarfmode_hotkey(top))
return NULL;
@ -303,7 +370,7 @@ static df::unit *getAnyUnit(df::viewscreen *top)
if (!ui_selected_unit)
return NULL;
return vector_get(world->units.other[0], *ui_selected_unit);
return vector_get(world->units.active, *ui_selected_unit);
}
case LookAround:
{

@ -421,18 +421,25 @@ bool Items::copyItem(df::item * itembase, DFHack::dfh_item &item)
return true;
}
df::unit *Items::getOwner(df::item * item)
df::general_ref *Items::getGeneralRef(df::item *item, df::general_ref_type type)
{
CHECK_NULL_POINTER(item);
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
return ref->getUnit();
}
return findRef(item->itemrefs, type);
}
df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type)
{
CHECK_NULL_POINTER(item);
return findRef(item->specific_refs, type);
}
df::unit *Items::getOwner(df::item * item)
{
auto ref = getGeneralRef(item, general_ref_type::UNIT_ITEMOWNER);
return NULL;
return ref ? ref->getUnit() : NULL;
}
bool Items::setOwner(df::item *item, df::unit *unit)
@ -478,16 +485,9 @@ bool Items::setOwner(df::item *item, df::unit *unit)
df::item *Items::getContainer(df::item * item)
{
CHECK_NULL_POINTER(item);
auto ref = getGeneralRef(item, general_ref_type::CONTAINED_IN_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;
return ref ? ref->getItem() : NULL;
}
void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
@ -536,6 +536,9 @@ df::coord Items::getPosition(df::item *item)
if (auto bld = ref->getBuilding())
return df::coord(bld->centerx, bld->centery, bld->z);
break;
default:
break;
}
}
}
@ -543,19 +546,6 @@ df::coord Items::getPosition(df::item *item)
return item->pos;
}
static void removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id)
{
for (int i = vec.size()-1; i >= 0; i--)
{
df::general_ref *ref = vec[i];
if (ref->getType() != type || ref->getID() != id)
continue;
vector_erase_at(vec, i);
delete ref;
}
}
static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{
if (item->flags.bits.on_ground)

@ -46,6 +46,7 @@ using namespace std;
#include "df/job.h"
#include "df/job_item.h"
#include "df/job_list_link.h"
#include "df/specific_ref.h"
#include "df/general_ref.h"
#include "df/general_ref_unit_workerst.h"
#include "df/general_ref_building_holderst.h"
@ -67,7 +68,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job)
pnew->list_link = NULL;
pnew->completion_timer = -1;
pnew->items.clear();
pnew->misc_links.clear();
pnew->specific_refs.clear();
// Clone refs
for (int i = pnew->references.size()-1; i >= 0; i--)
@ -93,7 +94,7 @@ void DFHack::Job::deleteJobStruct(df::job *job)
return;
// Only allow free-floating job structs
assert(!job->list_link && job->items.empty() && job->misc_links.empty());
assert(!job->list_link && job->items.empty() && job->specific_refs.empty());
for (int i = job->references.size()-1; i >= 0; i--)
delete job->references[i];
@ -311,3 +312,40 @@ bool DFHack::Job::listNewlyCreated(std::vector<df::job*> *pvec, int *id_var)
return true;
}
bool DFHack::Job::attachJobItem(df::job *job, df::item *item,
df::job_item_ref::T_role role,
int filter_idx, int insert_idx)
{
CHECK_NULL_POINTER(job);
CHECK_NULL_POINTER(item);
/*
* Functionality 100% reverse-engineered from DF code.
*/
if (role != df::job_item_ref::TargetContainer)
{
if (item->flags.bits.in_job)
return false;
item->flags.bits.in_job = true;
}
auto item_link = new df::specific_ref();
item_link->type = specific_ref_type::JOB;
item_link->job = job;
item->specific_refs.push_back(item_link);
auto job_link = new df::job_item_ref();
job_link->item = item;
job_link->role = role;
job_link->job_item_idx = filter_idx;
if (size_t(insert_idx) < job->items.size())
vector_insert_at(job->items, insert_idx, job_link);
else
job->items.push_back(job_link);
return true;
}

@ -145,6 +145,24 @@ df::map_block *Maps::getTileBlock (int32_t x, int32_t y, int32_t z)
return world->map.block_index[x >> 4][y >> 4][z];
}
df::tiletype *Maps::getTileType(int32_t x, int32_t y, int32_t z)
{
df::map_block *block = getTileBlock(x,y,z);
return block ? &block->tiletype[x&15][y&15] : NULL;
}
df::tile_designation *Maps::getTileDesignation(int32_t x, int32_t y, int32_t z)
{
df::map_block *block = getTileBlock(x,y,z);
return block ? &block->designation[x&15][y&15] : NULL;
}
df::tile_occupancy *Maps::getTileOccupancy(int32_t x, int32_t y, int32_t z)
{
df::map_block *block = getTileBlock(x,y,z);
return block ? &block->occupancy[x&15][y&15] : NULL;
}
df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos)
{
auto data = world->world_data;
@ -387,6 +405,20 @@ bool Maps::ReadGeology(vector<vector<int16_t> > *layer_mats, vector<df::coord2d>
return true;
}
bool Maps::canWalkBetween(df::coord pos1, df::coord pos2)
{
auto block1 = getTileBlock(pos1);
auto block2 = getTileBlock(pos2);
if (!block1 || !block2)
return false;
auto tile1 = MapExtras::index_tile<uint16_t>(block1->walkable, pos1);
auto tile2 = MapExtras::index_tile<uint16_t>(block2->walkable, pos2);
return tile1 && tile1 == tile2;
}
#define COPY(a,b) memcpy(&a,&b,sizeof(a))
MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
@ -676,11 +708,47 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos
int x = pos.x, y = pos.y;
switch (tileMaterial(tt)) {
case CONSTRUCTION: // just a fallback
case NONE:
case AIR:
rv.mat_type = -1;
break;
case DRIFTWOOD:
case SOIL:
{
rv.mat_index = basemats[x][y];
if (auto raw = df::inorganic_raw::find(rv.mat_index))
{
if (raw->flags.is_set(inorganic_flags::SOIL_ANY))
break;
int biome = mblock->biomeIndexAt(pos);
int idx = vector_get(parent->default_soil, biome, -1);
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
case STONE:
{
rv.mat_index = basemats[x][y];
if (auto raw = df::inorganic_raw::find(rv.mat_index))
{
if (!raw->flags.is_set(inorganic_flags::SOIL_ANY))
break;
int biome = mblock->biomeIndexAt(pos);
int idx = vector_get(parent->default_stone, biome, -1);
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
case MINERAL:
rv.mat_index = veinmats[x][y];
@ -727,11 +795,20 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos
break;
}
case CONSTRUCTION: // just a fallback
case MAGMA:
case HFS:
// use generic 'rock'
break;
case FROZEN_LIQUID:
rv.mat_type = builtin_mats::WATER;
break;
case POOL:
case BROOK:
case RIVER:
rv.mat_type = builtin_mats::WATER;
rv.mat_index = basemats[x][y];
break;
case ASHES:
@ -739,9 +816,6 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos
case CAMPFIRE:
rv.mat_type = builtin_mats::ASH;
break;
default:
rv.mat_type = -1;
}
return rv;
@ -816,18 +890,30 @@ void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &mate
}
}
df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p)
int MapExtras::Block::biomeIndexAt(df::coord2d p)
{
if (!block)
return df::coord2d(-30000,-30000);
return -1;
auto des = index_tile<df::tile_designation>(designation,p);
uint8_t idx = des.bits.biome;
if (idx >= 9)
return block->region_pos;
return -1;
idx = block->region_offset[idx];
if (idx >= parent->geoidx.size())
return -1;
return idx;
}
df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p)
{
if (!block)
return df::coord2d(-30000,-30000);
int idx = biomeIndexAt(p);
if (idx < 0)
return block->region_pos;
return parent->geoidx[idx];
}
@ -947,6 +1033,28 @@ MapExtras::MapCache::MapCache()
region_details[info->pos] = info;
}
}
default_soil.resize(layer_mats.size());
default_stone.resize(layer_mats.size());
for (size_t i = 0; i < layer_mats.size(); i++)
{
default_soil[i] = -1;
default_stone[i] = -1;
for (size_t j = 0; j < layer_mats[i].size(); j++)
{
auto raw = df::inorganic_raw::find(layer_mats[i][j]);
if (!raw)
continue;
bool is_soil = raw->flags.is_set(inorganic_flags::SOIL_ANY);
if (is_soil)
default_soil[i] = layer_mats[i][j];
else if (default_stone[i] == -1)
default_stone[i] = layer_mats[i][j];
}
}
}
MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
@ -960,9 +1068,9 @@ MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
}
else
{
if(blockcoord.x >= 0 && blockcoord.x < x_bmax &&
blockcoord.y >= 0 && blockcoord.y < y_bmax &&
blockcoord.z >= 0 && blockcoord.z < z_max)
if(unsigned(blockcoord.x) < x_bmax &&
unsigned(blockcoord.y) < y_bmax &&
unsigned(blockcoord.z) < z_max)
{
Block * nblo = new Block(this, blockcoord);
blocks[blockcoord] = nblo;
@ -980,169 +1088,3 @@ void MapExtras::MapCache::resetTags()
it->second->tags = NULL;
}
}
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;
link->item->tile_bitmask.clear();
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;
}

@ -80,7 +80,7 @@ bool MaterialInfo::decode(df::item *item)
bool MaterialInfo::decode(const df::material_vec_ref &vr, int idx)
{
if (idx < 0 || idx >= vr.mat_type.size() || idx >= vr.mat_index.size())
if (size_t(idx) >= vr.mat_type.size() || size_t(idx) >= vr.mat_index.size())
return decode(-1);
else
return decode(vr.mat_type[idx], vr.mat_index[idx]);
@ -103,7 +103,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index)
df::world_raws &raws = world->raws;
if (type >= sizeof(raws.mat_table.builtin)/sizeof(void*))
if (size_t(type) >= sizeof(raws.mat_table.builtin)/sizeof(void*))
return false;
if (index < 0)
@ -127,7 +127,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index)
mode = Creature;
subtype = type-CREATURE_BASE;
creature = df::creature_raw::find(index);
if (!creature || subtype >= creature->material.size())
if (!creature || size_t(subtype) >= creature->material.size())
return false;
material = creature->material[subtype];
}
@ -139,7 +139,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index)
if (!figure)
return false;
creature = df::creature_raw::find(figure->race);
if (!creature || subtype >= creature->material.size())
if (!creature || size_t(subtype) >= creature->material.size())
return false;
material = creature->material[subtype];
}
@ -148,7 +148,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index)
mode = Plant;
subtype = type-PLANT_BASE;
plant = df::plant_raw::find(index);
if (!plant || subtype >= plant->material.size())
if (!plant || size_t(subtype) >= plant->material.size())
return false;
material = plant->material[subtype];
}

@ -81,15 +81,32 @@ bool Translation::copyName(df::language_name * source, df::language_name * targe
return true;
}
std::string Translation::capitalize(const std::string &str, bool all_words)
{
string upper = str;
if (!upper.empty())
{
upper[0] = toupper(upper[0]);
if (all_words)
{
for (size_t i = 1; i < upper.size(); i++)
if (isspace(upper[i-1]))
upper[i] = toupper(upper[i]);
}
}
return upper;
}
void addNameWord (string &out, const string &word)
{
if (word.empty())
return;
string upper = word;
upper[0] = toupper(upper[0]);
if (out.length() > 0)
out.append(" ");
out.append(upper);
out.append(Translation::capitalize(word));
}
void Translation::setNickname(df::language_name *name, std::string nick)

@ -55,8 +55,13 @@ using namespace std;
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.h"
#include "df/assumed_identity.h"
#include "df/burrow.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
using namespace DFHack;
using namespace df::enums;
@ -78,7 +83,7 @@ df::unit * Units::GetCreature (const int32_t index)
if (!isValid()) return NULL;
// read pointer from vector at position
if(index > world->units.all.size())
if(size_t(index) > world->units.all.size())
return 0;
return world->units.all[index];
}
@ -146,7 +151,7 @@ void Units::CopyCreature(df::unit * source, t_unit & furball)
// mood stuff
furball.mood = source->mood;
furball.mood_skill = source->job.unk_2f8; // FIXME: really? More like currently used skill anyway.
furball.mood_skill = source->job.mood_skill; // FIXME: really? More like currently used skill anyway.
Translation::readName(furball.artifact_name, &source->status.artifact_name);
// labors
@ -529,6 +534,23 @@ df::item *Units::getContainer(df::unit *unit)
return NULL;
}
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure)
{
if (figure && figure->info && figure->info->reputation)
return df::assumed_identity::find(figure->info->reputation->cur_identity);
return NULL;
}
df::assumed_identity *Units::getIdentity(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
return getFigureIdentity(figure);
}
void Units::setNickname(df::unit *unit, std::string nick)
{
CHECK_NULL_POINTER(unit);
@ -547,12 +569,7 @@ void Units::setNickname(df::unit *unit, std::string nick)
{
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)
if (auto identity = getFigureIdentity(figure))
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
@ -567,23 +584,13 @@ void Units::setNickname(df::unit *unit, std::string nick)
Translation::setNickname(&identity->name, nick);
}
}
}
}
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)
{
// 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)
if (auto identity = getIdentity(unit))
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
@ -592,8 +599,6 @@ df::language_name *Units::getVisibleName(df::unit *unit)
return &identity->name;
}
}
}
return &unit->name;
}
@ -672,65 +677,224 @@ bool DFHack::Units::isDwarf(df::unit *unit)
return unit->race == ui->race_id;
}
void DFHack::Units::clearBurrowMembers(df::burrow *burrow)
double DFHack::Units::getAge(df::unit *unit, bool true_age)
{
CHECK_NULL_POINTER(burrow);
using df::global::cur_year;
using df::global::cur_year_tick;
for (size_t i = 0; i < burrow->units.size(); i++)
{
auto unit = df::unit::find(burrow->units[i]);
CHECK_NULL_POINTER(unit);
if (unit)
erase_from_vector(unit->burrows, burrow->id);
}
if (!cur_year || !cur_year_tick)
return -1;
burrow->units.clear();
double year_ticks = 403200.0;
double birth_time = unit->relations.birth_year + unit->relations.birth_time/year_ticks;
double cur_time = *cur_year + *cur_year_tick / year_ticks;
// 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)
if (!true_age && unit->relations.curse_year >= 0)
{
auto &sel = ui->burrows.sel_units;
for (size_t i = 0; i < sel.size(); i++)
sel[i] = false;
if (auto identity = getIdentity(unit))
{
if (identity->histfig_id < 0)
birth_time = identity->birth_year + identity->birth_second/year_ticks;
}
}
return cur_time - birth_time;
}
static bool noble_pos_compare(const Units::NoblePosition &a, const Units::NoblePosition &b)
{
if (a.position->precedence < b.position->precedence)
return true;
if (a.position->precedence > b.position->precedence)
return false;
return a.position->id < b.position->id;
}
bool DFHack::Units::isInBurrow(df::unit *unit, df::burrow *burrow)
bool DFHack::Units::getNoblePositions(std::vector<NoblePosition> *pvec, df::unit *unit)
{
CHECK_NULL_POINTER(unit);
CHECK_NULL_POINTER(burrow);
return binsearch_index(unit->burrows, burrow->id) >= 0;
pvec->clear();
auto histfig = df::historical_figure::find(unit->hist_figure_id);
if (!histfig)
return false;
for (size_t i = 0; i < histfig->entity_links.size(); i++)
{
auto link = histfig->entity_links[i];
auto epos = strict_virtual_cast<df::histfig_entity_link_positionst>(link);
if (!epos)
continue;
NoblePosition pos;
pos.entity = df::historical_entity::find(epos->entity_id);
if (!pos.entity)
continue;
pos.assignment = binsearch_in_vector(pos.entity->positions.assignments, epos->assignment_id);
if (!pos.assignment)
continue;
pos.position = binsearch_in_vector(pos.entity->positions.own, pos.assignment->position_id);
if (!pos.position)
continue;
pvec->push_back(pos);
}
if (pvec->empty())
return false;
std::sort(pvec->begin(), pvec->end(), noble_pos_compare);
return true;
}
void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable)
std::string DFHack::Units::getProfessionName(df::unit *unit, bool ignore_noble, bool plural)
{
using df::global::ui;
std::string prof = unit->custom_profession;
if (!prof.empty())
return prof;
CHECK_NULL_POINTER(unit);
CHECK_NULL_POINTER(burrow);
std::vector<NoblePosition> np;
if (!ignore_noble && getNoblePositions(&np, unit))
{
switch (unit->sex)
{
case 0:
prof = np[0].position->name_female[plural ? 1 : 0];
break;
case 1:
prof = np[0].position->name_male[plural ? 1 : 0];
break;
default:
break;
}
if (prof.empty())
prof = np[0].position->name[plural ? 1 : 0];
if (!prof.empty())
return prof;
}
return getCasteProfessionName(unit->race, unit->caste, unit->profession, plural);
}
std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::profession pid, bool plural)
{
std::string prof, race_prefix;
if (pid < 0 || !is_valid_enum_item(pid))
return "";
bool use_race_prefix = (race >= 0 && race != df::global::ui->race_id);
if (auto creature = df::creature_raw::find(race))
{
if (auto caste = vector_get(creature->caste, casteid))
{
race_prefix = caste->caste_name[0];
if (enable)
if (plural)
prof = caste->caste_profession_name.plural[pid];
else
prof = caste->caste_profession_name.singular[pid];
if (prof.empty())
{
insert_into_vector(unit->burrows, burrow->id);
insert_into_vector(burrow->units, unit->id);
switch (pid)
{
case profession::CHILD:
prof = caste->child_name[plural ? 1 : 0];
if (!prof.empty())
use_race_prefix = false;
break;
case profession::BABY:
prof = caste->baby_name[plural ? 1 : 0];
if (!prof.empty())
use_race_prefix = false;
break;
default:
break;
}
}
}
if (race_prefix.empty())
race_prefix = creature->name[0];
if (prof.empty())
{
if (plural)
prof = creature->profession_name.plural[pid];
else
prof = creature->profession_name.singular[pid];
if (prof.empty())
{
switch (pid)
{
erase_from_vector(unit->burrows, burrow->id);
erase_from_vector(burrow->units, unit->id);
case profession::CHILD:
prof = creature->general_child_name[plural ? 1 : 0];
if (!prof.empty())
use_race_prefix = false;
break;
case profession::BABY:
prof = creature->general_baby_name[plural ? 1 : 0];
if (!prof.empty())
use_race_prefix = false;
break;
default:
break;
}
}
}
}
// 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)
if (race_prefix.empty())
race_prefix = "Animal";
if (prof.empty())
{
switch (pid)
{
int idx = linear_index(ui->burrows.list_units, unit);
if (idx >= 0)
ui->burrows.sel_units[idx] = enable;
case profession::TRAINED_WAR:
prof = "War " + (use_race_prefix ? race_prefix : "Peasant");
use_race_prefix = false;
break;
case profession::TRAINED_HUNTER:
prof = "Hunting " + (use_race_prefix ? race_prefix : "Peasant");
use_race_prefix = false;
break;
case profession::STANDARD:
if (!use_race_prefix)
prof = "Peasant";
break;
default:
if (auto caption = ENUM_ATTR(profession, caption, pid))
prof = caption;
else
prof = ENUM_KEY_STR(profession, pid);
}
}
if (use_race_prefix)
{
if (!prof.empty())
race_prefix += " ";
prof = race_prefix + prof;
}
}
return Translation::capitalize(prof, true);
}

@ -54,14 +54,14 @@ uint32_t Vegetation::getCount()
df::plant * Vegetation::getPlant(const int32_t index)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return NULL;
return world->plants.all[index];
}
bool Vegetation::copyPlant(const int32_t index, t_plant &out)
{
if (index < 0 || index >= getCount())
if (uint32_t(index) >= getCount())
return false;
out.origin = world->plants.all[index];

@ -41,7 +41,7 @@ Windows::df_screentile *Windows::getScreenBuffer()
}
Windows::df_window::df_window(int x, int y, unsigned int width, unsigned int height)
:buffer(0), parent(0), left(x), top(y), width(width), height(height), current_painter(NULL)
:buffer(0), width(width), height(height), parent(0), left(x), top(y), current_painter(NULL)
{
buffer = 0;
};

@ -1 +1 @@
Subproject commit 25c2a3dad964abbcceb5abd41558b71fb113e83b
Subproject commit 970bb0ae9530e9c0522cb0ca8fe357815d72fab1

@ -1,4 +1,9 @@
#pragma once
#include <llimits.h>
#include <sstream>
#include <string>
#include <stack>
#include <set>
typedef vector <df::coord> coord_vec;
class Brush
@ -60,10 +65,15 @@ public:
};
~RectangleBrush(){};
std::string str() const {
if (x_ == 1 && y_ == 1 && z_ == 1) {
if (x_ == 1 && y_ == 1 && z_ == 1)
{
return "point";
} else {
return "rectangle";
}
else
{
std::ostringstream ss;
ss << "rect: " << x_ << "/" << y_ << "/" << z_ << std::endl;
return ss.str();
}
}
private:

@ -106,6 +106,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(regrass regrass.cpp)
# this one exports functions to lua
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
# not yet. busy with other crud again...
#DFHACK_PLUGIN(versionosd versionosd.cpp)
endif()

@ -121,11 +121,27 @@ 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")
if (!parameters.empty())
{
if (parameters[0] == "--core-context")
{
Lua::InterpreterLoop(out, Lua::Core::State, "core lua");
return CR_OK;
}
else if (parameters[0] == "--core-reload")
{
CoreSuspender suspend;
for (size_t i = 1; i < parameters.size(); i++)
{
lua_getglobal(Lua::Core::State, "reload");
lua_pushstring(Lua::Core::State, parameters[i].c_str());
Lua::SafeCall(out, Lua::Core::State, 1, 0);
}
return CR_OK;
}
}
mymutex->lock();
lua::state s=lua::glua::Get();

@ -5,7 +5,7 @@ function adv_tools.reincarnate(swap_soul) --only for adventurer i guess
if swap_soul==nil then
swap_soul=true
end
local adv=df.global.world.units.other[0][0]
local adv=df.global.world.units.active[0]
if adv.flags1.dead==false then
error("You are not dead (yet)!")
end

@ -472,8 +472,8 @@ function getSelectedUnit()
return nil
end
local unit_indx=df.global.ui_selected_unit
if unit_indx<#df.global.world.units.other[0]-1 then
return df.global.world.units.other[0][unit_indx]
if unit_indx<#df.global.world.units.active-1 then
return df.global.world.units.active[unit_indx]
else
return nil
end

@ -113,7 +113,7 @@ function tools.change_adv(unit,nemesis)
if unit==nil then
error("Invalid unit!")
end
local other=df.global.world.units.other[0]
local other=df.global.world.units.active
local unit_indx
for k,v in pairs(other) do
if v==unit then
@ -157,7 +157,7 @@ function tools.MakeFollow(unit,trgunit)
error("Invalid creature")
end
if trgunit==nil then
trgunit=df.global.world.units.other[0][0]
trgunit=df.global.world.units.active[0]
end
unit.relations.group_leader_id=trgunit.id
local u_nem=getNemesis(unit)

@ -1,6 +1,7 @@
#include "lua_Console.h"
#include "LuaTools.h"
#include "lua_Console.h"
#include <sstream>
//TODO error management. Using lua error? or something other?

@ -169,7 +169,7 @@ bool bodySwap(color_ostream &out, df::unit *player)
return false;
}
auto &vec = world->units.other[0];
auto &vec = world->units.active;
int idx = linear_index(vec, player);
if (idx < 0)
@ -195,7 +195,7 @@ df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap)
if (restore_swap)
{
df::unit *ctl = world->units.other[0][0];
df::unit *ctl = world->units.active[0];
auto ctl_nemesis = Units::getNemesis(ctl);
if (ctl_nemesis != real_nemesis)

@ -873,7 +873,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
if (is_on_break)
dwarf_info[dwarf].state = OTHER;
else if (dwarfs[dwarf]->meetings.size() > 0)
else if (dwarfs[dwarf]->specific_refs.size() > 0)
dwarf_info[dwarf].state = OTHER;
else
dwarf_info[dwarf].state = IDLE;

@ -13,6 +13,7 @@
#include "modules/MapCache.h"
#include "modules/World.h"
#include "modules/Units.h"
#include "modules/Burrows.h"
#include "TileTypes.h"
#include "DataDefs.h"
@ -345,7 +346,7 @@ static void handle_burrow_rename(color_ostream &out, df::burrow *burrow)
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);
Burrows::setAssignedTile(burrows[i], pos, true);
}
static void add_walls_to_burrows(color_ostream &out, std::vector<df::burrow*> &burrows,
@ -379,7 +380,7 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
for (size_t i = 0; i < grow_burrows.size(); i++)
{
auto b = df::burrow::find(grow_burrows[i]);
if (b && Maps::isBurrowTile(b, pos))
if (b && Burrows::isAssignedTile(b, pos))
to_grow.push_back(b);
}
@ -446,7 +447,7 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
if (source == target)
{
if (!enable)
Units::clearBurrowMembers(target);
Burrows::clearUnits(target);
return;
}
@ -456,7 +457,7 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
auto unit = df::unit::find(source->units[i]);
if (unit)
Units::setInBurrow(unit, target, enable);
Burrows::setAssignedUnit(target, unit, enable);
}
}
@ -468,22 +469,22 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
if (source == target)
{
if (!enable)
Maps::clearBurrowTiles(target);
Burrows::clearTiles(target);
return;
}
std::vector<df::map_block*> pvec;
Maps::listBurrowBlocks(&pvec, source);
Burrows::listBlocks(&pvec, source);
for (size_t i = 0; i < pvec.size(); i++)
{
auto block = pvec[i];
auto smask = Maps::getBlockBurrowMask(source, block);
auto smask = Burrows::getBlockMask(source, block);
if (!smask)
continue;
auto tmask = Maps::getBlockBurrowMask(target, block, enable);
auto tmask = Burrows::getBlockMask(target, block, enable);
if (!tmask)
continue;
@ -498,7 +499,7 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
tmask->tile_bitmask[j] &= ~smask->tile_bitmask[j];
if (!tmask->has_assignments())
Maps::deleteBlockBurrowMask(target, block, tmask);
Burrows::deleteBlockMask(target, block, tmask);
}
}
}
@ -523,7 +524,7 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas
continue;
if (!mask)
mask = Maps::getBlockBurrowMask(target, block, enable);
mask = Burrows::getBlockMask(target, block, enable);
if (!mask)
goto next_block;
@ -532,7 +533,7 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas
}
if (mask && !enable && !mask->has_assignments())
Maps::deleteBlockBurrowMask(target, block, mask);
Burrows::deleteBlockMask(target, block, mask);
next_block:;
}
@ -604,7 +605,7 @@ static command_result burrow(color_ostream &out, vector <string> &parameters)
bool state = (cmd == "enable");
for (int i = 1; i < parameters.size(); i++)
for (size_t i = 1; i < parameters.size(); i++)
{
string &option = parameters[i];
@ -619,13 +620,13 @@ static command_result burrow(color_ostream &out, vector <string> &parameters)
if (parameters.size() < 2)
return CR_WRONG_USAGE;
for (int i = 1; i < parameters.size(); i++)
for (size_t i = 1; i < parameters.size(); i++)
{
auto target = findByName(out, parameters[i]);
if (!target)
return CR_WRONG_USAGE;
Units::clearBurrowMembers(target);
Burrows::clearUnits(target);
}
}
else if (cmd == "set-units" || cmd == "add-units" || cmd == "remove-units")
@ -638,11 +639,11 @@ static command_result burrow(color_ostream &out, vector <string> &parameters)
return CR_WRONG_USAGE;
if (cmd == "set-units")
Units::clearBurrowMembers(target);
Burrows::clearUnits(target);
bool enable = (cmd != "remove-units");
for (int i = 2; i < parameters.size(); i++)
for (size_t i = 2; i < parameters.size(); i++)
{
auto source = findByName(out, parameters[i]);
if (!source)
@ -656,13 +657,13 @@ static command_result burrow(color_ostream &out, vector <string> &parameters)
if (parameters.size() < 2)
return CR_WRONG_USAGE;
for (int i = 1; i < parameters.size(); i++)
for (size_t i = 1; i < parameters.size(); i++)
{
auto target = findByName(out, parameters[i]);
if (!target)
return CR_WRONG_USAGE;
Maps::clearBurrowTiles(target);
Burrows::clearTiles(target);
}
}
else if (cmd == "set-tiles" || cmd == "add-tiles" || cmd == "remove-tiles")
@ -675,11 +676,11 @@ static command_result burrow(color_ostream &out, vector <string> &parameters)
return CR_WRONG_USAGE;
if (cmd == "set-tiles")
Maps::clearBurrowTiles(target);
Burrows::clearTiles(target);
bool enable = (cmd != "remove-tiles");
for (int i = 2; i < parameters.size(); i++)
for (size_t i = 2; i < parameters.size(); i++)
{
if (setTilesByKeyword(target, parameters[i], enable))
continue;

@ -188,6 +188,8 @@ static command_result stockcheck(color_ostream &out, vector <string> & parameter
building = ref->getBuilding();
break;
default:
break;
}
}
@ -215,6 +217,8 @@ static command_result stockcheck(color_ostream &out, vector <string> & parameter
building = ref->getBuilding();
break;
default:
break;
}
}
}

@ -193,7 +193,7 @@ static bool build_choice_matches(df::ui_build_item_req *req, df::build_req_choic
{
if (gen->mat_type == new_mat.type &&
gen->mat_index == new_mat.index &&
(ignore_select || gen->used_count < gen->candidates.size()))
(ignore_select || size_t(gen->used_count) < gen->candidates.size()))
{
return true;
}
@ -272,7 +272,7 @@ static command_result job_duplicate(color_ostream &out, vector <string> & parame
if (!job)
return CR_FAILURE;
if (!job->misc_links.empty() ||
if (!job->specific_refs.empty() ||
(job->job_items.empty() &&
job->job_type != job_type::CollectSand &&
job->job_type != job_type::CollectClay))
@ -305,7 +305,7 @@ static df::job_item *getJobItem(color_ostream &out, df::job *job, std::string id
return NULL;
int v = atoi(idx.c_str());
if (v < 1 || v > job->job_items.size()) {
if (v < 1 || size_t(v) > job->job_items.size()) {
out.printerr("Invalid item index.\n");
return NULL;
}

@ -374,6 +374,10 @@ command_result df_liquids_execute(color_ostream &out)
DFHack::DFCoord cursor(x,y,z);
coord_vec all_tiles = brush->points(mcache,cursor);
out << "working..." << endl;
// Force the game to recompute its walkability cache
df::global::world->reindex_pathfinding = true;
if(mode == "obsidian")
{
coord_vec::iterator iter = all_tiles.begin();
@ -384,6 +388,7 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setTemp2At(*iter,10015);
df::tile_designation des = mcache.designationAt(*iter);
des.bits.flow_size = 0;
des.bits.flow_forbid = false;
mcache.setDesignationAt(*iter, des);
iter ++;
}
@ -490,6 +495,9 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
// mark the tile passable or impassable like the game does
des.bits.flow_forbid = des.bits.flow_size &&
(des.bits.liquid_type == tile_liquid::Magma || des.bits.flow_size > 3);
mcache.setDesignationAt(current,des);
}
seen_blocks.insert(mcache.BlockAt(current / 16));

@ -2,6 +2,11 @@ local _ENV = mkmodule('plugins.burrows')
--[[
Native events:
* onBurrowRename(burrow)
* onDigComplete(job_type,pos,old_tiletype,new_tiletype)
Native functions:
* findByName(name) -> burrow
@ -13,21 +18,6 @@ local _ENV = mkmodule('plugins.burrows')
--]]
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
rawset_default(_ENV, dfhack.burrows)
return _ENV

@ -0,0 +1,53 @@
local _ENV = mkmodule('plugins.sort')
local utils = require('utils')
local units = require('plugins.sort.units')
orders = orders or {}
orders.units = units.orders
function parse_ordering_spec(type,...)
local group = orders[type]
if group == nil then
dfhack.printerr('Invalid ordering class: '..tostring(type))
return nil
end
local specs = table.pack(...)
local rv = { }
for _,spec in ipairs(specs) do
local nil_first = false
if string.sub(spec,1,1) == '<' then
nil_first = true
spec = string.sub(spec,2)
end
local reverse = false
if string.sub(spec,1,1) == '>' then
reverse = true
spec = string.sub(spec,2)
end
local cm = group[spec]
if cm == nil then
dfhack.printerr('Unknown order for '..type..': '..tostring(spec))
return nil
end
if nil_first or reverse then
cm = copyall(cm)
cm.nil_first = nil_first
cm.reverse = reverse
end
rv[#rv+1] = cm
end
return rv
end
make_sort_order = utils.make_sort_order
return _ENV

@ -0,0 +1,112 @@
local _ENV = mkmodule('plugins.sort.units')
local utils = require('utils')
orders = orders or {}
-- Relies on NULL being auto-translated to NULL, and then sorted
orders.exists = {
key = function(unit)
return 1
end
}
orders.name = {
key = function(unit)
local name = dfhack.units.getVisibleName(unit)
if name and name.has_name then
return dfhack.TranslateName(name)
end
end,
compare = utils.compare_name
}
orders.age = {
key = function(unit)
return dfhack.units.getAge(unit)
end
}
-- This assumes that units are added to active in arrival order
orders.arrival = {
key_table = function(units)
local size = units.n or #units
local lookup={}
for i=1,size do
if units[i] then
lookup[units[i].id] = i
end
end
local idx={}
for i,v in ipairs(df.global.world.units.active) do
if lookup[v.id] then
idx[lookup[v.id]] = i
end
end
return idx
end
}
local function findRaceCaste(unit)
local rraw = df.creature_raw.find(unit.race)
return rraw, safe_index(rraw, 'caste', unit.caste)
end
orders.noble = {
key = function(unit)
local info = dfhack.units.getNoblePositions(unit)
if info then
return info[1].position.precedence
end
end
}
orders.profession = {
key = function(unit)
local cp = dfhack.units.getProfessionName(unit)
if cp and cp ~= '' then
return string.lower(cp)
end
end
}
orders.profession_class = {
key = function(unit)
local pid = unit.profession
local parent = df.profession.attrs[pid].parent
if parent >= 0 then
return parent
else
return pid
end
end
}
orders.race = {
key = function(unit)
local rraw = findRaceCaste(unit)
if rraw then
return rraw.name[0]
end
end
}
orders.squad = {
key = function(unit)
local sidx = unit.military.squad_index
if sidx >= 0 then
return sidx
end
end
}
orders.squad_position = {
key = function(unit)
local sidx = unit.military.squad_index
if sidx >= 0 then
return sidx * 1000 + unit.military.squad_position
end
end
}
return _ENV

@ -39,6 +39,46 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
static dfproto::Tile::TileMaterialType toProto(df::tiletype_material mat)
{
/*
* This is surely ugly, but casting enums without officially
* defined numerical values to protobuf enums is against the
* way protobufs are supposed to be used, because it defeats
* the backward compatible nature of the protocols.
*/
switch (mat)
{
#define CONVERT(name) case tiletype_material::name: return dfproto::Tile::name;
case tiletype_material::NONE:
CONVERT(AIR)
case tiletype_material::PLANT:
CONVERT(SOIL)
CONVERT(STONE)
CONVERT(FEATURE)
CONVERT(LAVA_STONE)
CONVERT(MINERAL)
CONVERT(FROZEN_LIQUID)
CONVERT(CONSTRUCTION)
CONVERT(GRASS_LIGHT)
CONVERT(GRASS_DARK)
CONVERT(GRASS_DRY)
CONVERT(GRASS_DEAD)
CONVERT(HFS)
CONVERT(CAMPFIRE)
CONVERT(FIRE)
CONVERT(ASHES)
case tiletype_material::MAGMA:
return dfproto::Tile::MAGMA_TYPE;
CONVERT(DRIFTWOOD)
CONVERT(POOL)
CONVERT(BROOK)
CONVERT(RIVER)
#undef CONVERT
}
return dfproto::Tile::AIR;
}
command_result mapexport (color_ostream &out, std::vector <std::string> & parameters)
{
bool showHidden = false;
@ -195,7 +235,7 @@ command_result mapexport (color_ostream &out, std::vector <std::string> & parame
df::tiletype type = b->tiletypeAt(coord);
prototile->set_type((dfproto::Tile::TileType)tileShape(type));
prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type));
prototile->set_tile_material(toProto(tileMaterial(type)));
df::coord map_pos = df::coord(b_x*16+x,b_y*16+y,z);

@ -330,6 +330,39 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
out << "global feature idx: " << block.global_feature
<< endl;
out << std::endl;
if(block.occupancy[tileX][tileY].bits.no_grow)
out << "no grow" << endl;
for(size_t e=0; e<block.block_events.size(); e++)
{
df::block_square_event * blev = block.block_events[e];
df::block_square_event_type blevtype = blev->getType();
switch(blevtype)
{
case df::block_square_event_type::grass:
{
df::block_square_event_grassst * gr_ev = (df::block_square_event_grassst *)blev;
if(gr_ev->amount[tileX][tileY] > 0)
{
out << "amount of grass: " << (int)gr_ev->amount[tileX][tileY] << endl;
}
break;
}
case df::block_square_event_type::world_construction:
{
df::block_square_event_world_constructionst * co_ev = (df::block_square_event_world_constructionst*)blev;
uint16_t bits = co_ev->tile_bitmask[tileY];
out << "construction bits: " << bits << endl;
break;
}
default:
//out << "unhandled block event type!" << endl;
break;
}
}
return CR_OK;
}
@ -417,14 +450,10 @@ command_result df_bprobe (color_ostream &out, vector <string> & parameters)
break;
}
if(building.origin->is_room) //isRoom())
out << ", is room";
else
out << ", not a room";
out << ", room";
if(building.origin->getBuildStage()!=building.origin->getMaxBuildStage())
out << ", in construction";
out.print("\n");
}
return CR_OK;
}

@ -83,7 +83,12 @@ command_result df_regrass (color_ostream &out, vector <string> & parameters)
{
if ( tileShape(cur->tiletype[x][y]) != tiletype_shape::FLOOR
|| cur->designation[x][y].bits.subterranean
|| cur->occupancy[x][y].bits.building)
|| cur->occupancy[x][y].bits.building
|| cur->occupancy[x][y].bits.no_grow)
continue;
// don't touch furrowed tiles (dirt roads made on soil)
if(tileSpecial(cur->tiletype[x][y]) == tiletype_special::FURROWED)
continue;
int mat = tileMaterial(cur->tiletype[x][y]);
@ -93,6 +98,7 @@ command_result df_regrass (color_ostream &out, vector <string> & parameters)
)
continue;
// max = set amounts of all grass events on that tile to 100
if(max)
{

@ -0,0 +1,528 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Gui.h"
#include "modules/Translation.h"
#include "modules/Units.h"
#include "LuaTools.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/world.h"
#include "df/viewscreen_joblistst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_petst.h"
#include "df/layer_object_listst.h"
#include "MiscUtils.h"
#include <stdlib.h>
using std::vector;
using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using df::global::ui;
using df::global::world;
using df::global::ui_building_in_assign;
using df::global::ui_building_item_cursor;
using df::global::ui_building_assign_type;
using df::global::ui_building_assign_is_marked;
using df::global::ui_building_assign_units;
using df::global::ui_building_assign_items;
static bool unit_list_hotkey(df::viewscreen *top);
static command_result sort_units(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("sort");
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"sort-units", "Sort the visible unit list.", sort_units, unit_list_hotkey,
" sort-units order [order...]\n"
" Sort the unit list using the given sequence of comparisons.\n"
" The '<' prefix for an order makes undefined values sort first.\n"
" The '>' prefix reverses the sort order for defined values.\n"
" Unit order examples:\n"
" name, age, arrival, squad, squad_position, profession\n"
"The orderings are defined in hack/lua/plugins/sort/*.lua\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
template<class T>
void reorder_vector(std::vector<T> *pvec, const std::vector<unsigned> &order)
{
assert(pvec->size() == order.size());
std::vector<T> tmp(*pvec);
for (size_t i = 0; i < order.size(); i++)
(*pvec)[i] = tmp[order[i]];
}
template<class T>
void reorder_cursor(T *cursor, const std::vector<unsigned> &order)
{
if (*cursor == 0)
return;
for (size_t i = 0; i < order.size(); i++)
{
if (unsigned(*cursor) == order[i])
{
*cursor = T(i);
break;
}
}
}
bool parse_ordering_spec(color_ostream &out, lua_State *L, std::string type, const std::vector<std::string> &params)
{
if (!lua_checkstack(L, params.size() + 2))
return false;
if (!Lua::PushModulePublic(out, L, "plugins.sort", "parse_ordering_spec"))
return false;
Lua::Push(L, type);
for (size_t i = 0; i < params.size(); i++)
Lua::Push(L, params[i]);
if (!Lua::SafeCall(out, L, params.size()+1, 1))
return false;
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
return false;
}
return true;
}
bool read_order(color_ostream &out, lua_State *L, std::vector<unsigned> *order, size_t size)
{
std::vector<char> found;
Lua::StackUnwinder frame(L, 1);
if (!lua_istable(L, -1))
{
out.printerr("Not a table returned as ordering.\n");
return false;
}
if (lua_rawlen(L, -1) != size)
{
out.printerr("Invalid ordering size: expected %d, actual %d\n", size, lua_rawlen(L, -1));
return false;
}
order->clear();
order->resize(size);
found.resize(size);
for (size_t i = 1; i <= size; i++)
{
lua_rawgeti(L, frame[1], i);
int v = lua_tointeger(L, -1);
lua_pop(L, 1);
if (v < 1 || size_t(v) > size)
{
out.printerr("Order value out of range: %d\n", v);
return false;
}
if (found[v-1])
{
out.printerr("Duplicate order value: %d\n", v);
return false;
}
found[v-1] = 1;
(*order)[i-1] = v-1;
}
return true;
}
template<class T>
bool compute_order(color_ostream &out, lua_State *L, int base, std::vector<unsigned> *order, const std::vector<T> &key)
{
lua_pushvalue(L, base+1);
Lua::PushVector(L, key, true);
lua_pushvalue(L, base+2);
if (!Lua::SafeCall(out, L, 2, 1))
return false;
return read_order(out, L, order, key.size());
}
static bool ParseSpec(color_ostream &out, lua_State *L, const char *type, vector<string> &params)
{
if (!parse_ordering_spec(out, L, "units", params))
{
out.printerr("Invalid ordering specification for %s.\n", type);
return false;
}
return true;
}
#define PARSE_SPEC(type, params) \
if (!ParseSpec(*pout, L, type, params)) return false;
static void sort_null_first(vector<string> &parameters)
{
vector_insert_at(parameters, 0, std::string("<exists"));
}
static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx)
{
return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx));
}
static bool maybe_sort_units(color_ostream *pout, lua_State *L,
df::viewscreen *screen, vector<string> &parameters)
{
Lua::StackUnwinder top(L);
if (L)
{
if (!Lua::PushModulePublic(*pout, L, "plugins.sort", "make_sort_order"))
{
pout->printerr("Cannot access the sorter function.\n");
return false;
}
}
std::vector<unsigned> order;
if (auto units = strict_virtual_cast<df::viewscreen_unitlistst>(screen))
{
if (!L) return true;
/*
* Sort units in the 'u'nit list screen.
*/
PARSE_SPEC("units", parameters);
int page = units->page;
if (compute_order(*pout, L, top, &order, units->units[page]))
{
reorder_cursor(&units->cursor_pos[page], order);
reorder_vector(&units->units[page], order);
reorder_vector(&units->jobs[page], order);
}
return true;
}
else if (auto jobs = strict_virtual_cast<df::viewscreen_joblistst>(screen))
{
if (!L) return true;
/*
* Sort units in the 'j'ob list screen.
*/
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, jobs->units))
{
reorder_cursor(&jobs->cursor_pos, order);
reorder_vector(&jobs->units, order);
reorder_vector(&jobs->jobs, order);
}
return true;
}
else if (auto military = strict_virtual_cast<df::viewscreen_layer_militaryst>(screen))
{
switch (military->page)
{
case df::viewscreen_layer_militaryst::Positions:
{
auto &candidates = military->positions.candidates;
auto list3 = getLayerList(military, 2);
/*
* Sort candidate units in the 'p'osition page of the 'm'ilitary screen.
*/
if (list3 && !candidates.empty() && list3->bright)
{
if (!L) return true;
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, candidates))
{
reorder_cursor(&list3->cursor, order);
reorder_vector(&candidates, order);
}
return true;
}
return false;
}
default:
return false;
}
}
else if (auto profile = strict_virtual_cast<df::viewscreen_layer_workshop_profilest>(screen))
{
auto list1 = getLayerList(profile, 0);
if (!list1) return false;
if (!L) return true;
/*
* Sort units in the workshop 'q'uery 'P'rofile modification screen.
*/
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, profile->workers))
{
reorder_cursor(&list1->cursor, order);
reorder_vector(&profile->workers, order);
}
return true;
}
else if (auto nobles = strict_virtual_cast<df::viewscreen_layer_noblelistst>(screen))
{
switch (nobles->mode)
{
case df::viewscreen_layer_noblelistst::Appoint:
{
auto list2 = getLayerList(nobles, 1);
/*
* Sort units in the appointment candidate list of the 'n'obles screen.
*/
if (list2)
{
if (!L) return true;
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
std::vector<df::unit*> units;
for (size_t i = 0; i < nobles->candidates.size(); i++)
units.push_back(nobles->candidates[i]->unit);
if (compute_order(*pout, L, top, &order, units))
{
reorder_cursor(&list2->cursor, order);
reorder_vector(&nobles->candidates, order);
}
return true;
}
return false;
}
default:
return false;
}
}
else if (auto animals = strict_virtual_cast<df::viewscreen_petst>(screen))
{
switch (animals->mode)
{
case df::viewscreen_petst::List:
{
if (!L) return true;
/*
* Sort animal units in the Animal page of the 'z' status screen.
*/
PARSE_SPEC("units", parameters);
std::vector<df::unit*> units;
for (size_t i = 0; i < animals->animal.size(); i++)
units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]);
if (compute_order(*pout, L, top, &order, units))
{
reorder_cursor(&animals->cursor, order);
reorder_vector(&animals->animal, order);
reorder_vector(&animals->is_vermin, order);
reorder_vector(&animals->pet_info, order);
reorder_vector(&animals->is_tame, order);
reorder_vector(&animals->is_adopting, order);
}
return true;
}
case df::viewscreen_petst::SelectTrainer:
{
if (!L) return true;
/*
* Sort candidate trainers in the Animal page of the 'z' status screen.
*/
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, animals->trainer_unit))
{
reorder_cursor(&animals->trainer_cursor, order);
reorder_vector(&animals->trainer_unit, order);
reorder_vector(&animals->trainer_mode, order);
}
return true;
}
default:
return false;
}
}
else if (auto health = strict_virtual_cast<df::viewscreen_layer_overall_healthst>(screen))
{
auto list1 = getLayerList(health, 0);
if (!list1) return false;
if (!L) return true;
/*
* Sort units in the Health page of the 'z' status screen.
*/
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, health->unit))
{
reorder_cursor(&list1->cursor, order);
reorder_vector(&health->unit, order);
reorder_vector(&health->bits1, order);
reorder_vector(&health->bits2, order);
reorder_vector(&health->bits3, order);
}
return true;
}
else if (strict_virtual_cast<df::viewscreen_dwarfmodest>(screen))
{
switch (ui->main.mode)
{
case ui_sidebar_mode::Burrows:
if (!L) return true;
/*
* Sort burrow member candidate units in the 'w' sidebar mode.
*/
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, ui->burrows.list_units))
{
reorder_cursor(&ui->burrows.unit_cursor_pos, order);
reorder_vector(&ui->burrows.list_units, order);
reorder_vector(&ui->burrows.sel_units, order);
}
return true;
case ui_sidebar_mode::QueryBuilding:
if (!ui_building_in_assign || !*ui_building_in_assign)
return false;
// fall through for building owner / chain assign animal
case ui_sidebar_mode::ZonesPenInfo:
if (ui_building_item_cursor &&
ui_building_assign_type &&
ui_building_assign_is_marked &&
ui_building_assign_units &&
ui_building_assign_items &&
ui_building_assign_type->size() == ui_building_assign_units->size() &&
!ui_building_assign_type->empty())
{
if (!L) return true;
/*
* Sort building owner candidate units in the 'q' sidebar mode,
* or pen assignment candidate units in 'z'->'N', or cage assignment.
*/
// TODO: better way
bool is_assign_owner = ((*ui_building_assign_type)[0] == -1);
if (is_assign_owner)
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, *ui_building_assign_units))
{
reorder_cursor(ui_building_item_cursor, order);
reorder_vector(ui_building_assign_type, order);
reorder_vector(ui_building_assign_units, order);
if (ui_building_assign_units->size() == ui_building_assign_items->size())
reorder_vector(ui_building_assign_items, order);
if (ui_building_assign_units->size() == ui_building_assign_is_marked->size())
reorder_vector(ui_building_assign_is_marked, order);
}
return true;
}
return false;
default:
return false;
}
}
else
return false;
}
static bool unit_list_hotkey(df::viewscreen *screen)
{
vector<string> dummy;
return maybe_sort_units(NULL, NULL, screen, dummy);
}
static command_result sort_units(color_ostream &out, vector <string> &parameters)
{
if (parameters.empty())
return CR_WRONG_USAGE;
auto L = Lua::Core::State;
auto screen = Core::getInstance().getTopViewscreen();
if (!maybe_sort_units(&out, L, screen, parameters))
return CR_WRONG_USAGE;
return CR_OK;
}

@ -614,6 +614,9 @@ command_result executePaintJob(color_ostream &out)
coord_vec all_tiles = brush->points(map, cursor);
out.print("working...\n");
// Force the game to recompute its walkability cache
df::global::world->reindex_pathfinding = true;
for (coord_vec::iterator iter = all_tiles.begin(); iter != all_tiles.end(); ++iter)
{
const df::tiletype source = map.tiletypeAt(*iter);

@ -364,7 +364,7 @@ static ProtectedJob *get_known(int id)
static bool isSupportedJob(df::job *job)
{
return job->misc_links.empty() &&
return job->specific_refs.empty() &&
Job::getHolder(job) &&
(!job->job_items.empty() ||
job->job_type == job_type::CollectClay ||
@ -892,6 +892,9 @@ static void guess_job_material(df::job *job, MaterialInfo &mat, df::dfhack_mater
case item_type::WOOD:
mat_mask.bits.wood = mat_mask.bits.wood2 = true;
break;
default:
break;
}
}
}
@ -1080,12 +1083,11 @@ static bool itemInRealJob(df::item *item)
if (!item->flags.bits.in_job)
return false;
if (item->jobs.size() != 1 ||
item->jobs[0]->unk1 != 2 ||
item->jobs[0]->job == NULL)
auto ref = Items::getSpecificRef(item, specific_ref_type::JOB);
if (!ref || !ref->job)
return true;
return ENUM_ATTR(job_type, type, item->jobs[0]->job->job_type)
return ENUM_ATTR(job_type, type, ref->job->job_type)
!= job_type_class::Hauling;
}
@ -1148,6 +1150,9 @@ static void map_job_items(color_ostream &out)
if (item->getTotalDimension() < 10000)
is_invalid = true;
break;
default:
break;
}
if (item->flags.bits.melt && !item->flags.bits.owned && !itemBusy(item))

@ -1628,8 +1628,8 @@ void zoneInfo(color_ostream & out, df::building* building, bool verbose)
out << ", pen/pasture";
else if (civ->zone_flags.bits.pit_pond)
{
out << " (pit flags:" << civ->pit_flags << ")";
if(civ->pit_flags & 1)
out << " (pit flags:" << civ->pit_flags.whole << ")";
if(civ->pit_flags.bits.is_pond)
out << ", pond";
else
out << ", pit";

@ -0,0 +1,6 @@
-- Example of a lua script.
run_count = (run_count or 0) + 1
print('Arguments: ',...)
print('Command called '..run_count..' times.')

@ -0,0 +1,28 @@
-- Makes the game immediately save the state.
if not dfhack.isMapLoaded() then
dfhack.printerr("World and map aren't loaded.")
return
end
local ui_main = df.global.ui.main
local flags4 = df.global.d_init.flags4
local function restore_autobackup()
if ui_main.autosave_request and dfhack.isMapLoaded() then
dfhack.timeout(10, 'frames', restore_autobackup)
else
flags4.AUTOBACKUP = true
end
end
-- Request auto-save
ui_main.autosave_request = true
-- And since it will overwrite the backup, disable it temporarily
if flags4.AUTOBACKUP then
flags4.AUTOBACKUP = false
restore_autobackup()
end
print 'The game should save the state now.'