merged stuff from peterix

develop
Robert Heinrich 2012-04-15 12:32:25 +02:00
parent 9f95e67c75
commit cf0d45be78
39 changed files with 1651 additions and 524 deletions

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

@ -663,6 +663,14 @@ Job module
Units module
------------
* ``dfhack.units.getPosition(unit)``
Returns true *x,y,z* of the unit; may be not equal to unit.pos if caged.
* ``dfhack.units.getContainer(unit)``
Returns the container (cage) item or *nil*.
* ``dfhack.units.setNickname(unit,nick)``
Sets the unit's nickname properly.
@ -687,6 +695,10 @@ Units module
The unit is capable of rational action, i.e. not dead, insane or zombie.
* ``dfhack.units.clearBurrowMembers(burrow)``
Removes all units from the burrow.
* ``dfhack.units.isInBurrow(unit,burrow)``
Checks if the unit is in the burrow.
@ -699,6 +711,10 @@ Units module
Items module
------------
* ``dfhack.items.getPosition(item)``
Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory.
* ``dfhack.items.getOwner(item)``
Returns the owner unit or *nil*.
@ -708,6 +724,22 @@ Items module
Replaces the owner of the item. If unit is *nil*, removes ownership.
Returns *false* in case of error.
* ``dfhack.items.getContainer(item)``
Returns the container item or *nil*.
* ``dfhack.items.getContainedItems(item)``
Returns a list of items contained in this one.
* ``dfhack.items.moveToGround(item,pos)``
Move the item to the ground at position. Returns *false* if impossible.
* ``dfhack.items.moveToContainer(item,container)``
Move the item to the container. Returns *false* if impossible.
Maps module
-----------
@ -748,6 +780,10 @@ Maps module
Returns a table of map block pointers.
* ``dfhack.maps.clearBurrowTiles(burrow)``
Removes all tiles from the burrow.
* ``dfhack.maps.isBurrowTile(burrow,tile_coord)``
Checks if the tile is in burrow.

@ -900,6 +900,12 @@ a lua list containing them.</p>
<div class="section" id="units-module">
<h3><a class="toc-backref" href="#id16">Units module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p>
<p>Returns true <em>x,y,z</em> of the unit; may be not equal to unit.pos if caged.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.getContainer(unit)</tt></p>
<p>Returns the container (cage) item or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.setNickname(unit,nick)</tt></p>
<p>Sets the unit's nickname properly.</p>
</li>
@ -918,6 +924,9 @@ 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>
<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>
@ -929,6 +938,9 @@ a lua list containing them.</p>
<div class="section" id="items-module">
<h3><a class="toc-backref" href="#id17">Items module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p>
<p>Returns true <em>x,y,z</em> of the item; may be not equal to item.pos if in inventory.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getOwner(item)</tt></p>
<p>Returns the owner unit or <em>nil</em>.</p>
</li>
@ -936,6 +948,18 @@ a lua list containing them.</p>
<p>Replaces the owner of the item. If unit is <em>nil</em>, removes ownership.
Returns <em>false</em> in case of error.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getContainer(item)</tt></p>
<p>Returns the container item or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.getContainedItems(item)</tt></p>
<p>Returns a list of items contained in this one.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.moveToGround(item,pos)</tt></p>
<p>Move the item to the ground at position. Returns <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.moveToContainer(item,container)</tt></p>
<p>Move the item to the container. Returns <em>false</em> if impossible.</p>
</li>
</ul>
</div>
<div class="section" id="maps-module">
@ -968,6 +992,9 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.maps.listBurrowBlocks(burrow)</tt></p>
<p>Returns a table of map block pointers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.clearBurrowTiles(burrow)</tt></p>
<p>Removes all tiles from the burrow.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.isBurrowTile(burrow,tile_coord)</tt></p>
<p>Checks if the tile is in burrow.</p>
</li>

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

