Merge remote-tracking branch 'angavrilov/master' into 0.34.11-r4

Conflicts:
	NEWS
	library/xml
	plugins/CMakeLists.txt
	plugins/autoSyndrome.cpp
develop
expwnent 2013-10-19 20:19:29 -04:00
commit 57fc0f3e89
71 changed files with 2136 additions and 209 deletions

@ -104,9 +104,9 @@ OPTION(BUILD_PLUGINS "Build the plugins." ON)
# enable C++11 features
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable")
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x")
SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g -Wall -Wno-unused-variable")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -m32 -march=i686 -mtune=generic")
ELSEIF(MSVC)
# for msvc, tell it to always use 8-byte pointers to member functions to avoid confusion
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg /vmm /MP")

@ -717,6 +717,57 @@ Functions:
Checks if the material matches job_material_category or job_item.
Accept dfhack_material_category auto-assign table.
Random number generation
------------------------
* ``dfhack.random.new([seed[,perturb_count]])``
Creates a new random number generator object. Without any
arguments, the object is initialized using current time.
Otherwise, the seed must be either a non-negative integer,
or a list of such integers. The second argument may specify
the number of additional randomization steps performed to
improve the initial state.
* ``rng:init([seed[,perturb_count]])``
Re-initializes an already existing random number generator object.
* ``rng:random([limit])``
Returns a random integer. If ``limit`` is specified, the value
is in the range [0, limit); otherwise it uses the whole 32-bit
unsigned integer range.
* ``rng:drandom()``
Returns a random floating-point number in the range [0,1).
* ``rng:drandom0()``
Returns a random floating-point number in the range (0,1).
* ``rng:drandom1()``
Returns a random floating-point number in the range [0,1].
* ``rng:unitrandom()``
Returns a random floating-point number in the range [-1,1].
* ``rng:unitvector([size])``
Returns multiple values that form a random vector of length 1,
uniformly distributed over the corresponding sphere surface.
The default size is 3.
* ``fn = rng:perlin([dim]); fn(x[,y[,z]])``
Returns a closure that computes a classical Perlin noise function
of dimension *dim*, initialized from this random generator.
Dimension may be 1, 2 or 3 (default).
C++ function wrappers
=====================

@ -11,10 +11,12 @@ DFHack future
- multicmd: run a sequence of dfhack commands, separated by ';'
- autobutcher: A GUI front-end for the autobutcher plugin.
- invasionNow: trigger an invasion, or many
- startdwarf: change the number of dwarves for a new embark
Misc improvements:
- exterminate: renamed from slayrace, add help message, add butcher mode
- ruby: add df.dfhack_run "somecommand"
- magmasource: rename to source, allow water/magma sources/drains
- magmasource: rename to 'source', allow water/magma sources/drains
- core: fix SC_WORLD_(UN)LOADED event for arena mode
- autoSyndrome:
disable by default
reorganized special tags
@ -23,6 +25,8 @@ DFHack future
- syndromeTrigger: replaces and extends trueTransformation. Can trigger things when syndromes are added for any reason.
- fastdwarf: fixed bug involving fastdwarf and teledwarf being on at the same time
- workNow: can optionally look for jobs when jobs are completed
New tweaks:
- hive-crash: Prevent crash if bees die in a hive with ungathered products (bug 6368).
New plugins:
- buildingplan: Place furniture before it's built
- outsideOnly: make raw-specified buildings impossible to build inside

@ -209,6 +209,25 @@ or is a prefix ending at a '/' boundary would be considered for execution, i.e.
for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@foo/bar``,
``@foo`` or none.
Enabling plugins
================
Many plugins can be in a distinct enabled or disabled state. Some of
them activate and deactivate automatically depending on the contents
of the world raws. Others store their state in world data. However a
number of them have to be enabled globally, and the init file is the
right place to do it.
Most of such plugins support the built-in ``enable`` and ``disable``
commands. Calling them at any time without arguments prints a list
of enabled and disabled plugins, and shows whether that can be changed
through the same commands.
To enable or disable plugins that support this, use their names as
arguments for the command::
enable manipulator search
========
Commands
@ -1279,6 +1298,9 @@ Subcommands that persist until disabled or DF quit:
(i.e. the more units you have, the slower it becomes), and making
the units spar more.
:hive-crash: The hive code crashes if there are ungathered products in a hive without bees (bug 6368).
This tweak prevents it by auto-gathering the products if this happens.
fix-armory
----------

@ -147,8 +147,27 @@ tweak military-color-assigned
# remove inverse dependency of squad training speed on unit list size and use more sparring
tweak military-training
# enable autoSyndrome
autoSyndrome enable
# prevent crash if bees die in a hive with ungathered products by insta-gathering them
tweak hive-crash
###########################
# Globally acting plugins #
###########################
# Dwarf Manipulator (simple in-game Dwarf Therapist replacement)
enable manipulator
# Search tool in various screens (by falconne)
enable search
# Improved build material selection interface (by falconne)
enable automaterial
# Other interface improvement tools
#enable dwarfmonitor mousequery autotrade buildingplan resume zone
# Auto Syndrome
#autoSyndrome enable
###########
# Scripts #

@ -120,6 +120,7 @@ include/modules/Maps.h
include/modules/MapCache.h
include/modules/Materials.h
include/modules/Notes.h
include/modules/Random.h
include/modules/Screen.h
include/modules/Translation.h
include/modules/Vermin.h
@ -142,6 +143,7 @@ modules/kitchen.cpp
modules/Maps.cpp
modules/Materials.cpp
modules/Notes.cpp
modules/Random.cpp
modules/Screen.cpp
modules/Translation.cpp
modules/Vermin.cpp
@ -362,7 +364,7 @@ if(BUILD_DEVEL)
# without the '/', the directory itself is installed
install(DIRECTORY include/
DESTINATION ${DFHACK_INCLUDES_DESTINATION}
FILES_MATCHING PATTERN "*.h" ) #linux: include
FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" ) #linux: include
# Building the docs
IF(BUILD_DOXYGEN)
add_subdirectory (doc)

@ -259,7 +259,7 @@ static void listScripts(PluginManager *plug_mgr, std::map<string,string> &pset,
pset[prefix + files[i].substr(0, files[i].size()-4)] = help;
}
else if (plug_mgr->eval_ruby && hasEnding(files[i], ".rb"))
else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && hasEnding(files[i], ".rb"))
{
std::string help = getScriptHelp(path + files[i], "# ");
@ -312,6 +312,9 @@ static command_result runLuaScript(color_ostream &out, std::string name, vector<
static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr, std::string name, vector<string> &args)
{
if (!plug_mgr->ruby || !plug_mgr->ruby->is_enabled())
return CR_FAILURE;
std::string rbcmd = "$script_args = [";
for (size_t i = 0; i < args.size(); i++)
rbcmd += "'" + args[i] + "', ";
@ -319,7 +322,7 @@ static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr,
rbcmd += "catch(:script_finished) { load './hack/scripts/" + name + ".rb' }";
return plug_mgr->eval_ruby(out, rbcmd.c_str());
return plug_mgr->ruby->eval_ruby(out, rbcmd.c_str());
}
command_result Core::runCommand(color_ostream &out, const std::string &command)
@ -450,7 +453,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK;
}
if (plug_mgr->eval_ruby && fileExists(filename + ".rb"))
if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb"))
{
string help = getScriptHelp(filename + ".rb", "# ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str());
@ -544,6 +547,56 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
}
}
}
else if( first == "enable" || first == "disable" )
{
CoreSuspender suspend;
bool enable = (first == "enable");
if(parts.size())
{
command_result res = CR_OK;
for (size_t i = 0; i < parts.size(); i++)
{
Plugin * plug = plug_mgr->getPluginByName(parts[i]);
if(!plug)
{
res = CR_NOT_FOUND;
con.printerr("No such plugin: %s\n", parts[i].c_str());
}
else if (!plug->can_set_enabled())
{
res = CR_NOT_IMPLEMENTED;
con.printerr("Cannot %s plugin: %s\n", first.c_str(), parts[i].c_str());
}
else
{
res = plug->set_enabled(con, enable);
if (res != CR_OK || plug->is_enabled() != enable)
con.printerr("Could not %s plugin: %s\n", first.c_str(), parts[i].c_str());
}
}
return res;
}
else
{
for(size_t i = 0; i < plug_mgr->size();i++)
{
Plugin * plug = (plug_mgr->operator[](i));
if (!plug->can_be_enabled()) continue;
con.print(
"%20s\t%-3s%s\n",
(plug->getName()+":").c_str(),
plug->is_enabled() ? "on" : "off",
plug->can_set_enabled() ? "" : " (controlled elsewhere)"
);
}
}
}
else if(first == "ls" || first == "dir")
{
bool all = false;
@ -584,6 +637,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" load PLUGIN|all - Load a plugin by name or load all possible plugins.\n"
" unload PLUGIN|all - Unload a plugin or all loaded plugins.\n"
" reload PLUGIN|all - Reload a plugin or all loaded plugins.\n"
" enable/disable PLUGIN - Enable or disable a plugin if supported.\n"
"\n"
"plugins:\n"
);
@ -716,7 +770,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
if (fileExists(filename + ".lua"))
res = runLuaScript(con, first, parts);
else if (plug_mgr->eval_ruby && fileExists(filename + ".rb"))
else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb"))
res = runRubyScript(con, plug_mgr, first, parts);
else if (try_autocomplete(con, first, completed))
return CR_NOT_IMPLEMENTED;// runCommand(con, completed, parts);
@ -1160,7 +1214,8 @@ void Core::doUpdate(color_ostream &out, bool first_update)
{
df::world_data *wdata = df::global::world->world_data;
// when the game is unloaded, world_data isn't deleted, but its contents are
if (wdata && !wdata->sites.empty())
// regions work to detect arena too
if (wdata && !wdata->regions.empty())
new_wdata = wdata;
new_mapdata = df::global::world->map.block_index;
}

