Petr Mrázek 2012-04-15 00:13:39 +02:00
commit 45ca8c6ac1
24 changed files with 1201 additions and 49 deletions

@ -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.
@ -701,7 +713,7 @@ Items module
* ``dfhack.items.getPosition(item)``
Returns true *x,y,z* of the item.
Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory.
* ``dfhack.items.getOwner(item)``
@ -768,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>
@ -930,7 +939,7 @@ a lua list containing them.</p>
<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.</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>
@ -983,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>

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

@ -627,17 +627,29 @@ 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;
@ -696,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),
@ -729,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, "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");

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

@ -139,6 +139,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,6 +33,8 @@ distribution.
#include "RemoteClient.h"
typedef struct lua_State lua_State;
struct DFLibrary;
namespace tthread
{
@ -49,6 +51,7 @@ namespace DFHack
class PluginManager;
class virtual_identity;
class RPCService;
class function_identity_base;
enum state_change_event
{
@ -56,7 +59,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 +115,7 @@ namespace DFHack
{
struct RefLock;
struct RefAutolock;
struct RefAutoinc;
enum plugin_state
{
PS_UNLOADED,
@ -138,6 +152,9 @@ namespace DFHack
{
return name;
}
void open_lua(lua_State *state, int table);
private:
RefLock * access;
std::vector <PluginCommand> commands;
@ -147,6 +164,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 +236,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)
{

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

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

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

@ -529,7 +529,7 @@ df::coord Items::getPosition(df::item *item)
case general_ref_type::UNIT_HOLDER:
if (auto unit = ref->getUnit())
return unit->pos;
return Units::getPosition(unit);
break;
case general_ref_type::BUILDING_HOLDER:
@ -579,7 +579,11 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{
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:
@ -649,6 +653,8 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df:
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;

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

@ -36,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)
@ -98,6 +102,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(feature feature.cpp)
DFHACK_PLUGIN(lair lair.cpp)
DFHACK_PLUGIN(zone zone.cpp)
DFHACK_PLUGIN(burrows burrows.cpp)
# not yet. busy with other crud again...
#DFHACK_PLUGIN(versionosd versionosd.cpp)
endif()

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