@ -861,13 +861,9 @@ int Core::TileUpdate()
// should always be from simulation thread!
int Core::Update()
{
if(!started)
Init();
if(errorstate)
return -1;
color_ostream_proxy out(con);
// Pretend this thread has suspended the core in the usual way
{
lock_guard<mutex> lock(d->AccessMutex);
@ -877,6 +873,22 @@ int Core::Update()
d->df_suspend_depth = 1000;
}
// Initialize the core
bool first_update = false;
if(!started)
{
first_update = true;
Init();
if(errorstate)
return -1;
}
color_ostream_proxy out(con);
if (first_update)
plug_mgr->OnStateChange(out, SC_CORE_INITIALIZED);
// detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL;
void *new_mapdata = NULL;

@ -1,396 +0,0 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include <string>
#include <vector>
#include <map>
#include "MemAccess.h"
#include "Core.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
#include "DataDefs.h"
#include "DataIdentity.h"
#include "MiscUtils.h"
using namespace DFHack;
void *type_identity::do_allocate_pod() {
void *p = malloc(size);
memset(p, 0, size);
return p;
}
void type_identity::do_copy_pod(void *tgt, const void *src) {
memmove(tgt, src, size);
};
bool type_identity::do_destroy_pod(void *obj) {
free(obj);
return true;
}
void *type_identity::allocate() {
if (can_allocate())
return do_allocate();
else
return NULL;
}
bool type_identity::copy(void *tgt, const void *src) {
if (can_allocate() && tgt && src)
{
do_copy(tgt, src);
return true;
}
else
return false;
}
bool type_identity::destroy(void *obj) {
if (can_allocate() && obj)
return do_destroy(obj);
else
return false;
}
void *enum_identity::do_allocate() {
void *p = malloc(byte_size());
memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t)));
return p;
}
/* The order of global object constructor calls is
* undefined between compilation units. Therefore,
* this list has to be plain data, so that it gets
* initialized by the loader in the initial mmap.
*/
compound_identity *compound_identity::list = NULL;
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)
{
next = list; list = this;
}
void compound_identity::doInit(Core *)
{
if (scope_parent)
scope_parent->scope_children.push_back(this);
else
top_scope.push_back(this);
}
std::string compound_identity::getFullName()
{
if (scope_parent)
return scope_parent->getFullName() + "." + getName();
else
return getName();
}
static tthread::mutex *known_mutex = NULL;
void compound_identity::Init(Core *core)
{
if (!known_mutex)
known_mutex = new tthread::mutex();
// This cannot be done in the constructors, because
// they are called in an undefined order.
for (compound_identity *p = list; p; p = p->next)
p->doInit(core);
//FIXME: ... nuked. the group was empty...
/*
// Read pre-filled vtable ptrs
OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
for (virtual_identity *p = list; p; p = p->next) {
void * tmp;
if (ptr_table->getSafeAddress(p->getName(),tmp))
p->vtable_ptr = tmp;
}
*/
}
bitfield_identity::bitfield_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
int num_bits, const bitfield_item_info *bits)
: compound_identity(size, NULL, scope_parent, dfhack_name), bits(bits), num_bits(num_bits)
{
}
enum_identity::enum_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
type_identity *base_type,
int64_t first_item_value, int64_t last_item_value,
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)
{
}
struct_identity::struct_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name,
struct_identity *parent, const struct_field_info *fields)
: compound_identity(size, alloc, scope_parent, dfhack_name),
parent(parent), has_children(false), fields(fields)
{
}
void struct_identity::doInit(Core *core)
{
compound_identity::doInit(core);
if (parent) {
parent->children.push_back(this);
parent->has_children = true;
}
}
bool struct_identity::is_subclass(struct_identity *actual)
{
if (!has_children && actual != this)
return false;
for (; actual; actual = actual->getParent())
if (actual == this) return true;
return false;
}
std::string pointer_identity::getFullName()
{
return (target ? target->getFullName() : std::string("void")) + "*";
}
std::string container_identity::getFullName(type_identity *item)
{
return "<" + (item ? item->getFullName() : std::string("void")) + ">";
}
std::string ptr_container_identity::getFullName(type_identity *item)
{
return "<" + (item ? item->getFullName() : std::string("void")) + "*>";
}
std::string bit_container_identity::getFullName(type_identity *)
{
return "<bool>";
}
std::string df::buffer_container_identity::getFullName(type_identity *item)
{
return (item ? item->getFullName() : std::string("void")) +
(size > 0 ? stl_sprintf("[%d]", size) : std::string("[]"));
}
virtual_identity::virtual_identity(size_t size, TAllocateFn alloc,
const char *dfhack_name, const char *original_name,
virtual_identity *parent, const struct_field_info *fields)
: struct_identity(size, alloc, NULL, dfhack_name, parent, fields), original_name(original_name),
vtable_ptr(NULL)
{
}
static std::map<std::string, virtual_identity*> name_lookup;
void virtual_identity::doInit(Core *core)
{
struct_identity::doInit(core);
name_lookup[getOriginalName()] = this;
}
/* Vtable to identity lookup. */
std::map<void*, virtual_identity*> virtual_identity::known;
virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
{
if (!instance_ptr) return NULL;
// 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())
return it->second;
// If using a reader/writer lock, re-grab as write here, and recheck
Core &core = Core::getInstance();
std::string name = core.p->doReadClassName(vtable);
virtual_identity *actual = NULL;
auto name_it = name_lookup.find(name);
if (name_it != name_lookup.end()) {
virtual_identity *p = name_it->second;
if (p->vtable_ptr && p->vtable_ptr != vtable) {
std::cerr << "Conflicting vtable ptr for class '" << p->getName()
<< "': found 0x" << std::hex << unsigned(vtable)
<< ", previous 0x" << unsigned(p->vtable_ptr) << std::dec << std::endl;
abort();
} else if (!p->vtable_ptr) {
std::cerr << "class '" << p->getName() << "': vtable = 0x"
<< std::hex << unsigned(vtable) << std::dec << std::endl;
}
known[vtable] = p;
p->vtable_ptr = vtable;
return p;
}
std::cerr << "UNKNOWN CLASS '" << name << "': vtable = 0x"
<< std::hex << unsigned(vtable) << std::dec << std::endl;
known[vtable] = NULL;
return NULL;
}
void virtual_identity::adjust_vtable(virtual_ptr obj, virtual_identity *main)
{
if (vtable_ptr) {
*(void**)obj = vtable_ptr;
return;
}
if (main && main != this && is_subclass(main))
return;
std::cerr << "Attempt to create class '" << getName() << "' without known vtable." << std::endl;
abort();
}
virtual_ptr virtual_identity::clone(virtual_ptr obj)
{
virtual_identity *id = get(obj);
if (!id) return NULL;
virtual_ptr copy = id->instantiate();
if (!copy) return NULL;
id->do_copy(copy, obj);
return copy;
}
bool DFHack::findBitfieldField(unsigned *idx, const std::string &name,
unsigned size, const bitfield_item_info *items)
{
for (unsigned i = 0; i < size; i++) {
if (items[i].name && items[i].name == name)
{
*idx = i;
return true;
}
}
return false;
}
void DFHack::setBitfieldField(void *p, unsigned idx, unsigned size, int value)
{
uint8_t *data = ((uint8_t*)p) + (idx/8);
unsigned shift = idx%8;
uint32_t mask = ((1<<size)-1) << shift;
uint32_t vmask = ((value << shift) & mask);
#define ACCESS(type) *(type*)data = type((*(type*)data & ~mask) | vmask)
if (!(mask & ~0xFFU)) ACCESS(uint8_t);
else if (!(mask & ~0xFFFFU)) ACCESS(uint16_t);
else ACCESS(uint32_t);
#undef ACCESS
}
int DFHack::getBitfieldField(const void *p, unsigned idx, unsigned size)
{
const uint8_t *data = ((const uint8_t*)p) + (idx/8);
unsigned shift = idx%8;
uint32_t mask = ((1<<size)-1) << shift;
#define ACCESS(type) return int((*(type*)data & mask) >> shift)
if (!(mask & ~0xFFU)) ACCESS(uint8_t);
else if (!(mask & ~0xFFFFU)) ACCESS(uint16_t);
else ACCESS(uint32_t);
#undef ACCESS
}
void DFHack::bitfieldToString(std::vector<std::string> *pvec, const void *p,
unsigned size, const bitfield_item_info *items)
{
for (unsigned i = 0; i < size; i++) {
int value = getBitfieldField(p, i, std::min(1,items[i].size));
if (value) {
std::string name = format_key(items[i].name, i);
if (items[i].size > 1)
name += stl_sprintf("=%u", value);
pvec->push_back(name);
}
if (items[i].size > 1)
i += items[i].size-1;
}
}
int DFHack::findEnumItem(const std::string &name, int size, const char *const *items)
{
for (int i = 0; i < size; i++) {
if (items[i] && items[i] == name)
return i;
}
return -1;
}
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++) {
int value = getBitfieldField(p, i, 1);
if (value)
{
int ridx = int(i) - base;
const char *name = (ridx >= 0 && ridx < size) ? items[ridx] : NULL;
pvec->push_back(format_key(name, i));
}
}
}

