diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cbbe6a1f..1cbf9ed24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/build/generate-MSVC-all-breakfast.bat b/build/generate-MSVC-all-breakfast.bat new file mode 100644 index 000000000..08c5b03ff --- /dev/null +++ b/build/generate-MSVC-all-breakfast.bat @@ -0,0 +1,8 @@ +@echo off +IF EXIST DF_PATH.txt SET /P _DF_PATH= 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; diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 471ffd550..5129ba648 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -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 } }; diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 40269da80..f5ba565d5 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -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"); diff --git a/library/PlugLoad-linux.cpp b/library/PlugLoad-linux.cpp index aaf7b2c33..69945c6f4 100644 --- a/library/PlugLoad-linux.cpp +++ b/library/PlugLoad-linux.cpp @@ -14,7 +14,7 @@ #include #include "DFHack.h" -#include "Core.h" +#include "PluginManager.h" #include "Hooks.h" #include diff --git a/library/PlugLoad-windows.cpp b/library/PlugLoad-windows.cpp index eadc9343d..f9a75df97 100644 --- a/library/PlugLoad-windows.cpp +++ b/library/PlugLoad-windows.cpp @@ -28,7 +28,7 @@ distribution. #include #include #include -#include "Core.h" +#include "PluginManager.h" #include "Hooks.h" #include diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index a7faabeec..4d0c06ddf 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -32,6 +32,8 @@ distribution. #include "DataDefs.h" #include "MiscUtils.h" +#include "LuaWrapper.h" + using namespace DFHack; #include @@ -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 diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 00344d6a2..c8af46cbf 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -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() diff --git a/library/include/Core.h b/library/include/Core.h index 290a324b9..f8ac8e783 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -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; diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h index d78fe9e27..a949e784b 100644 --- a/library/include/DataFuncs.h +++ b/library/include/DataFuncs.h @@ -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 struct is_same_type { static const bool value = false; }; @@ -50,7 +46,7 @@ namespace df { template::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); diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index bab87e1bb..279400f74 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -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) @@ -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 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 container; @@ -400,6 +401,7 @@ namespace df } }; +#ifdef BUILD_DFHACK_LIB template 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 { \ + template<> struct DFHACK_EXPORT identity_traits { \ static number_identity 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 { + template<> struct DFHACK_EXPORT identity_traits { static bool_identity identity; static bool_identity *get() { return &identity; } }; - template<> struct identity_traits { + template<> struct DFHACK_EXPORT identity_traits { static stl_string_identity identity; static stl_string_identity *get() { return &identity; } }; - template<> struct identity_traits { + template<> struct DFHACK_EXPORT identity_traits { static ptr_string_identity identity; static ptr_string_identity *get() { return &identity; } }; - template<> struct identity_traits { + template<> struct DFHACK_EXPORT identity_traits { static ptr_string_identity identity; static ptr_string_identity *get() { return &identity; } }; - template<> struct identity_traits { + template<> struct DFHACK_EXPORT identity_traits { static pointer_identity identity; static pointer_identity *get() { return &identity; } }; - template<> struct identity_traits > { + template<> struct DFHACK_EXPORT identity_traits > { static stl_ptr_vector_identity identity; static stl_ptr_vector_identity *get() { return &identity; } }; - template<> struct identity_traits > { + template<> struct DFHACK_EXPORT identity_traits > { 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 struct identity_traits > { static primitive_identity *get(); }; +#endif template struct identity_traits { static pointer_identity *get(); }; +#ifdef BUILD_DFHACK_LIB template struct identity_traits { static container_identity *get(); }; @@ -493,11 +499,13 @@ namespace df template struct identity_traits > { static container_identity *get(); }; +#endif template struct identity_traits > { static stl_ptr_vector_identity *get(); }; +#ifdef BUILD_DFHACK_LIB template struct identity_traits > { static container_identity *get(); }; @@ -518,13 +526,16 @@ namespace df template struct identity_traits > { static container_identity *get(); }; +#endif // Container definitions +#ifdef BUILD_DFHACK_LIB template inline primitive_identity *identity_traits >::get() { return identity_traits::get(); } +#endif template inline pointer_identity *identity_traits::get() { @@ -532,6 +543,7 @@ namespace df return &identity; } +#ifdef BUILD_DFHACK_LIB template inline container_identity *identity_traits::get() { static buffer_container_identity identity(sz, identity_traits::get()); @@ -544,6 +556,7 @@ namespace df static stl_container_identity identity("vector", identity_traits::get()); return &identity; } +#endif template inline stl_ptr_vector_identity *identity_traits >::get() { @@ -551,6 +564,7 @@ namespace df return &identity; } +#ifdef BUILD_DFHACK_LIB template inline container_identity *identity_traits >::get() { typedef std::deque container; @@ -576,5 +590,5 @@ namespace df static enum_list_attr_identity identity(identity_traits::get()); return &identity; } -} #endif +} diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 479ca0d67..2304b940d 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -30,6 +30,7 @@ distribution. #include #include "DataDefs.h" +#include "PluginManager.h" #include #include @@ -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); diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 8f02099be..080e66e66 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -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 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 lua_commands; + static int lua_cmd_wrapper(lua_State *state); + + struct LuaFunction { + Plugin *owner; + std::string name; + function_identity_base *identity; + }; + std::map 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 &); 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 } diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 626f60bec..7230a12aa 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -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 diff --git a/library/proto/Basic.proto b/library/proto/Basic.proto index 246fba22e..c387bef01 100755 --- a/library/proto/Basic.proto +++ b/library/proto/Basic.proto @@ -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; +}; diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto index 3072f9cad..a5a07aa1c 100755 --- a/library/proto/BasicApi.proto +++ b/library/proto/BasicApi.proto @@ -99,4 +99,9 @@ message ListUnitsOut { message ListSquadsIn {} message ListSquadsOut { repeated BasicSquadInfo value = 1; -} +}; + +// RPC SetUnitLabors : SetUnitLaborsIn -> EmptyMessage +message SetUnitLaborsIn { + repeated UnitLaborState change = 1; +}; diff --git a/library/xml b/library/xml index 73cd75788..a54516705 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 73cd75788fc45b13790b4e6bb4d3f33691224d30 +Subproject commit a545167050fee9eedd5e319b518d961f933154a3 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 461988074..b520c5f19 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -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,7 +102,8 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(feature feature.cpp) DFHACK_PLUGIN(lair lair.cpp) DFHACK_PLUGIN(zone zone.cpp) - DFHACK_PLUGIN(burrows burrows.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() diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index 358f93d00..e68684bf7 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -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 diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index e690e9412..b01157c87 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -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 diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index c973405ee..74cb57c45 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -2,6 +2,9 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" +#include "Error.h" + +#include "DataFuncs.h" #include "modules/Gui.h" #include "modules/Job.h" @@ -420,6 +423,9 @@ static df::burrow *findByName(color_ostream &out, std::string name, bool silent static void copyUnits(df::burrow *target, df::burrow *source, bool enable) { + CHECK_NULL_POINTER(target); + CHECK_NULL_POINTER(source); + if (source == target) { if (!enable) @@ -439,6 +445,9 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool 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) @@ -480,6 +489,8 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable) 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++) @@ -512,6 +523,8 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas 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); @@ -538,6 +551,14 @@ static bool setTilesByKeyword(df::burrow *target, std::string name, bool 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 ¶meters) { CoreSuspender suspend; diff --git a/plugins/lua/burrows.lua b/plugins/lua/burrows.lua new file mode 100644 index 000000000..b1d51f6a6 --- /dev/null +++ b/plugins/lua/burrows.lua @@ -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 \ No newline at end of file diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 4c100cc48..31969bde7 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -227,6 +227,10 @@ static command_result tweak(color_ostream &out, vector ¶meters) unit->flags1.bits.forest = 0; if(unit->civ_id != df::global::ui->civ_id) unit->civ_id = df::global::ui->civ_id; + if(unit->profession == df::profession::MERCHANT) + unit->profession = df::profession::TRADER; + if(unit->profession2 == df::profession::MERCHANT) + unit->profession2 = df::profession::TRADER; return fix_clothing_ownership(out, unit); } else diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 1e90aa1cf..9f71b85d8 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -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 @@ -905,7 +907,7 @@ bool isAssigned(df::unit* unit) if( rtype == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED || rtype == df::general_ref_type::BUILDING_CAGED || rtype == df::general_ref_type::BUILDING_CHAIN - || (rtype == df::general_ref_type::CONTAINED_IN_ITEM && isBuiltCageAtPos(unit->pos)) + || (rtype == df::general_ref_type::CONTAINED_IN_ITEM && isInBuiltCage(unit)) ) { assigned = true; @@ -958,10 +960,10 @@ bool isInBuiltCage(df::unit* unit) df::building* building = world->buildings.all[b]; if( building->getType() == building_type::Cage) { - df::building_cagest* oldcage = (df::building_cagest*) building; - for(size_t oc=0; ocassigned_creature.size(); oc++) + df::building_cagest* cage = (df::building_cagest*) building; + for(size_t c=0; cassigned_creature.size(); c++) { - if(oldcage->assigned_creature[oc] == unit->id) + if(cage->assigned_creature[c] == unit->id) { caged = true; break; @@ -2769,7 +2771,9 @@ command_result autoButcher( color_ostream &out, bool verbose = false ) || !isTame(unit) || isWar(unit) // ignore war dogs etc || isHunter(unit) // ignore hunting dogs etc - || (isContainedInItem(unit) && hasValidMapPos(unit) && isBuiltCageAtPos(unit->pos)) + // ignore creatures in built cages to leave zoos alone + // (TODO: allow some kind of slaughter cages which you can place near the butcher) + || (isContainedInItem(unit) && isInBuiltCage(unit)) || unit->name.has_name ) continue;