@ -51,6 +51,7 @@ distribution.
#include "modules/Burrows.h"
#include "modules/Buildings.h"
#include "modules/Constructions.h"
#include "modules/Random.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
@ -92,6 +93,10 @@ using namespace DFHack;
using namespace DFHack::LuaWrapper;
using Screen::Pen;
using Random::MersenneRNG;
using Random::PerlinNoise1D;
using Random::PerlinNoise2D;
using Random::PerlinNoise3D;
void dfhack_printerr(lua_State *S, const std::string &str);
@ -739,12 +744,10 @@ void Lua::Push(lua_State *L, const Screen::Pen &info)
return;
}
void *pdata = lua_newuserdata(L, sizeof(Pen));
new (L) Pen(info);
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
lua_setmetatable(L, -2);
new (pdata) Pen(info);
}
static Pen *check_pen_native(lua_State *L, int index)
@ -1035,6 +1038,200 @@ static void OpenPen(lua_State *state)
lua_pop(state, 1);
}
/********************
* Random generator *
********************/
static int DFHACK_RANDOM_TOKEN = 0;
static MersenneRNG *check_random_native(lua_State *L, int index)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN);
if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2))
luaL_argerror(L, index, "not a random generator object");
lua_pop(L, 2);
return (MersenneRNG*)lua_touserdata(L, index);
}
static int dfhack_random_init(lua_State *L)
{
lua_settop(L, 3);
MersenneRNG *prng = check_random_native(L, 1);
if (lua_isnil(L, 2))
prng->init();
else
{
std::vector<uint32_t> data;
int tcnt = luaL_optint(L, 3, 1);
if (lua_isnumber(L, 2))
data.push_back(lua_tounsigned(L, 2));
else if (lua_istable(L, 2))
{
int cnt = lua_rawlen(L, 2);
if (cnt <= 0)
luaL_argerror(L, 2, "empty list in dfhack.random.init");
for (int i = 1; i <= cnt; i++)
{
lua_rawgeti(L, 2, i);
if (!lua_isnumber(L, -1))
luaL_argerror(L, 2, "not a number in dfhack.random.init argument");
data.push_back(lua_tounsigned(L, -1));
lua_pop(L, 1);
}
}
else
luaL_argerror(L, 2, "dfhack.random.init argument not number or table");
prng->init(data.data(), data.size(), tcnt);
}
lua_settop(L, 1);
return 1;
}
static int dfhack_random_new(lua_State *L)
{
new (L) MersenneRNG();
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN);
lua_setmetatable(L, -2);
lua_insert(L, 1);
return dfhack_random_init(L);
}
static int dfhack_random_random(lua_State *L)
{
MersenneRNG *prng = check_random_native(L, 1);
lua_settop(L, 2);
if (lua_gettop(L) < 2 || lua_isnil(L, 2))
lua_pushunsigned(L, prng->random());
else
lua_pushunsigned(L, prng->random(luaL_optunsigned(L, 2, 0)));
return 1;
}
static int dfhack_random_drandom(lua_State *L)
{
lua_pushnumber(L, check_random_native(L, 1)->drandom());
return 1;
}
static int dfhack_random_drandom0(lua_State *L)
{
lua_pushnumber(L, check_random_native(L, 1)->drandom0());
return 1;
}
static int dfhack_random_drandom1(lua_State *L)
{
lua_pushnumber(L, check_random_native(L, 1)->drandom1());
return 1;
}
static int dfhack_random_unitrandom(lua_State *L)
{
lua_pushnumber(L, check_random_native(L, 1)->unitrandom());
return 1;
}
static int dfhack_random_unitvector(lua_State *L)
{
MersenneRNG *prng = check_random_native(L, 1);
int size = luaL_optint(L, 2, 3);
if (size <= 0 || size > 32)
luaL_argerror(L, 2, "vector size must be positive");
luaL_checkstack(L, size, "not enough stack in dfhack.random.unitvector");
std::vector<double> buf(size);
prng->unitvector(buf.data(), size);
for (int i = 0; i < size; i++)
lua_pushnumber(L, buf[i]);
return size;
}
static int eval_perlin_1(lua_State *L)
{
auto &gen = *(PerlinNoise1D<float>*)lua_touserdata(L, lua_upvalueindex(1));
lua_pushnumber(L, gen(luaL_checknumber(L, 1)));
return 1;
}
static int eval_perlin_2(lua_State *L)
{
auto &gen = *(PerlinNoise2D<float>*)lua_touserdata(L, lua_upvalueindex(1));
lua_pushnumber(L, gen(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int eval_perlin_3(lua_State *L)
{
auto &gen = *(PerlinNoise3D<float>*)lua_touserdata(L, lua_upvalueindex(1));
lua_pushnumber(L, gen(luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3)));
return 1;
}
static int dfhack_random_perlin(lua_State *L)
{
MersenneRNG *prng = check_random_native(L, 1);
int size = luaL_optint(L, 2, 3);
switch (size)
{
case 1: {
auto pdata = new (L) PerlinNoise1D<float>();
pdata->init(*prng);
lua_pushcclosure(L, eval_perlin_1, 1);
break;
}
case 2: {
auto pdata = new (L) PerlinNoise2D<float>();
pdata->init(*prng);
lua_pushcclosure(L, eval_perlin_2, 1);
break;
}
case 3: {
auto pdata = new (L) PerlinNoise3D<float>();
pdata->init(*prng);
lua_pushcclosure(L, eval_perlin_3, 1);
break;
}
default:
luaL_argerror(L, 2, "perlin noise dimension must be 1, 2 or 3");
}
return 1;
}
static const luaL_Reg dfhack_random_funcs[] = {
{ "new", dfhack_random_new },
{ "init", dfhack_random_init },
{ "random", dfhack_random_random },
{ "drandom", dfhack_random_drandom },
{ "drandom0", dfhack_random_drandom0 },
{ "drandom1", dfhack_random_drandom1 },
{ "unitrandom", dfhack_random_unitrandom },
{ "unitvector", dfhack_random_unitvector },
{ "perlin", dfhack_random_perlin },
{ NULL, NULL }
};
static void OpenRandom(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "random");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN);
luaL_setfuncs(state, dfhack_random_funcs, 0);
lua_pop(state, 1);
}
/************************
* Wrappers for C++ API *
************************/
@ -2023,6 +2220,7 @@ void OpenDFHackApi(lua_State *state)
OpenPersistent(state);
OpenMatinfo(state);
OpenPen(state);
OpenRandom(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module);

@ -172,6 +172,8 @@ Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _f
plugin_onupdate = 0;
plugin_onstatechange = 0;
plugin_rpcconnect = 0;
plugin_enable = 0;
plugin_is_enabled = 0;
state = PS_UNLOADED;
access = new RefLock();
}
@ -245,6 +247,8 @@ 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");
plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable");
plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled");
plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby");
index_lua(plug);
this->name = *plug_name;
@ -254,11 +258,15 @@ bool Plugin::load(color_ostream &con)
{
state = PS_LOADED;
parent->registerCommands(this);
if ((plugin_onupdate || plugin_enable) && !plugin_is_enabled)
con.printerr("Plugin %s has no enabled var!\n", name.c_str());
return true;
}
else
{
con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str());
plugin_is_enabled = 0;
plugin_onupdate = 0;
reset_lua();
ClosePlugin(plugin_lib);
state = PS_BROKEN;
@ -294,6 +302,8 @@ bool Plugin::unload(color_ostream &con)
if(plugin_shutdown)
cr = plugin_shutdown(con);
// cleanup...
plugin_is_enabled = 0;
plugin_onupdate = 0;
reset_lua();
parent->unregisterCommands(this);
commands.clear();
@ -408,6 +418,12 @@ bool Plugin::can_invoke_hotkey(const std::string & command, df::viewscreen *top
command_result Plugin::on_update(color_ostream &out)
{
// Check things that are implicitly protected by the suspend lock
if (!plugin_onupdate)
return CR_NOT_IMPLEMENTED;
if (plugin_is_enabled && !*plugin_is_enabled)
return CR_OK;
// Grab mutex and call the thing
command_result cr = CR_NOT_IMPLEMENTED;
access->lock_add();
if(state == PS_LOADED && plugin_onupdate)
@ -419,6 +435,21 @@ command_result Plugin::on_update(color_ostream &out)
return cr;
}
command_result Plugin::set_enabled(color_ostream &out, bool enable)
{
command_result cr = CR_NOT_IMPLEMENTED;
access->lock_add();
if(state == PS_LOADED && plugin_is_enabled && plugin_enable)
{
cr = plugin_enable(out, enable);
if (cr == CR_OK && enable != is_enabled())
cr = CR_FAILURE;
}
access->lock_sub();
return cr;
}
command_result Plugin::on_state_change(color_ostream &out, state_change_event event)
{
command_result cr = CR_NOT_IMPLEMENTED;
@ -524,6 +555,37 @@ void Plugin::reset_lua()
}
}
int Plugin::lua_is_enabled(lua_State *state)
{
auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1));
RefAutoinc lock(obj->access);
if (obj->state == PS_LOADED && obj->plugin_is_enabled)
lua_pushboolean(state, obj->is_enabled());
else
lua_pushnil(state);
return 1;
}
int Plugin::lua_set_enabled(lua_State *state)
{
lua_settop(state, 1);
bool val = lua_toboolean(state, 1);
auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1));
RefAutoinc lock(obj->access);
color_ostream *out = Lua::GetOutput(state);
if (obj->state == PS_LOADED && obj->plugin_enable)
lua_pushboolean(state, obj->set_enabled(*out, val) == CR_OK);
else
luaL_error(state, "plugin %s unloaded, cannot enable or disable", obj->name.c_str());
return 1;
}
int Plugin::lua_cmd_wrapper(lua_State *state)
{
auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1));
@ -561,6 +623,19 @@ void Plugin::open_lua(lua_State *state, int table)
RefAutolock lock(access);
if (plugin_is_enabled)
{
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, lua_is_enabled, 1);
lua_setfield(state, table, "isEnabled");
}
if (plugin_enable)
{
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, lua_set_enabled, 1);
lua_setfield(state, table, "setEnabled");
}
for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
{
lua_pushlightuserdata(state, it->second);
@ -604,7 +679,7 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn)
PluginManager::PluginManager(Core * core)
{
cmdlist_mutex = new mutex();
eval_ruby = NULL;
ruby = NULL;
}
PluginManager::~PluginManager()
@ -699,7 +774,7 @@ void PluginManager::registerCommands( Plugin * p )
belongs[cmds[i].name] = p;
}
if (p->plugin_eval_ruby)
eval_ruby = p->plugin_eval_ruby;
ruby = p;
cmdlist_mutex->unlock();
}
@ -713,6 +788,6 @@ void PluginManager::unregisterCommands( Plugin * p )
belongs.erase(cmds[i].name);
}
if (p->plugin_eval_ruby)
eval_ruby = NULL;
ruby = NULL;
cmdlist_mutex->unlock();
}

@ -288,9 +288,9 @@ void VMethodInterposeLinkBase::set_chain(void *chain)
addr_to_method_pointer_(chain_mptr, chain);
}
VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority)
VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority, const char *name)
: host(host), vmethod_idx(vmethod_idx), interpose_method(interpose_method),
chain_mptr(chain_mptr), priority(priority),
chain_mptr(chain_mptr), priority(priority), name_str(name),
applied(false), saved_chain(NULL), next(NULL), prev(NULL)
{
if (vmethod_idx < 0 || interpose_method == NULL)
@ -303,8 +303,8 @@ VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int v
* - interpose_method comes from method_pointer_to_addr_
*/
fprintf(stderr, "Bad VMethodInterposeLinkBase arguments: %d %08x\n",
vmethod_idx, unsigned(interpose_method));
fprintf(stderr, "Bad VMethodInterposeLinkBase arguments: %d %08x (%s)\n",
vmethod_idx, unsigned(interpose_method), name_str);
fflush(stderr);
abort();
}

@ -29,15 +29,24 @@ distribution.
#ifndef DFHACK_EXPORT
#define DFHACK_EXPORT __attribute__ ((visibility("default")))
#endif
#ifndef DFHACK_IMPORT
#define DFHACK_IMPORT DFHACK_EXPORT
#endif
#else
#ifdef BUILD_DFHACK_LIB
#ifndef DFHACK_EXPORT
#define DFHACK_EXPORT __declspec(dllexport)
#endif
#ifndef DFHACK_IMPORT
#define DFHACK_IMPORT
#endif
#else
#ifndef DFHACK_EXPORT
#define DFHACK_EXPORT __declspec(dllimport)
#endif
#ifndef DFHACK_IMPORT
#define DFHACK_IMPORT DFHACK_EXPORT
#endif
#endif
#endif

@ -34,6 +34,11 @@ distribution.
#include <lua.h>
#include <lauxlib.h>
/// Allocate a new user data object and push it on the stack
inline void *operator new (std::size_t size, lua_State *L) {
return lua_newuserdata(L, size);
}
namespace DFHack {
class function_identity_base;
struct MaterialInfo;

@ -144,6 +144,11 @@ namespace DFHack
bool unload(color_ostream &out);
bool reload(color_ostream &out);
bool can_be_enabled() { return plugin_is_enabled != 0; }
bool is_enabled() { return plugin_is_enabled && *plugin_is_enabled; }
bool can_set_enabled() { return plugin_is_enabled != 0 && plugin_enable; }
command_result set_enabled(color_ostream &out, bool enable);
command_result invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool can_invoke_hotkey(const std::string & command, df::viewscreen *top );
plugin_state getState () const;
@ -165,6 +170,12 @@ namespace DFHack
void open_lua(lua_State *state, int table);
command_result eval_ruby(color_ostream &out, const char* cmd) {
if (!plugin_eval_ruby || !is_enabled())
return CR_FAILURE;
return plugin_eval_ruby(out, cmd);
}
private:
RefLock * access;
std::vector <PluginCommand> commands;
@ -184,17 +195,22 @@ namespace DFHack
static int lua_fun_wrapper(lua_State *state);
void push_function(lua_State *state, LuaFunction *fn);
static int lua_is_enabled(lua_State *state);
static int lua_set_enabled(lua_State *state);
struct LuaEvent;
std::map<std::string, LuaEvent*> lua_events;
void index_lua(DFLibrary *lib);
void reset_lua();
bool *plugin_is_enabled;
command_result (*plugin_init)(color_ostream &, std::vector <PluginCommand> &);
command_result (*plugin_status)(color_ostream &, std::string &);
command_result (*plugin_shutdown)(color_ostream &);
command_result (*plugin_onupdate)(color_ostream &);
command_result (*plugin_onstatechange)(color_ostream &, state_change_event);
command_result (*plugin_enable)(color_ostream &, bool);
RPCService* (*plugin_rpcconnect)(color_ostream &);
command_result (*plugin_eval_ruby)(color_ostream &, const char*);
};
@ -226,7 +242,7 @@ namespace DFHack
{
return all_plugins.size();
}
command_result (*eval_ruby)(color_ostream &, const char*);
Plugin *ruby;
// DATA
private:
tthread::mutex * cmdlist_mutex;
@ -250,6 +266,10 @@ namespace DFHack
DFhackDataExport const char * name = plugin_name;\
DFhackDataExport Plugin *plugin_self = NULL;
#define DFHACK_PLUGIN_IS_ENABLED(varname) \
DFhackDataExport bool plugin_is_enabled = false; \
bool &varname = plugin_is_enabled;
#define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \

@ -56,6 +56,8 @@ distribution.
#pragma warning( disable: 4018)
// nonstandard extension used: enum 'df::whatever::etc' used in qualified name
#pragma warning( disable: 4482)
// nonstandard extension used: 'extern' before template explicit instantiation
#pragma warning( disable: 4231)
#endif
#endif