@ -45,6 +45,7 @@ distribution.
#include "modules/Items.h"
#include "modules/Materials.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
@ -85,6 +86,33 @@ void push_pointer_vector(lua_State *state, const std::vector<T*> &pvec)
}
}
template<class T>
T *get_checked_arg(lua_State *state, int arg)
{
luaL_checkany(state, arg);
auto ptr = Lua::GetDFObject<T>(state, arg);
if (!ptr)
luaL_argerror(state, arg, "invalid type");
return ptr;
}
int push_pos(lua_State *state, df::coord pos)
{
if (!pos.isValid())
{
lua_pushnil(state);
return 1;
}
else
{
lua_pushinteger(state, pos.x);
lua_pushinteger(state, pos.y);
lua_pushinteger(state, pos.z);
return 3;
}
}
/**************************************************
* Per-world persistent configuration storage API *
**************************************************/
@ -599,23 +627,70 @@ static const luaL_Reg dfhack_job_funcs[] = {
static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getContainer),
WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName),
WRAPM(Units, getNemesis),
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
WRAPM(Units, clearBurrowMembers),
WRAPM(Units, isInBurrow),
WRAPM(Units, setInBurrow),
{ NULL, NULL }
};
static int units_getPosition(lua_State *state)
{
return push_pos(state, Units::getPosition(get_checked_arg<df::unit>(state,1)));
}
static const luaL_Reg dfhack_units_funcs[] = {
{ "getPosition", units_getPosition },
{ NULL, NULL }
};
static bool items_moveToGround(df::item *item, df::coord pos)
{
MapExtras::MapCache mc;
return Items::moveToGround(mc, item, pos);
}
static bool items_moveToContainer(df::item *item, df::item *container)
{
MapExtras::MapCache mc;
return Items::moveToContainer(mc, item, container);
}
static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getOwner),
WRAPM(Items, setOwner),
WRAPM(Items, getContainer),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
{ NULL, NULL }
};
static int items_getPosition(lua_State *state)
{
return push_pos(state, Items::getPosition(get_checked_arg<df::item>(state,1)));
}
static int items_getContainedItems(lua_State *state)
{
std::vector<df::item*> pvec;
Items::getContainedItems(get_checked_arg<df::item>(state,1),&pvec);
push_pointer_vector(state, pvec);
return 1;
}
static const luaL_Reg dfhack_items_funcs[] = {
{ "getPosition", items_getPosition },
{ "getContainedItems", items_getContainedItems },
{ NULL, NULL }
};
static bool maps_isBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y)
{
return Maps::isBlockBurrowTile(burrow, block, df::coord2d(x,y));
@ -633,6 +708,7 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, findBurrowByName),
WRAPM(Maps, clearBurrowTiles),
WRAPN(isBlockBurrowTile, maps_isBlockBurrowTile),
WRAPN(setBlockBurrowTile, maps_setBlockBurrowTile),
WRAPM(Maps, isBurrowTile),
@ -642,14 +718,8 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
static int maps_listBurrowBlocks(lua_State *state)
{
luaL_checkany(state, 1);
auto ptr = Lua::GetDFObject<df::burrow>(state, 1);
if (!ptr)
luaL_argerror(state, 1, "invalid burrow type");
std::vector<df::map_block*> pvec;
Maps::listBurrowBlocks(&pvec, ptr);
Maps::listBurrowBlocks(&pvec, get_checked_arg<df::burrow>(state,1));
push_pointer_vector(state, pvec);
return 1;
}
@ -672,7 +742,7 @@ void OpenDFHackApi(lua_State *state)
LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module);
OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
OpenModule(state, "units", dfhack_units_module);
OpenModule(state, "items", dfhack_items_module);
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);
}

@ -745,6 +745,22 @@ static int lua_dfhack_with_suspend(lua_State *L)
return lua_gettop(L);
}
static int dfhack_open_plugin(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktype(L, 2, LUA_TSTRING);
const char *name = lua_tostring(L, 2);
PluginManager *pmgr = Core::getInstance().getPluginManager();
Plugin *plugin = pmgr->getPluginByName(name);
if (!plugin)
luaL_error(L, "plugin not found: '%s'", name);
plugin->open_lua(L, 1);
return 0;
}
static const luaL_Reg dfhack_funcs[] = {
{ "print", lua_dfhack_print },
{ "println", lua_dfhack_println },
@ -757,6 +773,7 @@ static const luaL_Reg dfhack_funcs[] = {
{ "onerror", dfhack_onerror },
{ "call_with_finalizer", dfhack_call_with_finalizer },
{ "with_suspend", lua_dfhack_with_suspend },
{ "open_plugin", dfhack_open_plugin },
{ NULL, NULL }
};

@ -1030,6 +1030,12 @@ static int meta_global_newindex(lua_State *state)
static int meta_call_function(lua_State *state)
{
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
return method_wrapper_core(state, id);
}
int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id)
{
if (lua_gettop(state) != id->getNumArgs())
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");

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

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

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

@ -61,6 +61,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/world.h"
#include "df/world_data.h"
#include "df/unit.h"
#include "df/unit_misc_trait.h"
#include "df/unit_soul.h"
#include "df/unit_skill.h"
#include "df/material.h"
@ -316,6 +317,19 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
}
}
if (mask && mask->misc_traits())
{
auto &vec = unit -> status.misc_traits;
for (size_t i = 0; i < vec.size(); i++)
{
auto trait = vec[i];
auto item = info->add_misc_traits();
item->set_id(trait->id);
item->set_value(trait->value);
}
}
if (unit->curse.add_tags1.whole ||
unit->curse.add_tags2.whole ||
unit->curse.rem_tags1.whole ||
@ -614,6 +628,20 @@ static command_result ListSquads(color_ostream &stream,
return CR_OK;
}
static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn *in)
{
for (size_t i = 0; i < in->change_size(); i++)
{
auto change = in->change(i);
auto unit = df::unit::find(change.unit_id());
if (unit)
unit->status.labors[change.labor()] = change.value();
}
return CR_OK;
}
CoreService::CoreService() {
suspend_depth = 0;
@ -637,6 +665,8 @@ CoreService::CoreService() {
addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE);
addFunction("ListUnits", ListUnits);
addFunction("ListSquads", ListSquads);
addFunction("SetUnitLabors", SetUnitLabors);
}
CoreService::~CoreService()

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