@ -141,7 +141,7 @@ namespace DFHack
#define IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,priority) \
DFHack::VMethodInterposeLink<class::interpose_base,class::interpose_ptr_##name> \
class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name, priority);
class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name, priority, #class"::"#name);
#define IMPLEMENT_VMETHOD_INTERPOSE(class,name) IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,0)
@ -161,6 +161,7 @@ namespace DFHack
void *interpose_method; // Pointer to the code of the interposing method
void *chain_mptr; // Pointer to the chain field in the subclass below
int priority; // Higher priority hooks are called earlier
const char *name_str; // Name of the hook
bool applied; // True if this hook is currently applied
void *saved_chain; // Pointer to the code of the original vmethod or next hook
@ -179,12 +180,14 @@ namespace DFHack
VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id);
bool find_child_hosts(virtual_identity *cur, void *vmptr);
public:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority, const char *name);
~VMethodInterposeLinkBase();
bool is_applied() { return applied; }
bool apply(bool enable = true);
void remove();
const char *name() { return name_str; }
};
template<class Base, class Ptr>
@ -198,13 +201,13 @@ namespace DFHack
operator Ptr () { return chain; }
template<class Ptr2>
VMethodInterposeLink(Ptr target, Ptr2 src, int priority)
VMethodInterposeLink(Ptr target, Ptr2 src, int priority, const char *name)
: VMethodInterposeLinkBase(
&Base::_identity,
vmethod_pointer_to_idx(target),
method_pointer_to_addr(src),
&chain,
priority
priority, name
)
{ src = target; /* check compatibility */ }
};

@ -0,0 +1,130 @@
#pragma once
namespace DFHack {
namespace Random {
/*
* A good explanation:
* http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html
*/
// Interpolation functions
template<class T>
inline T s_curve(T t)
{
// Classical function
//return t * t * (3 - 2*t);
// 2002 version from http://mrl.nyu.edu/~perlin/paper445.pdf
return t * t * t * (t * (t * 6 - 15) + 10);
}
template<class T>
inline T lerp(T s, T a, T b)
{
return a + s * (b-a);
}
// Dot product of VSIZE vectors pointed by pa, pb
template<class T, unsigned i>
struct DotProduct {
static inline T eval(T *pa, T *pb);
};
template<class T>
struct DotProduct<T,0> {
static inline T eval(T *pa, T *pb) { return pa[0]*pb[0]; }
};
template<class T, unsigned i>
inline T DotProduct<T,i>::eval(T *pa, T *pb) {
return DotProduct<T,i-1>::eval(pa, pb) + pa[i]*pb[i];
}
// Templates used to force unrolling and inlining of the loops
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
template<unsigned mask>
struct PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,-1> {
typedef typename PerlinNoise<T,VSIZE,BITS,IDXT>::Temp Temp;
static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *, const T *, Temp *) {}
static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq);
};
// Initialization of the temporaries from input coordinates
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
template<unsigned mask, int i>
inline void PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::setup(
PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt
) {
Impl<mask,i-1>::setup(self, pv, pt);
T t = std::floor(pv[i]);
pt[i].s = s_curve(pt[i].r0 = pv[i] - t);
unsigned b = unsigned(int32_t(t));
pt[i].b0 = self->idxmap[i][b & mask];
pt[i].b1 = self->idxmap[i][(b+1) & mask];
}
// Main recursion. Uses tables from self and pt.
// Recursion changes current index idx, and current offset vector pq.
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
template<unsigned mask>
inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask, -1>::eval(
PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq
) {
return DotProduct<T,VSIZE-1>::eval(pq, self->gradients[idx]);
}
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
template<unsigned mask, int i>
inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::eval(
PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq
) {
pq[i] = pt[i].r0;
T u = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b0, pq);
pq[i] -= 1;
T v = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b1, pq);
return lerp(pt[i].s, u, v);
}
// Actual methods of the object
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
void PerlinNoise<T,VSIZE,BITS,IDXT>::init(MersenneRNG &rng)
{
STATIC_ASSERT(VSIZE > 0 && BITS <= 8*sizeof(IDXT));
// Random unit gradient vectors
for (unsigned i = 0; i < TSIZE; i++)
rng.unitvector(gradients[i], VSIZE);
// Random permutation tables
for (unsigned j = 0; j < VSIZE; j++)
{
for (unsigned i = 0; i < TSIZE; i++)
idxmap[j][i] = i;
rng.permute(idxmap[j], TSIZE);
}
}
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
T PerlinNoise<T,VSIZE,BITS,IDXT>::eval(const T coords[VSIZE])
{
// Precomputed properties from the coordinates
Temp tmp[VSIZE];
// Temporary used to build the current offset vector
T q[VSIZE];
Impl<TSIZE-1,VSIZE-1>::setup(this, coords, tmp);
return Impl<TSIZE-1,VSIZE-1>::eval(this, tmp, 0, q);
}
}} // namespace

@ -0,0 +1,179 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#ifndef CL_MOD_RANDOM
#define CL_MOD_RANDOM
/**
* \defgroup grp_random Random: Random number and noise generation
* @ingroup grp_modules
*/
#include "Export.h"
#include "Module.h"
#include "Types.h"
#include "DataDefs.h"
namespace DFHack
{
namespace Random
{
class DFHACK_EXPORT MersenneRNG
{
static const unsigned MT_LEN = 624;
unsigned mt_index;
uint32_t mt_buffer[MT_LEN];
void twist();
void prefill(unsigned step, int twist_cnt);
public:
/* No constructor or destructor - safe to treat as data */
void init(const uint32_t *pseed, unsigned cnt, int twist_cnt = 1);
void init(); // uses time
void init(uint32_t seed, int twist_cnt = 1) { init(&seed, 1, twist_cnt); }
// [0, 2^32)
uint32_t random() {
if (mt_index >= MT_LEN) twist();
return mt_buffer[mt_index++];
}
// [0, limit)
uint32_t random(uint32_t limit) {
return uint32_t(uint64_t(random())*limit >> 32);
}
// (0, 1)
double drandom0() {
return (double(random())+1)/4294967297.0;
}
// [0, 1)
double drandom() {
return double(random())/4294967296.0;
}
// [0, 1]
double drandom1() {
return double(random())/4294967295.0;
}
// [-1, 1]
double unitrandom() {
return drandom1()*2.0 - 1.0;
}
// Two exact replicas of functions in DF code
int32_t df_trandom(uint32_t max=2147483647LU);
int32_t df_loadtrandom(uint32_t max=2147483647LU);
template<class T>
void unitvector(T *p, int size);
template<class T>
void permute(T *p, int size) {
while(size > 1)
{
int j = random(size--);
T c = p[j]; p[j] = p[size]; p[size] = c;
}
}
};
#ifndef DFHACK_RANDOM_CPP
extern template void MersenneRNG::unitvector<float>(float *p, int size);
extern template void MersenneRNG::unitvector<double>(double *p, int size);
#endif
/*
* Classical Perlin noise function in template form.
* http://mrl.nyu.edu/~perlin/doc/oscar.html#noise
*
* Using an improved hash function from:
* http://www.cs.utah.edu/~aek/research/noise.pdf
*/
template<class T, unsigned VSIZE, unsigned BITS = 8, class IDXT = uint8_t>
class PerlinNoise
{
// Size of randomness tables
static const unsigned TSIZE = 1<<BITS;
T gradients[TSIZE][VSIZE];
IDXT idxmap[VSIZE][TSIZE];
// Templates used to unwind and inline recursion and loops
struct Temp {
T r0, s;
unsigned b0, b1;
};
template<unsigned mask, int i>
struct Impl {
static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt);
static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq);
};
public:
/* No constructor or destructor - safe to treat as data */
void init(MersenneRNG &rng);
T eval(const T coords[VSIZE]);
};
#ifndef DFHACK_RANDOM_CPP
extern template class DFHACK_IMPORT PerlinNoise<float, 1>;
extern template class DFHACK_IMPORT PerlinNoise<float, 2>;
extern template class DFHACK_IMPORT PerlinNoise<float, 3>;
#endif
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
class PerlinNoise1D : public PerlinNoise<T, 1, BITS, IDXT>
{
public:
T operator() (T x) { return this->eval(&x); }
};
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
class PerlinNoise2D : public PerlinNoise<T, 2, BITS, IDXT>
{
public:
T operator() (T x, T y) {
T tmp[2] = { x, y };
return this->eval(tmp);
}
};
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
class PerlinNoise3D : public PerlinNoise<T, 3, BITS, IDXT>
{
public:
T operator() (T x, T y, T z) {
T tmp[3] = { x, y, z };
return this->eval(tmp);
}
};
}
}
#endif

@ -220,6 +220,12 @@ function dfhack.matinfo:__tostring()
return "<material "..self.type..":"..self.index.." "..self:getToken()..">"
end
dfhack.random.__index = dfhack.random
function dfhack.random:__tostring()
return "<random generator>"
end
function dfhack.maps.getSize()
local map = df.global.world.map
return map.x_count_block, map.y_count_block, map.z_count_block