@ -65,12 +65,6 @@ namespace DFHack
{
class df_window;
}
// anon type, pretty much
struct DFLibrary;
DFLibrary * OpenPlugin (const char * filename);
void * LookupPlugin (DFLibrary * plugin ,const char * function);
void ClosePlugin (DFLibrary * plugin);
// Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
// There should never be more than one instance
@ -139,6 +133,8 @@ namespace DFHack
static void print(const char *format, ...);
static void printerr(const char *format, ...);
PluginManager *getPluginManager() { return plug_mgr; }
private:
DFHack::Console con;

@ -32,10 +32,6 @@ distribution.
#include "DataIdentity.h"
#include "LuaWrapper.h"
#ifndef BUILD_DFHACK_LIB
#error Due to export issues this header is internal to the main library.
#endif
namespace df {
// A very simple and stupid implementation of some stuff from boost
template<class U, class V> struct is_same_type { static const bool value = false; };
@ -50,7 +46,7 @@ namespace df {
template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value>
struct function_wrapper {};
class cur_lua_ostream_argument {
class DFHACK_EXPORT cur_lua_ostream_argument {
DFHack::color_ostream *out;
public:
cur_lua_ostream_argument(lua_State *state);

@ -160,8 +160,6 @@ namespace DFHack
};
}
// Due to export issues, this stuff can only work in the main dll
#ifdef BUILD_DFHACK_LIB
namespace df
{
using DFHack::function_identity_base;
@ -171,7 +169,7 @@ namespace df
using DFHack::ptr_container_identity;
using DFHack::bit_container_identity;
class number_identity_base : public primitive_identity {
class DFHACK_EXPORT number_identity_base : public primitive_identity {
const char *name;
public:
@ -197,7 +195,7 @@ namespace df
virtual void write(void *ptr, double val) { *(T*)ptr = T(val); }
};
class bool_identity : public primitive_identity {
class DFHACK_EXPORT bool_identity : public primitive_identity {
public:
bool_identity() : primitive_identity(sizeof(bool)) {};
@ -207,7 +205,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class ptr_string_identity : public primitive_identity {
class DFHACK_EXPORT ptr_string_identity : public primitive_identity {
public:
ptr_string_identity() : primitive_identity(sizeof(char*)) {};
@ -217,7 +215,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_string_identity : public DFHack::constructed_identity {
class DFHACK_EXPORT stl_string_identity : public DFHack::constructed_identity {
public:
stl_string_identity()
: constructed_identity(sizeof(std::string), &allocator_fn<std::string>)
@ -233,7 +231,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_ptr_vector_identity : public ptr_container_identity {
class DFHACK_EXPORT stl_ptr_vector_identity : public ptr_container_identity {
public:
typedef std::vector<void*> container;
@ -276,6 +274,8 @@ namespace df
}
};
// Due to export issues, this stuff can only work in the main dll
#ifdef BUILD_DFHACK_LIB
class buffer_container_identity : public container_identity {
int size;
@ -370,8 +370,9 @@ namespace df
((container*)ptr)->set(idx, val);
}
};
#endif
class stl_bit_vector_identity : public bit_container_identity {
class DFHACK_EXPORT stl_bit_vector_identity : public bit_container_identity {
public:
typedef std::vector<bool> container;
@ -400,6 +401,7 @@ namespace df
}
};
#ifdef BUILD_DFHACK_LIB
template<class T>
class enum_list_attr_identity : public container_identity {
public:
@ -421,9 +423,10 @@ namespace df
return (void*)&((container*)ptr)->items[idx];
}
};
#endif
#define NUMBER_IDENTITY_TRAITS(type) \
template<> struct identity_traits<type> { \
template<> struct DFHACK_EXPORT identity_traits<type> { \
static number_identity<type> identity; \
static number_identity_base *get() { return &identity; } \
};
@ -439,37 +442,37 @@ namespace df
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
template<> struct identity_traits<bool> {
template<> struct DFHACK_EXPORT identity_traits<bool> {
static bool_identity identity;
static bool_identity *get() { return &identity; }
};
template<> struct identity_traits<std::string> {
template<> struct DFHACK_EXPORT identity_traits<std::string> {
static stl_string_identity identity;
static stl_string_identity *get() { return &identity; }
};
template<> struct identity_traits<char*> {
template<> struct DFHACK_EXPORT identity_traits<char*> {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }
};
template<> struct identity_traits<const char*> {
template<> struct DFHACK_EXPORT identity_traits<const char*> {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }
};
template<> struct identity_traits<void*> {
template<> struct DFHACK_EXPORT identity_traits<void*> {
static pointer_identity identity;
static pointer_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<void*> > {
template<> struct DFHACK_EXPORT identity_traits<std::vector<void*> > {
static stl_ptr_vector_identity identity;
static stl_ptr_vector_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<bool> > {
template<> struct DFHACK_EXPORT identity_traits<std::vector<bool> > {
static stl_bit_vector_identity identity;
static stl_bit_vector_identity *get() { return &identity; }
};
@ -478,14 +481,17 @@ namespace df
// Container declarations
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > {
static primitive_identity *get();
};
#endif
template<class T> struct identity_traits<T *> {
static pointer_identity *get();
};
#ifdef BUILD_DFHACK_LIB
template<class T, int sz> struct identity_traits<T [sz]> {
static container_identity *get();
};
@ -493,11 +499,13 @@ namespace df
template<class T> struct identity_traits<std::vector<T> > {
static container_identity *get();
};
#endif
template<class T> struct identity_traits<std::vector<T*> > {
static stl_ptr_vector_identity *get();
};
#ifdef BUILD_DFHACK_LIB
template<class T> struct identity_traits<std::deque<T> > {
static container_identity *get();
};
@ -518,13 +526,16 @@ namespace df
template<class T> struct identity_traits<enum_list_attr<T> > {
static container_identity *get();
};
#endif
// Container definitions
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT>
inline primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
return identity_traits<FT>::get();
}
#endif
template<class T>
inline pointer_identity *identity_traits<T *>::get() {
@ -532,6 +543,7 @@ namespace df
return &identity;
}
#ifdef BUILD_DFHACK_LIB
template<class T, int sz>
inline container_identity *identity_traits<T [sz]>::get() {
static buffer_container_identity identity(sz, identity_traits<T>::get());
@ -544,6 +556,7 @@ namespace df
static stl_container_identity<container> identity("vector", identity_traits<T>::get());
return &identity;
}
#endif
template<class T>
inline stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
@ -551,6 +564,7 @@ namespace df
return &identity;
}
#ifdef BUILD_DFHACK_LIB
template<class T>
inline container_identity *identity_traits<std::deque<T> >::get() {
typedef std::deque<T> container;
@ -576,5 +590,5 @@ namespace df
static enum_list_attr_identity<T> identity(identity_traits<T>::get());
return &identity;
}
}
#endif
}

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

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

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

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

@ -17,3 +17,10 @@ inline void setassignment( int x, int y, bool bit )
else
tile_bitmask[y] &= ~(1 << x);
}
bool has_assignments()
{
for (int i = 0; i < 16; i++)
if (tile_bitmask[i])
return true;
return false;
}

@ -17,3 +17,10 @@ inline void setassignment( int x, int y, bool bit )
else
tile_bitmask[y] &= ~(1 << x);
}
bool has_assignments()
{
for (int i = 0; i < 16; i++)
if (tile_bitmask[i])
return true;
return false;
}

@ -42,6 +42,10 @@ namespace df
struct itemdef;
}
namespace MapExtras {
class MapCache;
}
/**
* \defgroup grp_items Items module and its types
* @ingroup grp_modules
@ -128,13 +132,15 @@ DFHACK_EXPORT df::unit *getOwner(df::item *item);
DFHACK_EXPORT bool setOwner(df::item *item, df::unit *unit);
/// which item is it contained in?
DFHACK_EXPORT int32_t getItemContainerID(const df::item * item);
DFHACK_EXPORT df::item *getItemContainer(const df::item * item);
DFHACK_EXPORT df::item *getContainer(df::item *item);
/// which items does it contain?
DFHACK_EXPORT bool getContainedItems(const df::item * item, /*output*/ std::vector<int32_t> &items);
DFHACK_EXPORT void getContainedItems(df::item *item, /*output*/ std::vector<df::item*> *items);
/// Returns the true position of the item.
DFHACK_EXPORT df::coord getPosition(df::item *item);
DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos);
DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container);
/// read item references, filtered by class
DFHACK_EXPORT bool readItemRefs(const df::item * item, const df::general_ref_type type,
/*output*/ std::vector<int32_t> &values);
}
}

@ -262,12 +262,20 @@ extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, d
DFHACK_EXPORT df::burrow *findBurrowByName(std::string name);
void listBurrowBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow);
DFHACK_EXPORT void listBurrowBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow);
DFHACK_EXPORT void clearBurrowTiles(df::burrow *burrow);
df::block_burrow *getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create = false);
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);
bool isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile);
bool setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable);
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);

@ -193,6 +193,11 @@ DFHACK_EXPORT int32_t GetDwarfCivId ( void );
DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target);
/// Returns the true position of the unit (non-trivial in case of caged).
DFHACK_EXPORT df::coord getPosition(df::unit *unit);
DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit);
@ -201,6 +206,10 @@ DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isDead(df::unit *unit);
DFHACK_EXPORT bool isAlive(df::unit *unit);
DFHACK_EXPORT bool isSane(df::unit *unit);
DFHACK_EXPORT bool isCitizen(df::unit *unit);
DFHACK_EXPORT bool isDwarf(df::unit *unit);
DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow);
DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow);
DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable);

@ -55,6 +55,10 @@ function mkmodule(module,env)
error("Not a table in package.loaded["..module.."]")
end
end
local plugname = string.match(module,'^plugins%.(%w+)$')
if plugname then
dfhack.open_plugin(pkg,plugname)
end
setmetatable(pkg, { __index = (env or _G) })
return pkg
end
@ -86,6 +90,21 @@ function copyall(table)
return rv
end
function pos2xyz(pos)
local x = pos.x
if x and x ~= -30000 then
return x, pos.y, pos.z
end
end
function xyz2pos(x,y,z)
if x then
return {x=x,y=y,z=z}
else
return {x=-30000,y=-30000,z=-30000}
end
end
function dfhack.persistent:__tostring()
return "<persistent "..self.entry_id..":"..self.key.."=\""
..self.value.."\":"..table.concat(self.ints,",")..">"