@ -151,7 +151,7 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
CHECK_NULL_POINTER(&a);
CHECK_NULL_POINTER(&b);
if (!(CMP(job_type) && CMP(unk2) &&
if (!(CMP(job_type) && CMP(job_subtype) &&
CMP(mat_type) && CMP(mat_index) &&
CMP(item_subtype) && CMP(item_category.whole) &&
CMP(hist_figure_id) && CMP(material_category.whole) &&

@ -0,0 +1,156 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 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>
using namespace std;
#define DFHACK_RANDOM_CPP
#include "modules/Random.h"
#include "VersionInfo.h"
#include "MemAccess.h"
#include "Types.h"
#include "ModuleFactory.h"
#include "Core.h"
#include "Error.h"
#include "VTableInterpose.h"
#include <cmath>
using namespace DFHack;
using namespace df::enums;
using namespace DFHack::Random;
//public domain RNG stuff by Michael Brundage
//modified to be compatible with the version in DF
#define MT_IA 397
#define MT_IB (MT_LEN - MT_IA)
#define UPPER_MASK 0x80000000
#define LOWER_MASK 0x7FFFFFFF
#define MATRIX_A 0x9908B0DF
#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK)
#define MAGIC(s) (((s)&1)*MATRIX_A)
void MersenneRNG::twist()
{
uint32_t *b = mt_buffer;
uint32_t s;
unsigned i;
i = 0;
for (; i < MT_IB; i++) {
s = TWIST(b, i, i+1);
b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s);
}
for (; i < MT_LEN-1; i++) {
s = TWIST(b, i, i+1);
b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s);
}
s = TWIST(b, MT_LEN-1, 0);
b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s);
mt_index = 0;
}
void MersenneRNG::prefill(unsigned step, int twist_cnt)
{
for(unsigned i=step;i<MT_LEN;i++)
{
//2010: better init line from wikipedia, ultimate source unknown
mt_buffer[i]=1812433253UL * (mt_buffer[i-step] ^ (mt_buffer[i-step]>>30)) + i;
}
mt_index = 0;
for(int j=0;j<twist_cnt;j++)
twist();
}
void MersenneRNG::init()
{
init(Core::getInstance().p->getTickCount(), 20);
}
void MersenneRNG::init(const uint32_t *pseed, unsigned cnt, int twist_cnt)
{
memcpy(mt_buffer, pseed, cnt*sizeof(uint32_t));
prefill(cnt, twist_cnt);
}
int32_t MersenneRNG::df_trandom(uint32_t max)
{
if(max<=1)return 0;
uint32_t seed=random();
seed=seed%2147483647LU;
seed=seed/((2147483647LU/max)+1);
return((int32_t)seed);
}
int32_t MersenneRNG::df_loadtrandom(uint32_t max)
{
uint32_t seed=random();
seed=seed%max;
return((int32_t)seed);
}
template<class T>
void MersenneRNG::unitvector(T *p, int size)
{
for (;;)
{
T rsqr = 0;
for (int i = 0; i < size; i++)
{
p[i] = (T)unitrandom();
rsqr += p[i]*p[i];
}
if (rsqr > 0 && rsqr <= 1)
{
rsqr = std::sqrt(rsqr);
for (int i = 0; i < size; i++)
p[i] /= rsqr;
break;
}
}
}
template void MersenneRNG::unitvector<float>(float *p, int size);
template void MersenneRNG::unitvector<double>(double *p, int size);
#include "modules/PerlinNoise.inc"
template class DFHACK_EXPORT PerlinNoise<float, 1>;
template class DFHACK_EXPORT PerlinNoise<float, 2>;
template class DFHACK_EXPORT PerlinNoise<float, 3>;

@ -66,6 +66,7 @@ using namespace std;
#include "df/game_mode.h"
#include "df/unit_misc_trait.h"
#include "df/unit_skill.h"
#include "df/curse_attr_change.h"
using namespace DFHack;
using namespace df::enums;
@ -1208,12 +1209,10 @@ int Units::computeMovementSpeed(df::unit *unit)
if (unsigned(unit->counters.webbed-1) <= 8)
speed += unit->counters.webbed*100;
// Muscle weight vs vascular tissue (?)
// Muscle and fat weight vs expected size
auto &attr_tissue = unit->body.physical_attr_tissues;
int muscle = attr_tissue[STRENGTH];
int blood = attr_tissue[AGILITY];
speed = std::max(speed*3/4, std::min(speed*3/2, int(int64_t(speed)*muscle/blood)));
auto &s_info = unit->body.size_info;
speed = std::max(speed*3/4, std::min(speed*3/2, int(int64_t(speed)*s_info.size_cur/s_info.size_base)));
// Attributes
@ -1243,7 +1242,7 @@ int Units::computeMovementSpeed(df::unit *unit)
// Inventory encumberance
int total_weight = calcInventoryWeight(unit);
int free_weight = std::max(1, muscle/10 + strength_attr*3);
int free_weight = std::max(1, s_info.size_cur/10 + strength_attr*3);
if (free_weight < total_weight)
{

@ -1 +1 @@
Subproject commit 20ecaa0393df1ea111861d67c789aaaa56a37c58
Subproject commit e62d498e68e3a87929b144220d03e691016f7aae

@ -158,6 +158,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(autotrade autotrade.cpp)
DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(cleanconst cleanconst.cpp)
endif()

@ -48,6 +48,7 @@ using df::global::ui;
typedef df::reaction_product_item_improvementst improvement_product;
DFHACK_PLUGIN("add-spatter");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
struct ReagentSource {
int idx;
@ -378,7 +379,7 @@ static bool find_reactions(color_ostream &out)
parse_product(out, out_prod.back(), it->second.react, itprod);
}
for (size_t i = 0; i < prod.size(); i++)
for (size_t i = 0; i < out_prod.size(); i++)
{
if (out_prod[i].isValid())
products[out_prod[i].product] = &out_prod[i];
@ -390,6 +391,7 @@ static bool find_reactions(color_ostream &out)
static void enable_hooks(bool enable)
{
is_enabled = enable;
INTERPOSE_HOOK(item_hook, isImprovable).apply(enable);
INTERPOSE_HOOK(product_hook, produce).apply(enable);
}

@ -99,7 +99,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap);
static bool in_transient_swap = false;
DFHACK_PLUGIN_IS_ENABLED(in_transient_swap);
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{

@ -39,12 +39,11 @@
using namespace std;
using namespace DFHack;
static bool enabled = false;
namespace ResetPolicy {
typedef enum {DoNothing, ResetDuration, AddDuration, NewInstance} ResetPolicy;
}
DFHACK_PLUGIN_IS_ENABLED(enabled);
DFHACK_PLUGIN("autoSyndrome");
command_result autoSyndrome(color_ostream& out, vector<string>& parameters);
@ -75,35 +74,44 @@ DFhackCExport command_result plugin_shutdown(color_ostream& out) {
return CR_OK;
}*/
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enabled == enable)
return CR_OK;
enabled = enable;
if ( enabled ) {
EventManager::EventHandler handle(processJob, 0);
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, plugin_self);
} else {
EventManager::unregisterAll(plugin_self);
}
return CR_OK;
}
command_result autoSyndrome(color_ostream& out, vector<string>& parameters) {
if ( parameters.size() > 1 )
return CR_WRONG_USAGE;
bool wasEnabled = enabled;
bool enable = false;
if ( parameters.size() == 1 ) {
if ( parameters[0] == "enable" ) {
enabled = true;
enable = true;
} else if ( parameters[0] == "disable" ) {
enabled = false;
enable = false;
} else {
int32_t a = atoi(parameters[0].c_str());
if ( a < 0 || a > 1 )
return CR_WRONG_USAGE;
enabled = (bool)a;
enable = (bool)a;
}
}
out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled");
if ( enabled == wasEnabled )
return CR_OK;
EventManager::unregisterAll(plugin_self);
if ( enabled ) {
EventManager::EventHandler handle(processJob, 0);
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, plugin_self);
}
return CR_OK;
return plugin_enable(out, enable);
}
bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit, ResetPolicy::ResetPolicy policy) {

@ -125,7 +125,7 @@ static command_result autodump_main(color_ostream &out, vector <string> & parame
{
if (!Gui::getCursorCoords(cx,cy,cz))
{
out.printerr("Cursor position not found. Please enabled the cursor.\n");
out.printerr("Cursor position not found. Please enable the cursor.\n");
return CR_FAILURE;
}
pos_cursor = DFCoord(cx,cy,cz);

@ -76,7 +76,7 @@ using df::global::world;
* (mining, hunting, and woodcutting) need to be handled carefully to minimize churn.
*/
static int enable_autolabor = 0;
DFHACK_PLUGIN_IS_ENABLED(enable_autolabor);
static bool print_debug = 0;
@ -535,6 +535,7 @@ static void setOptionEnabled(ConfigFlags flag, bool on)
static void cleanup_state()
{
enable_autolabor = false;
labor_infos.clear();
}
@ -1297,6 +1298,27 @@ void print_labor (df::unit_labor labor, color_ostream &out)
}
}
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable )
{
if (!Core::getInstance().isWorldLoaded()) {
out.printerr("World is not loaded: please load a game first.\n");
return CR_FAILURE;
}
if (enable && !enable_autolabor)
{
enable_plugin(out);
}
else if(!enable && enable_autolabor)
{
enable_autolabor = false;
setOptionEnabled(CF_ENABLED, false);
out << "Autolabor is disabled." << endl;
}
return CR_OK;
}
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters)
{
@ -1312,19 +1334,8 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
parameters[0] == "1" || parameters[0] == "disable"))
{
bool enable = (parameters[0] == "1" || parameters[0] == "enable");
if (enable && !enable_autolabor)
{
enable_plugin(out);
}
else if(!enable && enable_autolabor)
{
enable_autolabor = false;
setOptionEnabled(CF_ENABLED, false);
out << "The plugin is disabled." << endl;
}
return CR_OK;
return plugin_enable(out, enable);
}
else if (parameters.size() == 2 && parameters[0] == "haulpct")
{

@ -1179,11 +1179,27 @@ color_ostream_proxy console_out(Core::getInstance().getConsole());
IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, render);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (!gps || !INTERPOSE_HOOK(jobutils_hook, feed).apply() || !INTERPOSE_HOOK(jobutils_hook, render).apply())
out.printerr("Could not insert jobutils hooks!\n");
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(jobutils_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(jobutils_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
hotkeys[construction_type::Wall] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_WALL;
hotkeys[construction_type::Floor] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FLOOR;
hotkeys[construction_type::Ramp] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_RAMP;

@ -583,7 +583,6 @@ static command_result autotrade_cmd(color_ostream &out, vector <string> & parame
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event)
@ -600,11 +599,30 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps || !INTERPOSE_HOOK(trade_hook, feed).apply() || !INTERPOSE_HOOK(trade_hook, render).apply())
out.printerr("Could not insert autotrade hooks!\n");
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
depot_info.reset();
monitor.reset();
if (!INTERPOSE_HOOK(trade_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(trade_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(
PluginCommand(
"autotrade", "Automatically send items in marked stockpiles to trade depot, when trading is possible.",

@ -92,6 +92,8 @@ static void debug(const string &msg)
* Material Choice Screen
*/
static string material_to_string_fn(MaterialInfo m) { return m.toString(); }
struct ItemFilter
{
df::dfhack_material_category mat_mask;
@ -114,8 +116,10 @@ struct ItemFilter
bool matches(MaterialInfo &material) const
{
return any_of(materials.begin(), materials.end(),
[&] (const MaterialInfo &m) { return material.matches(m); });
for (auto it = materials.begin(); it != materials.end(); ++it)
if (material.matches(*it))
return true;
return false;
}
bool matches(df::item *item)
@ -137,8 +141,7 @@ struct ItemFilter
{
vector<string> descriptions;
transform_(materials, descriptions,
[] (MaterialInfo m) { return m.toString(); });
transform_(materials, descriptions, material_to_string_fn);
if (descriptions.size() == 0)
bitfield_to_string(&descriptions, mat_mask);
@ -157,8 +160,8 @@ struct ItemFilter
str.append("/");
if (materials.size() > 0)
{
for_each_(materials,
[&] (MaterialInfo &m) { str.append(m.getToken() + ","); });
for (size_t i = 0; i < materials.size(); i++)
str.append(materials[i].getToken() + ",");
if (str[str.size()-1] == ',')
str.resize(str.size () - 1);
@ -217,6 +220,7 @@ private:
bool valid;
};
static MaterialInfo &material_info_identity_fn(MaterialInfo &m) { return m; }
class ViewscreenChooseMaterial : public dfhack_viewscreen
{
@ -284,13 +288,12 @@ public:
// Category masks
auto masks = masks_column.getSelectedElems();
for_each_(masks,
[&] (df::dfhack_material_category &m) { filter->mat_mask.whole |= m.whole; });
for (auto it = masks.begin(); it != masks.end(); ++it)
filter->mat_mask.whole |= it->whole;
// Specific materials
auto materials = materials_column.getSelectedElems();
transform_(materials, filter->materials,
[] (MaterialInfo &m) { return m; });
transform_(materials, filter->materials, material_info_identity_fn);
Screen::dismiss(this);
}
@ -465,6 +468,7 @@ private:
}
};
static void delete_item_fn(df::job_item *x) { delete x; }
// START Planning
class PlannedBuilding
@ -555,7 +559,7 @@ public:
auto job = building->jobs[0];
for_each_(job->job_items, [] (df::job_item *x) { delete x; });
for_each_(job->job_items, delete_item_fn);
job->job_items.clear();
job->flags.bits.suspend = false;
@ -613,9 +617,10 @@ private:
ItemFilter filter;
};
static map<df::building_type, bool> planmode_enabled, saved_planmodes;
static void enable_quickfort_fn(pair<const df::building_type, bool>& pair) { pair.second = true; }
class Planner
{
public:
@ -804,8 +809,7 @@ public:
void enableQuickfortMode()
{
saved_planmodes = planmode_enabled;
for_each_(planmode_enabled,
[] (pair<const df::building_type, bool>& pair) { pair.second = true; } );
for_each_(planmode_enabled, enable_quickfort_fn);
quickfort_mode = true;
}
@ -1093,8 +1097,8 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
OutputHotkeyString(x, y, "Material Filter:", "m", true, left_margin);
auto filter_descriptions = filter->getMaterialFilterAsVector();
for_each_(filter_descriptions,
[&](string d) { OutputString(COLOR_BROWN, x, y, " *" + d, true, left_margin); });
for (auto it = filter_descriptions.begin(); it != filter_descriptions.end(); ++it)
OutputString(COLOR_BROWN, x, y, " *" + *it, true, left_margin);
}
else
{
@ -1121,8 +1125,8 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
OutputString(COLOR_BROWN, x, y, "Materials:", true, left_margin);
auto filters = filter->getMaterialFilterAsVector();
for_each_(filters,
[&](string d) { OutputString(COLOR_BLUE, x, y, "*" + d, true, left_margin); });
for (auto it = filters.begin(); it != filters.end(); ++it)
OutputString(COLOR_BLUE, x, y, "*" + *it, true, left_margin);
}
else
{
@ -1153,12 +1157,29 @@ static command_result buildingplan_cmd(color_ostream &out, vector <string> & par
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps || !INTERPOSE_HOOK(buildingplan_hook, feed).apply() || !INTERPOSE_HOOK(buildingplan_hook, render).apply())
out.printerr("Could not insert buildingplan hooks!\n");
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
planner.reset(out);
if (!INTERPOSE_HOOK(buildingplan_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(buildingplan_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(
PluginCommand(
"buildingplan", "Place furniture before it's built",

@ -221,7 +221,8 @@ static void detect_digging(color_ostream &out)
}
}
static bool active = false;
DFHACK_PLUGIN_IS_ENABLED(active);
static bool auto_grow = false;
static std::vector<int> grow_burrows;

@ -120,14 +120,14 @@ command_result catsplosion (color_ostream &out, std::vector <std::string> & para
female->relations.pregnancy_timer = rand() % 100 + 1;
totalchanged++;
}
else if(!female->relations.pregnancy_ptr)
else if(!female->relations.pregnancy_genes)
{
df::unit_genes *preg = new df::unit_genes;
preg->appearance = female->appearance.genes.appearance;
preg->colors = female->appearance.genes.colors;
female->relations.pregnancy_ptr = preg;
female->relations.pregnancy_genes = preg;
female->relations.pregnancy_timer = rand() % 100 + 1;
female->relations.pregnancy_mystery = 1; // WTF is this?
female->relations.pregnancy_caste = 1;
totalcreated ++;
}
}

@ -0,0 +1,87 @@
// Destroys items being used as part of constructions
// and flags the constructions to recreate their components upon disassembly
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Maps.h"
#include "DataDefs.h"
#include "df/item.h"
#include "df/world.h"
#include "df/construction.h"
#include "df/map_block.h"
using namespace std;
using namespace DFHack;
using df::global::world;
DFHACK_PLUGIN("cleanconst");
command_result df_cleanconst(color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
size_t numItems = world->items.all.size();
int cleaned_total = 0;
// proceed with the cleanup operation
for (size_t i = 0; i < numItems; i++)
{
df::item *item = world->items.all[i];
// only process items marked as "in construction"
if (!item->flags.bits.construction)
continue;
df::coord pos(item->pos.x, item->pos.y, item->pos.z);
df::construction *cons = df::construction::find(pos);
if (!cons)
{
out.printerr("Item at %i,%i,%i marked as construction but no construction is present!\n", pos.x, pos.y, pos.z);
continue;
}
// if the construction is already labeled as "no build item", then leave it alone
if (cons->flags.bits.no_build_item)
continue;
// only destroy the item if the construction claims to be made of the exact same thing
if (item->getType() != cons->item_type ||
item->getSubtype() != cons->item_subtype ||
item->getMaterial() != cons->mat_type ||
item->getMaterialIndex() != cons->mat_index)
continue;
item->flags.bits.garbage_collect = 1;
cons->flags.bits.no_build_item = 1;
cleaned_total++;
}
out.print("Done. %d construction items cleaned up.\n", cleaned_total);
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"cleanconst", "Cleans up construction materials.",
df_cleanconst, false,
" This utility alters all constructions on the map so that they spawn their\n"
" building component when they are disassembled, allowing their actual\n"
" build items to be safely deleted.\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}

@ -76,7 +76,7 @@ using df::global::world;
#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
static int enable_autolabor = 0;
DFHACK_PLUGIN_IS_ENABLED(enable_autolabor);
static bool print_debug = 0;
@ -1375,6 +1375,7 @@ static void setOptionEnabled(ConfigFlags flag, bool on)
static void cleanup_state()
{
enable_autolabor = false;
labor_infos.clear();
}
@ -2384,6 +2385,27 @@ df::unit_labor lookup_labor_by_name (std::string& name)
return labor;
}
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable )
{
if (!Core::getInstance().isWorldLoaded()) {
out.printerr("World is not loaded: please load a game first.\n");
return CR_FAILURE;
}
if (enable && !enable_autolabor)
{
enable_plugin(out);
}
else if(!enable && enable_autolabor)
{
enable_autolabor = false;
setOptionEnabled(CF_ENABLED, false);
out << "Autolabor is disabled." << endl;
}
return CR_OK;
}
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters)
{
@ -2398,19 +2420,7 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
(parameters[0] == "enable" || parameters[0] == "disable"))
{
bool enable = (parameters[0] == "enable");
if (enable && !enable_autolabor)
{
enable_plugin(out);
}
else if(!enable && enable_autolabor)
{
enable_autolabor = false;
setOptionEnabled(CF_ENABLED, false);
out << "The plugin is disabled." << endl;
}
return CR_OK;
return plugin_enable(out, enable);
}
else if (parameters.size() == 3 &&
(parameters[0] == "max" || parameters[0] == "priority"))

@ -16,6 +16,8 @@ using std::vector;
using std::string;
using namespace DFHack;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
//FIXME: possible race conditions with calling kittens from the IO thread and shutdown from Core.
bool shutdown_flag = false;
bool final_flag = true;
@ -141,6 +143,7 @@ command_result trackmenu (color_ostream &out, vector <string> & parameters)
if(df::global::ui)
{
trackmenu_flg = true;
is_enabled = true;
last_menu = df::global::ui->main.mode;
out.print("Menu: %d\n",last_menu);
return CR_OK;
@ -155,6 +158,7 @@ command_result trackmenu (color_ostream &out, vector <string> & parameters)
command_result trackpos (color_ostream &out, vector <string> & parameters)
{
trackpos_flg = !trackpos_flg;
is_enabled = true;
return CR_OK;
}
@ -214,6 +218,7 @@ command_result ktimer (color_ostream &out, vector <string> & parameters)
// harmless potential data race here...
timeLast = timeend;
timering = true;
is_enabled = true;
return CR_OK;
}

@ -15,6 +15,8 @@ using namespace DFHack;
uint64_t timeLast=0;
static tthread::mutex* mymutex=0;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
struct memory_data
{
void * addr;
@ -96,6 +98,7 @@ void Deinit()
{
if(memdata.state==STATE_ON)
{
is_enabled = false;
memdata.state=STATE_OFF;
delete [] memdata.buf;
delete [] memdata.lbuf;
@ -140,6 +143,7 @@ command_result memview (color_ostream &out, vector <string> & parameters)
{
Deinit();
memdata.state=STATE_OFF;
is_enabled = false;
mymutex->unlock();
return CR_OK;
}
@ -156,6 +160,7 @@ command_result memview (color_ostream &out, vector <string> & parameters)
mymutex->unlock();
return CR_OK;
}
is_enabled = true;
memdata.state=STATE_ON;
}
if(parameters.size()>1)

@ -31,7 +31,7 @@ static command_result nestboxes(color_ostream &out, vector <string> & parameters
DFHACK_PLUGIN("nestboxes");
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
static void eggscan(color_ostream &out)
{
@ -97,6 +97,12 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
return CR_OK;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
enabled = enable;
return CR_OK;
}
static command_result nestboxes(color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;

@ -23,6 +23,8 @@ using df::global::gps;
DFHACK_PLUGIN("vshook");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
struct title_hook : df::viewscreen_titlest {
typedef df::viewscreen_titlest interpose_base;
@ -37,17 +39,30 @@ struct title_hook : df::viewscreen_titlest {
IMPLEMENT_VMETHOD_INTERPOSE(title_hook, render);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (gps)
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(title_hook, render).apply())
out.printerr("Could not interpose viewscreen_titlest::render\n");
if (!INTERPOSE_HOOK(title_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
// DON'T DO THIS IN NON-EXAMPLE PLUGINS
plugin_enable(out, true);
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
INTERPOSE_HOOK(title_hook, render).remove();

@ -179,9 +179,9 @@ public:
addDwarfActivity(unit, *entry);
}
for_each_(dwarf_activity_values[unit],
[&] (const pair<activity_type, size_t> &x)
{ dwarf_activity_values[unit][x.first] = getPercentage(x.second, dwarf_total); } );
auto &values = dwarf_activity_values[unit];
for (auto it = values.begin(); it != values.end(); ++it)
it->second = getPercentage(it->second, dwarf_total);
dwarves_column.add(getUnitName(unit), unit);
}
@ -212,9 +212,8 @@ public:
vector<pair<activity_type, size_t>> rev_vec(dwarf_activities->begin(), dwarf_activities->end());
sort(rev_vec.begin(), rev_vec.end(), less_second<activity_type, size_t>());
for_each_(rev_vec,
[&] (pair<activity_type, size_t> x)
{ dwarf_activity_column.add(getActivityItem(x.first, x.second), x.first); });
for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it)
dwarf_activity_column.add(getActivityItem(it->first, it->second), it->first);
}
dwarf_activity_column.fixWidth();
@ -754,9 +753,8 @@ public:
vector<pair<df::unit *, size_t>> rev_vec(dwarf_activities->begin(), dwarf_activities->end());
sort(rev_vec.begin(), rev_vec.end(), less_second<df::unit *, size_t>());
for_each_(rev_vec,
[&] (pair<df::unit *, size_t> x)
{ dwarf_activity_column.add(getDwarfAverage(x.first, x.second), x.first); });
for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it)
dwarf_activity_column.add(getDwarfAverage(it->first, it->second), it->first);
}
}
@ -778,9 +776,8 @@ public:
vector<pair<activity_type, size_t>> rev_vec(category_activities->begin(), category_activities->end());
sort(rev_vec.begin(), rev_vec.end(), less_second<activity_type, size_t>());
for_each_(rev_vec,
[&] (pair<activity_type, size_t> x)
{ category_breakdown_column.add(getBreakdownAverage(x.first, x.second), x.first); });
for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it)
category_breakdown_column.add(getBreakdownAverage(it->first, it->second), it->first);
}
category_breakdown_column.fixWidth();
@ -1179,11 +1176,15 @@ IMPLEMENT_VMETHOD_INTERPOSE(dwarf_monitor_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(dwarf_monitor_hook, render);
DFHACK_PLUGIN("dwarfmonitor");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
static bool set_monitoring_mode(const string &mode, const bool &state)
{
bool mode_recognized = false;
if (!is_enabled)
return false;
if (mode == "work" || mode == "all")
{
mode_recognized = true;
@ -1201,6 +1202,24 @@ static bool set_monitoring_mode(const string &mode, const bool &state)
return mode_recognized;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (is_enabled != enable)
{
if (!INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(dwarf_monitor_hook, render).apply(enable))
return CR_FAILURE;
reset();
is_enabled = enable;
}
return CR_OK;
}
static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & parameters)
{
bool show_help = false;
@ -1222,6 +1241,9 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & par
}
else if ((cmd == 'e' || cmd == 'E') && !mode.empty())
{
if (!is_enabled)
plugin_enable(out, true);
if (set_monitoring_mode(mode, true))
{
out << "Monitoring enabled: " << mode << endl;
@ -1257,9 +1279,6 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & par
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands)
{
if (!gps || !INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply() || !INTERPOSE_HOOK(dwarf_monitor_hook, render).apply())
out.printerr("Could not insert dwarfmonitor hooks!\n");
activity_labels[JOB_IDLE] = "Idle";
activity_labels[JOB_MILITARY] = "Military Duty";
activity_labels[JOB_LEISURE] = "Leisure";

@ -19,6 +19,8 @@ using df::global::world;
// dfhack interface
DFHACK_PLUGIN("fastdwarf");
DFHACK_PLUGIN_IS_ENABLED(active);
static bool enable_fastdwarf = false;
static bool enable_teledwarf = false;
@ -155,6 +157,8 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
return CR_WRONG_USAGE;
}
active = enable_fastdwarf || enable_teledwarf;
out.print("Current state: fast = %d, teleport = %d.\n",
(df::global::debug_turbospeed && *df::global::debug_turbospeed) ? 2 : (enable_fastdwarf ? 1 : 0),
enable_teledwarf ? 1 : 0);
@ -162,6 +166,17 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
return CR_OK;
}
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable )
{
if (active != enable)
{
active = enable_fastdwarf = enable;
enable_teledwarf = false;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand("fastdwarf",

@ -698,7 +698,7 @@ static void try_store_ammo(df::squad *squad)
}
}
static bool is_enabled = false;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_onupdate(color_ostream &out, state_change_event event)
{
@ -810,6 +810,21 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
return CR_OK;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!Core::getInstance().isWorldLoaded()) {
out.printerr("World is not loaded: please load a game first.\n");
return CR_FAILURE;
}
if (enable)
enable_plugin(out);
else
disable_plugin(out);
return CR_OK;
}
static command_result fix_armory(color_ostream &out, vector <string> &parameters)
{
CoreSuspender suspend;
@ -820,13 +835,9 @@ static command_result fix_armory(color_ostream &out, vector <string> &parameters
string cmd = parameters[0];
if (cmd == "enable")
{
enable_plugin(out);
}
return plugin_enable(out, true);
else if (cmd == "disable")
{
disable_plugin(out);
}
return plugin_enable(out, false);
else
return CR_WRONG_USAGE;

@ -24,6 +24,7 @@ int32_t prevX, prevY, prevZ;
uint8_t prevMenuWidth;
DFHACK_PLUGIN("follow");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
@ -53,6 +54,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
followedUnit = 0;
prevX=prevY=prevZ = -1;
prevMenuWidth = 0;
is_enabled = false;
break;
default:
break;
@ -97,6 +99,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
}
else if((prevX != x || prevY != y || prevZ != z) && prevMenuWidth == menu_width) //User has manually moved the window, stop following the unit
{
is_enabled = false;
followedUnit = 0;
prevX=prevY=prevZ = -1;
prevMenuWidth = 0;
@ -146,6 +149,7 @@ command_result follow (color_ostream &out, std::vector <std::string> & parameter
followedUnit = Gui::getSelectedUnit(out);
if (followedUnit)
{
is_enabled = true;
std::ostringstream ss;
ss << "Unpause to begin following " << df::global::world->raws.creatures.all[followedUnit->race]->name[0];
if (followedUnit->name.has_name) ss << " " << followedUnit->name.first_name;
@ -153,5 +157,6 @@ command_result follow (color_ostream &out, std::vector <std::string> & parameter
out.print(ss.str().c_str());
}
else followedUnit = 0;
is_enabled = (followedUnit != NULL);
return CR_OK;
}

@ -71,7 +71,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
*/
static size_t constructionSize = 0;
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
void doInfiniteSky(color_ostream& out, int32_t howMany);
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
@ -155,6 +155,12 @@ void doInfiniteSky(color_ostream& out, int32_t howMany) {
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
enabled = enable;
return CR_OK;
}
command_result infiniteSky (color_ostream &out, std::vector <std::string> & parameters)
{
if ( parameters.size() > 1 )

@ -159,22 +159,22 @@ function empregnate(unit)
unit.curse.add_tags2.STERILE=false
end
local genes = unit.appearance.genes
if unit.relations.pregnancy_ptr == nil then
if unit.relations.pregnancy_genes == nil then
print("creating preg ptr.")
if false then
print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_ptr"))))
print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_genes"))))
return
end
unit.relations.pregnancy_ptr = { new = true, assign = genes }
unit.relations.pregnancy_genes = { new = true, assign = genes }
end
local ngenes = unit.relations.pregnancy_ptr
local ngenes = unit.relations.pregnancy_genes
if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then
print("Array sizes incorrect, fixing.")
ngenes:assign(genes);
end
print("Setting preg timer.")
unit.relations.pregnancy_timer=10
unit.relations.pregnancy_mystery=1
unit.relations.pregnancy_caste=1
end
menu:add("Empregnate",empregnate)
function healunit(unit)

@ -73,11 +73,10 @@ function job_outputs.CustomReaction(callback, job)
if mat then
local rp = mat.material.reaction_product
local idx = utils.linear_index(rp.id, p_code)
if not idx then
goto continue
local idx = utils.linear_index(rp.id, p_code, 'value')
if idx then
mat_type, mat_index = rp.material.mat_type[idx], rp.material.mat_index[idx]
end
mat_type, mat_index = rp.material.mat_type[idx], rp.material.mat_index[idx]
else
if p_code == "SOAP_MAT" then
mat_mask = { soap = true }

@ -1209,10 +1209,27 @@ IMPLEMENT_VMETHOD_INTERPOSE(unitlist_hook, render);
DFHACK_PLUGIN("manipulator");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(unitlist_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(unitlist_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{
if (!gps || !INTERPOSE_HOOK(unitlist_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_hook, render).apply())
out.printerr("Could not insert Dwarf Manipulator hooks!\n");
return CR_OK;
}

@ -15,6 +15,8 @@ using namespace std;
using namespace DFHack;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
static int factor = 1;
static map<int, int> processedThoughtCountTable;
@ -38,6 +40,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) {
if ( wasLoaded ) {
//we just unloaded the game: clear all data
factor = 1;
is_enabled = false;
processedThoughtCountTable.clear();
fakeThoughts.clear();
wasLoaded = false;
@ -138,6 +141,17 @@ DFhackCExport command_result plugin_init(color_ostream& out, vector<PluginComman
return CR_OK;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enable != is_enabled)
{
is_enabled = enable;
factor = enable ? 2 : 1;
}
return CR_OK;
}
command_result misery(color_ostream &out, vector<string>& parameters) {
if ( !df::global::world || !df::global::world->map.block_index ) {
out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n");
@ -153,15 +167,18 @@ command_result misery(color_ostream &out, vector<string>& parameters) {
return CR_WRONG_USAGE;
}
factor = 1;
is_enabled = false;
return CR_OK;
} else if ( parameters[0] == "enable" ) {
is_enabled = true;
factor = 2;
if ( parameters.size() == 2 ) {
factor = atoi(parameters[1].c_str());
if ( factor < 1 ) {
int a = atoi(parameters[1].c_str());
if ( a <= 1 ) {
out.printerr("Second argument must be a positive integer.\n");
return CR_WRONG_USAGE;
}
factor = a;
}
} else if ( parameters[0] == "clear" ) {
for ( size_t a = 0; a < fakeThoughts.size(); a++ ) {
@ -176,6 +193,7 @@ command_result misery(color_ostream &out, vector<string>& parameters) {
return CR_WRONG_USAGE;
}
factor = a;
is_enabled = factor > 1;
}
return CR_OK;

@ -33,12 +33,6 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
// add tracking here
return CR_OK;
}
void printCurrentModes(t_gamemodes gm, Console & con)
{
con << "Current game type:\t" << gm.g_type << " (";

@ -239,12 +239,28 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest
IMPLEMENT_VMETHOD_INTERPOSE(mousequery_hook, feed);
DFHACK_PLUGIN("mousequery");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (!gps || !INTERPOSE_HOOK(mousequery_hook, feed).apply())
out.printerr("Could not insert mousequery hooks!\n");
if (!gps)
return CR_FAILURE;
if (is_enabled != enable)
{
last_x = last_y = last_z = -1;
if (!INTERPOSE_HOOK(mousequery_hook, feed).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
last_x = last_y = last_z = -1;
return CR_OK;

@ -145,7 +145,7 @@ struct trap_hook : df::building_trapst {
INTERPOSE_NEXT(updateAction)();
}
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk))
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk))
{
INTERPOSE_NEXT(drawBuilding)(db, unk);
@ -160,7 +160,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, getName);
IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, updateAction);
IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, drawBuilding);
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
static void enable_hooks(bool enable)
{

@ -55,6 +55,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
static command_result rename(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("rename");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
@ -174,6 +175,9 @@ KNOWN_BUILDINGS
static bool enable_building_rename(char code, bool enable)
{
if (enable)
is_enabled = true;
if (code == 'Z')
INTERPOSE_HOOK(dwarf_render_zone_hook, render).apply(enable);
@ -189,6 +193,7 @@ KNOWN_BUILDINGS
static void disable_building_rename()
{
is_enabled = false;
INTERPOSE_HOOK(dwarf_render_zone_hook, render).remove();
#define BUILDING(code, cname, tag) \

@ -107,7 +107,7 @@ struct SuspendedBuilding
}
};
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
static bool buildings_scanned = false;
static vector<SuspendedBuilding> suspended_buildings, resumed_buildings;
@ -125,8 +125,10 @@ void scan_for_suspended_buildings()
SuspendedBuilding sb(bld);
sb.is_planned = job->job_items.size() == 1 && job->job_items[0]->item_type == item_type::NONE;
auto it = find_if(resumed_buildings.begin(), resumed_buildings.end(),
[&] (SuspendedBuilding &rsb) { return rsb.bld == bld; });
auto it = resumed_buildings.begin();
for (; it != resumed_buildings.end(); ++it)
if (it->bld == bld) break;
sb.was_resumed = it != resumed_buildings.end();
@ -234,6 +236,23 @@ struct resume_hook : public df::viewscreen_dwarfmodest
IMPLEMENT_VMETHOD_INTERPOSE(resume_hook, render);
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enabled != enable)
{
clear_scanned();
if (!INTERPOSE_HOOK(resume_hook, render).apply(enable))
return CR_FAILURE;
enabled = enable;
}
return CR_OK;
}
static command_result resume_cmd(color_ostream &out, vector <string> & parameters)
{
@ -251,12 +270,12 @@ static command_result resume_cmd(color_ostream &out, vector <string> & parameter
}
else if (cmd == 's')
{
enabled = true;
plugin_enable(out, true);
out << "Overlay enabled" << endl;
}
else if (cmd == 'h')
{
enabled = false;
plugin_enable(out, false);
out << "Overlay disabled" << endl;
}
else if (cmd == 'a')
@ -275,12 +294,8 @@ static command_result resume_cmd(color_ostream &out, vector <string> & parameter
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
if (!gps || !INTERPOSE_HOOK(resume_hook, render).apply())
out.printerr("Could not insert resume hooks!\n");
commands.push_back(
PluginCommand(
"resume", "A plugin to help display and resume suspended constructions conveniently",

@ -85,6 +85,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCom
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_active);
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
t_gamemodes gm;
@ -117,6 +119,7 @@ command_result nopause (color_ostream &out, vector <string> & parameters)
nopause_state = 0;
else
nopause_state = 1;
is_active = nopause_state || (revealed == REVEALED);
out.print("nopause %sactivated.\n", (nopause_state ? "" : "de"));
}
else
@ -237,6 +240,7 @@ command_result reveal(color_ostream &out, vector<string> & params)
else
revealed = DEMON_REVEALED;
}
is_active = nopause_state || (revealed == REVEALED);
con.print("Map revealed.\n");
if(!no_hell)
con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n");
@ -296,6 +300,7 @@ command_result unreveal(color_ostream &out, vector<string> & params)
// give back memory.
hidesaved.clear();
revealed = NOT_REVEALED;
is_active = nopause_state || (revealed == REVEALED);
con.print("Map hidden!\n");
return CR_OK;
}
@ -490,6 +495,7 @@ command_result revforget(color_ostream &out, vector<string> & params)
// give back memory.
hidesaved.clear();
revealed = NOT_REVEALED;
is_active = nopause_state || (revealed == REVEALED);
con.print("Reveal data forgotten!\n");
return CR_OK;
}

@ -53,15 +53,26 @@ module DFHack
end
# check item flags to see if it is suitable for use as a job input material
def item_isfree(i)
def item_isfree(i, check_empty=true)
!i.flags.trader and
!i.flags.in_job and
(!i.flags.in_inventory or i.general_refs.grep(GeneralRefContainedInItemst).first) and
!i.flags.construction and
!i.flags.removed and
!i.flags.in_building and
!i.flags.forbid and
!i.flags.dump and
!i.flags.owned and
!i.flags.forbid
!i.flags.in_chest and # used as hospital supply ?
(!i.flags.container or not check_empty or
!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainsItemst) }) and
(!i.flags.in_inventory or
(!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefUnitHolderst) and # allow hauled items TODO check if holder is a thief
ir.unit_tg.inventory.find { |ii| ii.item == i and ii.mode != :Hauled } } and
!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainedInItemst) and
!item_isfree(ir.item_tg, false) })) and
(!i.flags.in_building or
!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefBuildingHolderst) and
ir.building_tg.contained_items.find { |bi| bi.use_mode == 2 and bi.item == i } }) and
(!i.flags.on_ground or !df.map_tile_at(i).designation.hidden) # i.flags.unk11?
end
end
end

@ -475,7 +475,7 @@ module DFHack
class StlVector32 < MemStruct
attr_accessor :_tg
def initialize(tg)
@_tg = tg
@_tg = tg || Number.new(32, false, 0, nil)
end
def length

@ -46,6 +46,16 @@ static std::vector<std::string> *dfhack_run_queue;
DFHACK_PLUGIN("ruby")
DFhackDataExport bool plugin_is_enabled = true;
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
plugin_is_enabled = enable;
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
onupdate_active = 0;

@ -1609,6 +1609,7 @@ IMPLEMENT_HOOKS(df::viewscreen_dwarfmodest, burrow_search);
DFHACK_PLUGIN("search");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
#define SEARCH_HOOKS \
HOOK_ACTION(unitlist_search_hook) \
@ -1624,15 +1625,28 @@ DFHACK_PLUGIN("search");
HOOK_ACTION(burrow_search_hook) \
HOOK_ACTION(stockpile_search_hook)
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (!gps || !gview)
return CR_FAILURE;
if (is_enabled != enable)
{
#define HOOK_ACTION(hook) \
!INTERPOSE_HOOK(hook, feed).apply() || \
!INTERPOSE_HOOK(hook, render).apply() ||
!INTERPOSE_HOOK(hook, feed).apply(enable) || \
!INTERPOSE_HOOK(hook, render).apply(enable) ||
if (SEARCH_HOOKS 0)
return CR_FAILURE;
is_enabled = enable;
}
if (!gps || !gview || SEARCH_HOOKS 0)
out.printerr("Could not insert Search hooks!\n");
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{
#undef HOOK_ACTION
const string a[] = {"Meager Quarters", "Modest Quarters", "Quarters", "Decent Quarters", "Fine Quarters", "Great Bedroom", "Grand Bedroom", "Royal Bedroom"};

@ -22,7 +22,7 @@ using namespace df::enums;
using df::global::world;
const int buffer = 20; // seed number buffer - 20 is reasonable
bool running = false; // whether seedwatch is counting the seeds or not
DFHACK_PLUGIN_IS_ENABLED(running); // whether seedwatch is counting the seeds or not
// abbreviations for the standard plants
map<string, string> abbreviations;
@ -96,6 +96,12 @@ string searchAbbreviations(string in)
}
};
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
running = enable;
return CR_OK;
}
command_result df_seedwatch(color_ostream &out, vector<string>& parameters)
{
CoreSuspender suspend;

@ -1766,7 +1766,7 @@ DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_END
};
static bool is_enabled = false;
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
static void enable_hooks(bool enable)
{
@ -1809,6 +1809,25 @@ static void clear_caches(color_ostream &out)
}
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (gamemode && *gamemode != game_mode::DWARF)
return CR_FAILURE;
if (enable != is_enabled)
{
if (enable)
enable_plugin();
else
{
World::DeletePersistentData(World::GetPersistentData("siege-engine/enabled"));
enable_hooks(false);
}
}
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {

@ -721,7 +721,7 @@ struct workshop_hook : df::building_workshopst {
INTERPOSE_NEXT(updateAction)();
}
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk))
DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk))
{
INTERPOSE_NEXT(drawBuilding)(db, unk);
@ -950,8 +950,12 @@ static bool find_engines(color_ostream &out)
return !engines.empty();
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
static void enable_hooks(bool enable)
{
is_enabled = enable;
INTERPOSE_HOOK(liquid_hook, getItemDescription).apply(enable);
INTERPOSE_HOOK(liquid_hook, adjustTemperature).apply(enable);
INTERPOSE_HOOK(liquid_hook, checkTemperatureDamage).apply(enable);

@ -57,6 +57,7 @@
#include "df/activity_event_individual_skill_drillst.h"
#include "df/activity_event_skill_demonstrationst.h"
#include "df/activity_event_sparringst.h"
#include "df/building_hivest.h"
#include <stdlib.h>
@ -136,6 +137,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" to make them stand out more in the list.\n"
" tweak military-training [disable]\n"
" Speed up melee squad training, removing inverse dependency on unit count.\n"
" tweak hive-crash [disable]\n"
" Prevents crash if bees die in a hive with uncollected products (bug 6368).\n"
));
return CR_OK;
}
@ -932,19 +935,60 @@ struct military_training_id_hook : df::activity_event_individual_skill_drillst {
IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process);
struct hive_crash_hook : df::building_hivest {
typedef df::building_hivest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, updateAction, ())
{
bool any_bees = false;
for (size_t i = 0; i < contained_items.size(); i++)
{
if (contained_items[i]->item->getType() != item_type::VERMIN)
continue;
any_bees = true;
break;
}
if (!any_bees)
{
bool any_products = false;
for (size_t i = 0; i < contained_items.size(); i++)
{
if (contained_items[i]->use_mode != 0 ||
!contained_items[i]->item->flags.bits.in_building)
continue;
contained_items[i]->item->flags.bits.in_building = false;
any_products = true;
}
if (any_products)
{
color_ostream_proxy out(Core::getInstance().getConsole());
out.print("Bees died in hive with products at (%d,%d,%d); preventing crash.\n",
centerx, centery, z);
}
}
INTERPOSE_NEXT(updateAction)();
}
};
IMPLEMENT_VMETHOD_INTERPOSE(hive_crash_hook, updateAction);
static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector <string> &parameters)
{
if (vector_get(parameters, 1) == "disable")
{
hook.remove();
out.print("Disabled tweak %s\n", parameters[0].c_str());
out.print("Disabled tweak %s (%s)\n", parameters[0].c_str(), hook.name());
}
else
{
if (hook.apply())
out.print("Enabled tweak %s\n", parameters[0].c_str());
out.print("Enabled tweak %s (%s)\n", parameters[0].c_str(), hook.name());
else
out.printerr("Could not activate tweak %s\n", parameters[0].c_str());
out.printerr("Could not activate tweak %s (%s)\n", parameters[0].c_str(), hook.name());
}
}
@ -1112,6 +1156,10 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters);
}
else if (cmd == "hive-crash")
{
enable_hook(out, INTERPOSE_HOOK(hive_crash_hook, updateAction), parameters);
}
else
return CR_WRONG_USAGE;