@ -39,6 +39,7 @@ using namespace std;
#include "modules/Materials.h"
#include "modules/Items.h"
#include "modules/Units.h"
#include "modules/MapCache.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "Error.h"
@ -46,6 +47,7 @@ using namespace std;
#include "df/world.h"
#include "df/item.h"
#include "df/building.h"
#include "df/tool_uses.h"
#include "df/itemdef_weaponst.h"
#include "df/itemdef_trapcompst.h"
@ -65,6 +67,8 @@ using namespace std;
#include "df/job_item.h"
#include "df/general_ref.h"
#include "df/general_ref_unit_itemownerst.h"
#include "df/general_ref_contains_itemst.h"
#include "df/general_ref_contained_in_itemst.h"
using namespace DFHack;
using namespace df::enums;
@ -454,6 +458,8 @@ bool Items::setOwner(df::item *item, df::unit *unit)
vector_erase_at(item->itemrefs, i);
}
item->flags.bits.owned = false;
if (unit)
{
auto ref = df::allocate<df::general_ref_unit_itemownerst>();
@ -466,50 +472,193 @@ bool Items::setOwner(df::item *item, df::unit *unit)
insert_into_vector(unit->owned_items, item->id);
item->itemrefs.push_back(ref);
}
else
item->flags.bits.owned = false;
return true;
}
int32_t Items::getItemContainerID(const df::item * item)
df::item *Items::getContainer(df::item * item)
{
CHECK_NULL_POINTER(item);
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getID();
return ref->getItem();
}
return -1;
return NULL;
}
df::item *Items::getItemContainer(const df::item * item)
void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
{
CHECK_NULL_POINTER(item);
items->clear();
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();
if (ref->getType() != general_ref_type::CONTAINS_ITEM)
continue;
auto child = ref->getItem();
if (child)
items->push_back(child);
}
return NULL;
}
bool Items::getContainedItems(const df::item * item, std::vector<int32_t> &items)
df::coord Items::getPosition(df::item *item)
{
CHECK_NULL_POINTER(item);
if (item->flags.bits.in_inventory ||
item->flags.bits.in_chest ||
item->flags.bits.in_building)
{
for (size_t i = 0; i < item->itemrefs.size(); i++)
{
df::general_ref *ref = item->itemrefs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
if (auto item2 = ref->getItem())
return getPosition(item2);
break;
case general_ref_type::UNIT_HOLDER:
if (auto unit = ref->getUnit())
return Units::getPosition(unit);
break;
case general_ref_type::BUILDING_HOLDER:
if (auto bld = ref->getBuilding())
return df::coord(bld->centerx, bld->centery, bld->z);
break;
}
}
}
return item->pos;
}
static void removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id)
{
return readItemRefs(item, general_ref_type::CONTAINS_ITEM, items);
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;
}
}
bool Items::readItemRefs(const df::item * item, df::general_ref_type type, std::vector<int32_t> &values)
static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{
values.clear();
if (item->flags.bits.on_ground)
{
if (!mc.removeItemOnGround(item))
Core::printerr("Item was marked on_ground, but not in block: %d (%d,%d,%d)\n",
item->id, item->pos.x, item->pos.y, item->pos.z);
for (size_t i = 0; i < item->itemrefs.size(); i++)
item->flags.bits.on_ground = false;
return true;
}
else if (item->flags.bits.in_inventory)
{
df::general_ref *ref = item->itemrefs[i];
if (ref->getType() == type)
values.push_back(ref->getID());
bool found = false;
for (int i = item->itemrefs.size()-1; i >= 0; i--)
{
df::general_ref *ref = item->itemrefs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
if (auto item2 = ref->getItem())
{
item2->flags.bits.weight_computed = false;
removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id);
}
break;
case general_ref_type::UNIT_HOLDER:
case general_ref_type::BUILDING_HOLDER:
return false;
default:
continue;
}
found = true;
vector_erase_at(item->itemrefs, i);
delete ref;
}
if (!found)
return false;
item->flags.bits.in_inventory = false;
return true;
}
else
return false;
}
static void putOnGround(MapExtras::MapCache &mc, df::item *item, df::coord pos)
{
item->pos = pos;
item->flags.bits.on_ground = true;
if (!mc.addItemOnGround(item))
Core::printerr("Could not add item %d to ground at (%d,%d,%d)\n",
item->id, pos.x, pos.y, pos.z);
}
bool DFHack::Items::moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos)
{
CHECK_NULL_POINTER(item);
if (!detachItem(mc, item))
return false;
putOnGround(mc, item, pos);
return true;
}
bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container)
{
CHECK_NULL_POINTER(item);
CHECK_NULL_POINTER(container);
if (!detachItem(mc, item))
return false;
auto ref1 = df::allocate<df::general_ref_contains_itemst>();
auto ref2 = df::allocate<df::general_ref_contained_in_itemst>();
if (!ref1 || !ref2)
{
delete ref1; delete ref2;
Core::printerr("Could not allocate container refs.\n");
putOnGround(mc, item, getPosition(container));
return false;
}
return !values.empty();
item->pos = container->pos;
item->flags.bits.in_inventory = true;
container->flags.bits.container = true;
container->flags.bits.weight_computed = false;
ref1->item_id = item->id;
container->itemrefs.push_back(ref1);
ref2->item_id = container->id;
item->itemrefs.push_back(ref2);
return true;
}