@ -427,7 +427,7 @@ public:
void clearSelection()
{
for_each_(list, [] (ListEntry<T> &e) { e.selected = false; });
for_each_(list, clear_fn);
}
void selectItem(const T elem)
@ -550,8 +550,7 @@ public:
void sort()
{
if (force_sort || list.size() < 100)
std::sort(list.begin(), list.end(),
[] (ListEntry<T> const& a, ListEntry<T> const& b) { return a.text.compare(b.text) < 0; });
std::sort(list.begin(), list.end(), sort_fn);
filterDisplay();
}
@ -569,6 +568,9 @@ public:
}
private:
static void clear_fn(ListEntry<T> &e) { e.selected = false; }
static bool sort_fn(ListEntry<T> const& a, ListEntry<T> const& b) { return a.text.compare(b.text) < 0; }
vector<ListEntry<T>> list;
vector<ListEntry<T>*> display_list;
string search_string;

@ -402,7 +402,8 @@ public:
* GLOBAL VARIABLES *
******************************/
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
static PersistentDataItem config;
static int last_tick_frame_count = 0;
@ -1387,10 +1388,13 @@ static void update_data_structures(color_ostream &out)
* LUA API *
*************/
static bool isEnabled() { return enabled; }
static void setEnabled(color_ostream &out, bool enable)
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!Core::getInstance().isWorldLoaded()) {
out.printerr("World is not loaded: please load a game first.\n");
return CR_FAILURE;
}
if (enable && !enabled)
{
enable_plugin(out);
@ -1401,6 +1405,8 @@ static void setEnabled(color_ostream &out, bool enable)
setOptionEnabled(CF_ENABLED, false);
stop_protect(out);
}
return CR_OK;
}
static void push_count_history(lua_State *L, ItemConstraint *icv)
@ -1591,8 +1597,6 @@ static int getCountHistory(lua_State *L)
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(isEnabled),
DFHACK_LUA_FUNCTION(setEnabled),
DFHACK_LUA_FUNCTION(deleteConstraint),
DFHACK_LUA_END
};
@ -1767,10 +1771,10 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
{
bool enable = (cmd == "enable");
if (enable)
setEnabled(out, true);
plugin_enable(out, true);
else if (parameters.size() == 1)
{
setEnabled(out, false);
plugin_enable(out, false);
out << "The plugin is disabled." << endl;
return CR_OK;
@ -1805,7 +1809,10 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
}
if (!enabled)
out << "Note: the plugin is not enabled." << endl;
{
out.printerr("Error: the plugin is not enabled.\n");
return CR_WRONG_USAGE;
}
if (cmd == "jobs")
{

@ -88,6 +88,10 @@ command_result df_autobutcher(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("zone");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable);
const string zone_help =
"Allows easier management of pens/pastures, pits and cages.\n"
"Options:\n"
@ -3015,6 +3019,7 @@ command_result df_autobutcher(color_ostream &out, vector <string> & parameters)
}
else if (p == "start")
{
plugin_enable(out, true);
enable_autobutcher = true;
start_autobutcher(out);
return autoButcher(out, verbose);
@ -3485,6 +3490,7 @@ command_result autoButcher( color_ostream &out, bool verbose = false )
command_result start_autobutcher(color_ostream &out)
{
plugin_enable(out, true);
enable_autobutcher = true;
if (!config_autobutcher.isValid())
@ -3545,6 +3551,7 @@ command_result init_autobutcher(color_ostream &out)
if(!enable_autobutcher)
return CR_OK;
plugin_enable(out, true);
// read watchlist from save
std::vector<PersistentDataItem> items;
@ -3579,6 +3586,7 @@ command_result cleanup_autobutcher(color_ostream &out)
command_result start_autonestbox(color_ostream &out)
{
plugin_enable(out, true);
enable_autonestbox = true;
if (!config_autonestbox.isValid())
@ -3620,6 +3628,8 @@ command_result init_autonestbox(color_ostream &out)
sleep_autonestbox = config_autonestbox.ival(1);
}
}
if (enable_autonestbox)
plugin_enable(out, true);
return CR_OK;
}
@ -3856,6 +3866,9 @@ static void autobutcher_setEnabled(color_ostream &out, bool enable)
config_autobutcher.ival(0) = enable_autobutcher;
out << "Autobutcher stopped." << endl;
}
if (enable)
plugin_enable(out, true);
}
static void autowatch_setEnabled(color_ostream &out, bool enable)
@ -4455,12 +4468,25 @@ IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, render);
//END zone filters
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(zone_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(zone_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
if (!gps || !INTERPOSE_HOOK(zone_hook, feed).apply() || !INTERPOSE_HOOK(zone_hook, render).apply())
out.printerr("Could not insert jobutils hooks!\n");
commands.push_back(PluginCommand(
"zone", "manage activity zones.",
df_zone, false,

@ -0,0 +1,345 @@
-- Exports an ini file for Dwarf Therapist.
local utils = require 'utils'
local ms = require 'memscan'
-- Utility functions
local globals = df.global
local global_addr = dfhack.internal.getAddress
local os_type = dfhack.getOSType()
local rdelta = dfhack.internal.getRebaseDelta()
local vbias = 0
if os_type == 'windows' then vbias = -4 end
local lines = {}
local complete = true
local function header(name)
table.insert(lines, '')
table.insert(lines, '['..name..']')
end
local function value(name,addr)
local line
if not addr then
complete = false
line = name..'=0x0'
elseif addr < 0x10000 then
line = string.format('%s=0x%04x',name,addr)
else
line = string.format('%s=0x%08x',name,addr)
end
table.insert(lines, line)
end
local function address(name,bias,base,field,...)
local addr
if base == globals then
addr = global_addr(field)
bias = bias - rdelta
if addr and select('#',...) > 0 then
_,addr = df.sizeof(ms.field_ref(base,field,...))
end
elseif base._kind == 'class-type' then
-- field_offset crashes with classes due to vtable problems,
-- so we have to create a real temporary object here.
local obj = df.new(base)
if obj then
local _,a1 = df.sizeof(obj)
local _,a2 = df.sizeof(ms.field_ref(obj,field,...))
addr = a2-a1
obj:delete()
end
else
addr = ms.field_offset(base,field,...)
end
if addr then
addr = addr + bias
end
value(name, addr)
end
local function offset(name,base,...)
address(name,0,base,...)
end
local function vector(name,base,...)
address(name,vbias,base,...)
end
-- List of actual values
header('addresses')
vector('translation_vector',globals,'world','raws','language','translations')
vector('language_vector',globals,'world','raws','language','words')
vector('creature_vector',globals,'world','units','all')
vector('active_creature_vector',globals,'world','units','active')
offset('dwarf_race_index',globals,'ui','race_id')
vector('squad_vector',globals,'world','squads','all')
offset('current_year',globals,'cur_year')
offset('cur_year_tick',globals,'cur_year_tick')
offset('dwarf_civ_index',globals,'ui','civ_id')
vector('races_vector',globals,'world','raws','creatures','all')
vector('reactions_vector',globals,'world','raws','reactions')
vector('historical_figures',globals,'world','history','figures')
vector('fake_identities',globals,'world','assumed_identities','all')
vector('historical_figures_vector',globals,'world','history','figures')
vector('fake_identities_vector',globals,'world','assumed_identities','all')
offset('fortress_entity',globals,'ui','main','fortress_entity')
vector('historical_entities_vector',globals,'world','entities','all')
vector('weapons_vector',globals,'world','raws','itemdefs','weapons')
vector('trap_vector',globals,'world','raws','itemdefs','trapcomps')
vector('toy_vector',globals,'world','raws','itemdefs','toys')
vector('tool_vector',globals,'world','raws','itemdefs','tools')
vector('instrument_vector',globals,'world','raws','itemdefs','instruments')
vector('armor_vector',globals,'world','raws','itemdefs','armor')
vector('ammo_vector',globals,'world','raws','itemdefs','ammo')
vector('siegeammo_vector',globals,'world','raws','itemdefs','siege_ammo')
vector('glove_vector',globals,'world','raws','itemdefs','gloves')
vector('shoe_vector',globals,'world','raws','itemdefs','shoes')
vector('shield_vector',globals,'world','raws','itemdefs','shields')
vector('helm_vector',globals,'world','raws','itemdefs','helms')
vector('pant_vector',globals,'world','raws','itemdefs','pants')
vector('food_vector',globals,'world','raws','itemdefs','food')
vector('colors_vector',globals,'world','raws','language','colors')
vector('shapes_vector',globals,'world','raws','language','shapes')
offset('base_materials',globals,'world','raws','mat_table','builtin')
vector('inorganics_vector',globals,'world','raws','inorganics')
vector('plants_vector',globals,'world','raws','plants','all')
vector('material_templates_vector',globals,'world','raws','material_templates')
offset('world_data',globals,'world','world_data')
vector('active_sites_vector',df.world_data,'active_site')
offset('world_site_type',df.world_site,'type')
header('offsets')
offset('word_table',df.language_translation,'words')
value('string_buffer_offset', 0x0000)
header('word_offsets')
offset('base',df.language_word,'word')
offset('noun_singular',df.language_word,'forms','Noun')
offset('noun_plural',df.language_word,'forms','NounPlural')
offset('adjective',df.language_word,'forms','Adjective')
offset('verb',df.language_word,'forms','Verb')
offset('present_simple_verb',df.language_word,'forms','Verb3rdPerson')
offset('past_simple_verb',df.language_word,'forms','VerbPast')
offset('past_participle_verb',df.language_word,'forms','VerbPassive')
offset('present_participle_verb',df.language_word,'forms','VerbGerund')
offset('words',df.language_name,'words')
offset('word_type',df.language_name,'parts_of_speech')
offset('language_id',df.language_name,'language')
header('race_offsets')
offset('name_singular',df.creature_raw,'name',0)
offset('name_plural',df.creature_raw,'name',1)
offset('adjective',df.creature_raw,'name',2)
offset('baby_name_singular',df.creature_raw,'general_baby_name',0)
offset('baby_name_plural',df.creature_raw,'general_baby_name',1)
offset('child_name_singular',df.creature_raw,'general_child_name',0)
offset('child_name_plural',df.creature_raw,'general_child_name',1)
vector('pref_string_vector',df.creature_raw,'prefstring')
vector('castes_vector',df.creature_raw,'caste')
vector('pop_ratio_vector',df.creature_raw,'pop_ratio')
vector('materials_vector',df.creature_raw,'material')
offset('flags',df.creature_raw,'flags')
header('caste_offsets')
offset('caste_name',df.caste_raw,'caste_name')
offset('caste_descr',df.caste_raw,'description')
offset('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range')
offset('caste_ment_att_ranges',df.caste_raw,'attributes','ment_att_range')
offset('adult_size',df.caste_raw,'misc','adult_size')
offset('flags',df.caste_raw,'flags')
vector('extracts',df.caste_raw,'extracts','extract_matidx')
offset('skill_rates',df.caste_raw,'skill_rates')
offset('caste_att_rates',df.caste_raw,'attributes','phys_att_rates')
offset('caste_att_caps',df.caste_raw,'attributes','phys_att_cap_perc')
header('hist_entity_offsets')
vector('squads',df.historical_entity,'squads')
vector('positions',df.historical_entity,'positions','own')
vector('assignments',df.historical_entity,'positions','assignments')
offset('assign_hist_id',df.entity_position_assignment,'histfig')
offset('assign_position_id',df.entity_position_assignment,'position_id')
offset('position_id',df.entity_position,'id')
offset('position_name',df.entity_position,'name')
offset('position_female_name',df.entity_position,'name_female')
offset('position_male_name',df.entity_position,'name_male')
header('hist_figure_offsets')
offset('hist_race',df.historical_figure,'race')
offset('hist_name',df.historical_figure,'name')
offset('id',df.historical_figure,'id')
offset('hist_fig_info',df.historical_figure,'info')
offset('reputation',df.historical_figure_info,'reputation')
offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity')
offset('fake_name',df.assumed_identity,'name')
offset('fake_birth_year',df.assumed_identity,'birth_year')
offset('fake_birth_time',df.assumed_identity,'birth_second')
header('weapon_offsets')
offset('name_plural',df.itemdef_weaponst,'name_plural')
offset('single_size',df.itemdef_weaponst,'two_handed')
offset('multi_size',df.itemdef_weaponst,'minimum_size')
offset('ammo',df.itemdef_weaponst,'ranged_ammo')
header('material_offsets')
offset('solid_name',df.material_common,'state_name','Solid')
offset('liquid_name',df.material_common,'state_name','Liquid')
offset('gas_name',df.material_common,'state_name','Gas')
offset('powder_name',df.material_common,'state_name','Powder')
offset('paste_name',df.material_common,'state_name','Paste')
offset('pressed_name',df.material_common,'state_name','Pressed')
offset('inorganic_materials_vector',df.inorganic_raw,'material')
offset('flags',df.material_common,'flags')
header('plant_offsets')
offset('name',df.plant_raw,'name')
offset('name_plural',df.plant_raw,'name_plural')
offset('name_leaf_plural',df.plant_raw,'leaves_plural')
offset('name_seed_plural',df.plant_raw,'seed_plural')
vector('materials_vector',df.plant_raw,'material')
offset('flags',df.plant_raw,'flags')
header('item_offsets')
offset('name_plural',df.itemdef_armorst,'name_plural')
offset('adjective',df.itemdef_armorst,'name_preplural')
offset('mat_name',df.itemdef_armorst,'material_placeholder')
header('descriptor_offsets')
offset('color_name',df.descriptor_color,'name')
offset('shape_name_plural',df.descriptor_shape,'name_plural')
header('dwarf_offsets')
offset('first_name',df.unit,'name','first_name')
offset('nick_name',df.unit,'name','nickname')
offset('last_name',df.unit,'name','words')
offset('custom_profession',df.unit,'custom_profession')
offset('profession',df.unit,'profession')
offset('race',df.unit,'race')
offset('flags1',df.unit,'flags1')
offset('flags2',df.unit,'flags2')
offset('flags3',df.unit,'flags3')
offset('caste',df.unit,'caste')
offset('sex',df.unit,'sex')
offset('id',df.unit,'id')
offset('animal_type',df.unit,'training_level')
offset('civ',df.unit,'civ_id')
vector('specific_refs',df.unit,'specific_refs')
offset('squad_id',df.unit,'military','squad_id')
offset('squad_position',df.unit,'military','squad_position')
offset('recheck_equipment',df.unit,'military','pickup_flags')
offset('mood',df.unit,'mood')
offset('birth_year',df.unit,'relations','birth_year')
offset('birth_time',df.unit,'relations','birth_time')
offset('current_job',df.unit,'job','current_job')
offset('physical_attrs',df.unit,'body','physical_attrs')
vector('body_size',df.unit,'appearance','body_modifiers')
offset('curse',df.unit,'curse','name')
offset('curse_add_flags1',df.unit,'curse','add_tags1')
offset('turn_count',df.unit,'curse','time_on_site')
vector('souls',df.unit,'status','souls')
vector('states',df.unit,'status','misc_traits')
offset('labors',df.unit,'status','labors')
offset('happiness',df.unit,'status','happiness')
vector('thoughts',df.unit,'status','recent_events')
offset('squad_ref_id',df.unit,'hist_figure_id')
offset('hist_id',df.unit,'hist_figure_id')
offset('artifact_name',df.unit,'status','artifact_name')
header('soul_details')
offset('name',df.unit_soul,'name')
offset('mental_attrs',df.unit_soul,'mental_attrs')
vector('skills',df.unit_soul,'skills')
offset('traits',df.unit_soul,'traits')
vector('preferences',df.unit_soul,'preferences')
header('job_details')
offset('id',df.job,'job_type')
value('on_break_flag',df.misc_trait_type.OnBreak)
offset('sub_job_id',df.job,'reaction_name')
offset('reaction',df.reaction,'name')
offset('reaction_skill',df.reaction,'skill')
offset('mat_type',df.job,'mat_type')
offset('mat_index',df.job,'mat_index')
offset('mat_category',df.job,'material_category')
header('squad_offsets')
offset('id',df.squad,'id')
offset('name',df.squad,'name')
offset('name_old',df.squad,'name','words')
offset('alias',df.squad,'alias')
vector('members',df.squad,'positions')
-- Final creation of the file
local out = io.open('therapist.ini', 'w')
out:write('[info]\n')
-- TODO: add an api function to retrieve the checksum
out:write('checksum=<<fillme>>\n')
out:write('version_name='..dfhack.getDFVersion()..'\n')
out:write('complete='..(complete and 'true' or 'false')..'\n')
for i,v in ipairs(lines) do
out:write(v..'\n')
end
out:write[[
[valid_flags_1]
size=1
1\name=Not from around these parts
1\value=0x80000000
[valid_flags_2]
size=0
[invalid_flags_1]
size=10
1\name=a zombie
1\value=0x00001000
2\name=a skeleton
2\value=0x00002000
3\name=a merchant or diplomat
3\value=0x00000040
4\name=outpost liason
4\value=0x00000800
5\name=an invader or hostile
5\value=0x00020000
6\name=an invader or hostile
6\value=0x00080000
7\name=an invader or hostile
7\value=0x000C0000
8\name=a merchant escort
8\value=0x00000080
9\name="Dead, Jim."
9\value=0x00000002
10\name=marauder
10\value=0x00000010
[invalid_flags_2]
size=5
1\name="killed, Jim."
1\value=0x00000080
2\name=from the Underworld. SPOOKY!
2\value=0x00040000
3\name=resident
3\value=0x00080000
4\name=visitor_uninvited
4\value=0x00400000
5\name=visitor
5\value=0x00800000
[invalid_flags_3]
size=1
1\name=a ghost
1\value=0x00001000
]]
out:close()

@ -1306,6 +1306,27 @@ local function find_cur_year_tick()
ms.found_offset('cur_year_tick', addr)
end
local function find_cur_year_tick_advmode()
stop_autosave()
local feed = dwarfmode_to_top()
local addr = searcher:find_interactive(
'Searching for cur_year_tick_advmode.',
'int32_t',
function(idx)
if idx > 0 then
if not step_n_frames(1, feed) then
return false
end
end
return true, nil, 144
end,
20
)
ms.found_offset('cur_year_tick_advmode', addr)
end
--
-- cur_season_tick
--
@ -1329,7 +1350,7 @@ menu, then do as instructed below:]],
return false
end
end
return true, math.floor(((df.global.cur_year_tick+10)%100800)/10)
return true, math.floor((df.global.cur_year_tick%100800)/10)
end
)
ms.found_offset('cur_season_tick', addr)
@ -1361,7 +1382,7 @@ menu, then do as instructed below:]],
return false
end
end
return true, math.floor((df.global.cur_year_tick+10)/100800)%4
return true, math.floor(df.global.cur_year_tick/100800)%4
end
)
ms.found_offset('cur_season', addr)
@ -1499,6 +1520,7 @@ print('\nUnpausing globals:\n')
exec_finder(find_cur_year, 'cur_year')
exec_finder(find_cur_year_tick, 'cur_year_tick')
exec_finder(find_cur_year_tick_advmode, 'cur_year_tick_advmode')
exec_finder(find_cur_season_tick, 'cur_season_tick')
exec_finder(find_cur_season, 'cur_season')
exec_finder(find_process_jobs, 'process_jobs')

@ -5,6 +5,11 @@
-- This allows just adding stub definitions, and simply saving and
-- reloading the game.
-- Usage example:
--[[
devel/inject-raws trapcomp ITEM_TRAPCOMP_STEAM_PISTON workshop STEAM_ENGINE MAGMA_STEAM_ENGINE reaction STOKE_BOILER
]]
local utils = require 'utils'
local raws = df.global.world.raws

@ -0,0 +1,132 @@
-- Generates an image using multiple octaves of perlin noise.
local args = {...}
local rng = dfhack.random.new(3)
if #args < 3 then
qerror('Usage: devel/test-perlin <fname.pgm> <density> <coeff|expr...x[...y[...z]]>...')
end
local fname = table.remove(args,1)
local goal = tonumber(table.remove(args,1)) or qerror('Invalid density')
local zscale = 6
local coeffs = {}
local fns = {}
local env = copyall(math)
env.apow = function(x,y) return math.pow(math.abs(x),y) end
for i = 1,#args do
local fn = rng:perlin(3);
local fn2 = rng:perlin(3);
local fn3 = rng:perlin(3);
local fn4 = rng:perlin(3);
fns[i] = fn;
local val = string.match(args[i],'^([+-]?[%d.]+)$')
if val then
coeffs[i] = function(x) return x * val end
else
local argstr = 'x'
if string.match(args[i], '%f[%w]w%f[^%w]') then
argstr = 'x,y,z,w'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5), fn4(x+0.5,y+0.5,z+0.5)
end
elseif string.match(args[i], '%f[%w]z%f[^%w]') then
argstr = 'x,y,z'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5)
end
elseif string.match(args[i], '%f[%w]y%f[^%w]') then
argstr = 'x,y'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z)
end
end
local f,err = load(
'return function('..argstr..') return ('..args[i]..') end',
'=(expr)', 't', env
)
if not f then
qerror(err)
end
coeffs[i] = f()
end
end
function render(thresh,file)
local area = 0
local line, arr = '', {}
for zy = 0,1 do
for y = 0,48*4-1 do
line = ''
for zx = 0,1 do
for x = 0,48*4-1 do
local tx = (0.5+x)/(48*2)
local ty = (0.5+y)/(48*2)
local tz = 0.5+(zx+zy*2)/(48*2/zscale)
local v1 = 0
for i = 1,#coeffs do
v1 = v1 + coeffs[i](fns[i](tx,ty,tz))
tx = tx*2
ty = ty*2
tz = tz*2
end
local v = -1
if v1 > thresh then
v = v1;
area = area + 1
end
if file then
local c = math.max(0, math.min(255, v * 127 + 128))
arr[2*x+1] = c
arr[2*x+2] = c
end
end
if file then
line = line..string.char(table.unpack(arr))
end
end
if file then
file:write(line,line)
end
end
end
return area/4/(48*4)/(48*4)
end
function search(fn,min,max,goal,eps)
while true do
local center = (max+min)/2
local cval = fn(center)
print('At '..center..': '..cval)
if math.abs(cval-goal) < eps then
return center
end
if cval > goal then
min = center
else
max = center
end
end
end
local thresh = search(render, -1, 1, goal, 0.001)
local file,err = io.open(fname, 'wb')
if not file then
print('error: ',err)
return
end
file:write('P5\n768 768\n255\n')
local area = render(thresh, file)
file:close()
print('Area fraction: '..area)

@ -8,7 +8,7 @@ if cmd=="--file" or cmd=="-f" then
if f==nil then
qerror(err)
end
dfhack.pcall(f,table.unpack(args,3))
dfhack.safecall(f,table.unpack(args,3))
elseif cmd=="--save" or cmd=="-s" then
if df.global.world.cur_savegame.save_dir=="" then
qerror("Savefile not loaded")
@ -19,7 +19,7 @@ elseif cmd=="--save" or cmd=="-s" then
if f==nil then
qerror(err)
end
dfhack.pcall(f,table.unpack(args,3))
dfhack.safecall(f,table.unpack(args,3))
elseif cmd~=nil then
-- Support some of the prefixes allowed by dfhack.interpreter
local prefix

@ -0,0 +1,9 @@
# patch start dwarf count
nr = $script_args[0].to_i
raise 'too low' if nr < 7
addr = df.get_global_address('start_dwarf_count')
df.memory_patch(addr, [nr].pack('L'))