@ -596,7 +596,7 @@ void MapExtras::Block::init_item_counts()
if (item_counts) return;
item_counts = new T_item_counts[16];
memset(item_counts, 0, sizeof(T_item_counts));
memset(item_counts, 0, sizeof(T_item_counts)*16);
if (!block) return;
@ -715,6 +715,42 @@ void Maps::listBurrowBlocks(std::vector<df::map_block*> *pvec, df::burrow *burro
}
}
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);
@ -754,6 +790,36 @@ df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *bl
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);
@ -774,8 +840,13 @@ bool Maps::setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coor
auto mask = getBlockBurrowMask(burrow, block, enable);
if (mask)
{
mask->setassignment(tile & 15, enable);
if (!enable && !mask->has_assignments())
deleteBlockBurrowMask(burrow, block, mask);
}
return true;
}

@ -40,6 +40,7 @@ using namespace std;
// we connect to those
#include "modules/Units.h"
#include "modules/Items.h"
#include "modules/Materials.h"
#include "modules/Translation.h"
#include "ModuleFactory.h"
@ -500,6 +501,34 @@ void Units::CopyNameTo(df::unit * creature, df::language_name * target)
Translation::copyName(&creature->name, target);
}
df::coord Units::getPosition(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
if (unit->flags1.bits.caged)
{
auto cage = getContainer(unit);
if (cage)
return Items::getPosition(cage);
}
return unit->pos;
}
df::item *Units::getContainer(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
for (size_t i = 0; i < unit->refs.size(); i++)
{
df::general_ref *ref = unit->refs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getItem();
}
return NULL;
}
void Units::setNickname(df::unit *unit, std::string nick)
{
CHECK_NULL_POINTER(unit);
@ -624,6 +653,51 @@ bool DFHack::Units::isSane(df::unit *unit)
return true;
}
bool DFHack::Units::isCitizen(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return unit->civ_id == ui->civ_id &&
!unit->flags1.bits.merchant &&
!unit->flags1.bits.diplomat &&
!unit->flags2.bits.resident &&
!unit->flags1.bits.dead &&
!unit->flags3.bits.ghostly;
}
bool DFHack::Units::isDwarf(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return unit->race == ui->race_id;
}
void DFHack::Units::clearBurrowMembers(df::burrow *burrow)
{
CHECK_NULL_POINTER(burrow);
for (size_t i = 0; i < burrow->units.size(); i++)
{
auto unit = df::unit::find(burrow->units[i]);
if (unit)
erase_from_vector(unit->burrows, burrow->id);
}
burrow->units.clear();
// Sync ui if active
if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
{
auto &sel = ui->burrows.sel_units;
for (size_t i = 0; i < sel.size(); i++)
sel[i] = false;
}
}
bool DFHack::Units::isInBurrow(df::unit *unit, df::burrow *burrow)
{
CHECK_NULL_POINTER(unit);

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

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

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

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

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

@ -182,15 +182,7 @@ static command_result autodump_main(color_ostream &out, vector <string> & parame
// Don't move items if they're already at the cursor
if (pos_cursor != pos_item)
{
if (!MC.removeItemOnGround(itm))
out.printerr("Item %d wasn't in the source block.\n", itm->id);
itm->pos = pos_cursor;
if (!MC.addItemOnGround(itm))
out.printerr("Could not add item %d to destination block.\n", itm->id);
}
Items::moveToGround(MC, itm, pos_cursor);
}
else // destroy
{

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

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

@ -890,6 +890,8 @@ df::general_ref_building_civzone_assignedst * createCivzoneRef()
return newref;
}
bool isInBuiltCage(df::unit* unit);
// check if assigned to pen, pit, (built) cage or chain
// note: BUILDING_CAGED is not set for animals (maybe it's used for dwarves who get caged as sentence)
// animals in cages (no matter if built or on stockpile) get the ref CONTAINED_IN_ITEM instead