Merge remote-tracking branch 'upstream/master'

develop
Robert Heinrich 2012-03-24 12:02:50 +01:00
commit 106aedbaa5
29 changed files with 4069 additions and 1267 deletions

@ -53,7 +53,7 @@ set(DF_VERSION_MINOR "34")
set(DF_VERSION_PATCH "05")
set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}")
set(DFHACK_RELEASE "1e")
set(DFHACK_RELEASE "1f")
set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-r${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
@ -103,6 +103,7 @@ ENDIF()
# use shared libraries for protobuf
ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS)
ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL)
if(UNIX)
add_definitions(-D_LINUX)

@ -1,6 +1,9 @@
PROJECT ( lua C )
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# TODO: make this RelWithDebInfo only
ADD_DEFINITIONS(-DLUA_USE_APICHECK)
IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE )
ELSE()
@ -76,8 +79,13 @@ src/lzio.c
)
LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA})
ADD_LIBRARY ( lua STATIC EXCLUDE_FROM_ALL ${SRC_LIBLUA} )
ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} )
TARGET_LINK_LIBRARIES ( lua ${LIBS})
install(TARGETS lua
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})
IDE_FOLDER(lua "Depends")
#SET ( SRC_LUA src/lua.c )

@ -151,11 +151,20 @@
** the libraries, you may want to use the following definition (define
** LUA_BUILD_AS_DLL to get it).
*/
#ifdef __cplusplus
#define LUA_API_EXTERN extern "C"
#else
#define LUA_API_EXTERN extern
#endif
#if defined(LUA_BUILD_AS_DLL)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport)
#if defined(_MSC_VER)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport) LUA_API_EXTERN
#else
#define LUA_API __declspec(dllimport) LUA_API_EXTERN
#endif
#else
#define LUA_API __declspec(dllimport)
#define LUA_API LUA_API_EXTERN __attribute__ ((visibility("default")))
#endif
#else
#ifdef __cplusplus

@ -55,8 +55,10 @@ SET(MAIN_SOURCES
Core.cpp
ColorText.cpp
DataDefs.cpp
LuaWrapper.cpp
DataStatics.cpp
DataStaticsCtor.cpp
DataStaticsFields.cpp
MiscUtils.cpp
PluginManager.cpp
TileTypes.cpp
@ -195,6 +197,14 @@ ADD_CUSTOM_COMMAND(
ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static.inc)
IF(UNIX)
# Don't produce debug info for generated stubs
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32)
ENDIF()
# Compilation
ADD_DEFINITIONS(-DBUILD_DFHACK_LIB)
@ -242,7 +252,7 @@ ENDIF()
#effectively disables debug builds...
SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" )
TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket ${PROJECT_LIBS})
TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS})
SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "")
TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket)

@ -34,28 +34,186 @@ distribution.
#include "tinythread.h"
// must be last due to MS stupidity
#include "DataDefs.h"
#include "DataIdentity.h"
#include "MiscUtils.h"
using namespace DFHack;
void *type_identity::do_allocate_pod() {
void *p = malloc(size);
memset(p, 0, size);
return p;
}
void type_identity::do_copy_pod(void *tgt, const void *src) {
memmove(tgt, src, size);
};
void *type_identity::allocate() {
if (can_allocate())
return do_allocate();
else
return NULL;
}
bool type_identity::copy(void *tgt, const void *src) {
if (can_allocate() && tgt && src)
do_copy(tgt, src);
else
return false;
}
void *enum_identity::do_allocate() {
void *p = malloc(byte_size());
memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t)));
return p;
}
/* The order of global object constructor calls is
* undefined between compilation units. Therefore,
* this list has to be plain data, so that it gets
* initialized by the loader in the initial mmap.
*/
virtual_identity *virtual_identity::list = NULL;
compound_identity *compound_identity::list = NULL;
std::vector<compound_identity*> compound_identity::top_scope;
virtual_identity::virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent)
: dfhack_name(dfhack_name), original_name(original_name), parent(parent),
prev(NULL), vtable_ptr(NULL), has_children(true)
compound_identity::compound_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name)
: constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name)
{
// Link into the static list. Nothing else can be safely done at this point.
next = list; list = this;
next = list; list = this;
}
void compound_identity::doInit(Core *)
{
if (scope_parent)
scope_parent->scope_children.push_back(this);
else
top_scope.push_back(this);
}
std::string compound_identity::getFullName()
{
if (scope_parent)
return scope_parent->getFullName() + "." + getName();
else
return getName();
}
/* Vtable to identity lookup. */
static tthread::mutex *known_mutex = NULL;
void compound_identity::Init(Core *core)
{
if (!known_mutex)
known_mutex = new tthread::mutex();
// This cannot be done in the constructors, because
// they are called in an undefined order.
for (compound_identity *p = list; p; p = p->next)
p->doInit(core);
//FIXME: ... nuked. the group was empty...
/*
// Read pre-filled vtable ptrs
OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
for (virtual_identity *p = list; p; p = p->next) {
void * tmp;
if (ptr_table->getSafeAddress(p->getName(),tmp))
p->vtable_ptr = tmp;
}
*/
}
bitfield_identity::bitfield_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
int num_bits, const bitfield_item_info *bits)
: compound_identity(size, NULL, scope_parent, dfhack_name), bits(bits), num_bits(num_bits)
{
}
enum_identity::enum_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
type_identity *base_type,
int64_t first_item_value, int64_t last_item_value,
const char *const *keys)
: compound_identity(size, NULL, scope_parent, dfhack_name),
first_item_value(first_item_value), last_item_value(last_item_value),
keys(keys), base_type(base_type)
{
}
struct_identity::struct_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name,
struct_identity *parent, const struct_field_info *fields)
: compound_identity(size, alloc, scope_parent, dfhack_name),
parent(parent), has_children(false), fields(fields)
{
}
void struct_identity::doInit(Core *core)
{
compound_identity::doInit(core);
if (parent) {
parent->children.push_back(this);
parent->has_children = true;
}
}
bool struct_identity::is_subclass(struct_identity *actual)
{
for (; actual; actual = actual->getParent())
if (actual == this) return true;
return false;
}
std::string pointer_identity::getFullName()
{
return (target ? target->getFullName() : std::string("void")) + "*";
}
std::string container_identity::getFullName(type_identity *item)
{
return "<" + (item ? item->getFullName() : std::string("void")) + ">";
}
std::string ptr_container_identity::getFullName(type_identity *item)
{
return "<" + (item ? item->getFullName() : std::string("void")) + "*>";
}
std::string bit_container_identity::getFullName(type_identity *)
{
return "<bool>";
}
std::string df::buffer_container_identity::getFullName(type_identity *item)
{
return (item ? item->getFullName() : std::string("void")) +
(size > 0 ? stl_sprintf("[%d]", size) : std::string("[]"));
}
virtual_identity::virtual_identity(size_t size, TAllocateFn alloc,
const char *dfhack_name, const char *original_name,
virtual_identity *parent, const struct_field_info *fields)
: struct_identity(size, alloc, NULL, dfhack_name, parent, fields), original_name(original_name),
vtable_ptr(NULL)
{
}
static std::map<std::string, virtual_identity*> name_lookup;
void virtual_identity::doInit(Core *core)
{
struct_identity::doInit(core);
name_lookup[getOriginalName()] = this;
}
/* Vtable to identity lookup. */
std::map<void*, virtual_identity*> virtual_identity::known;
virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
@ -78,8 +236,9 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
virtual_identity *actual = NULL;
for (virtual_identity *p = list; p; p = p->next) {
if (strcmp(name.c_str(), p->getOriginalName()) != 0) continue;
auto name_it = name_lookup.find(name);
if (name_it != name_lookup.end()) {
virtual_identity *p = name_it->second;
if (p->vtable_ptr && p->vtable_ptr != vtable) {
std::cerr << "Conflicting vtable ptr for class '" << p->getName()
@ -103,14 +262,6 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
return NULL;
}
bool virtual_identity::is_subclass(virtual_identity *actual)
{
for (; actual; actual = actual->parent)
if (actual == this) return true;
return false;
}
void virtual_identity::adjust_vtable(virtual_ptr obj, virtual_identity *main)
{
if (vtable_ptr) {
@ -135,35 +286,6 @@ virtual_ptr virtual_identity::clone(virtual_ptr obj)
return copy;
}
void virtual_identity::Init(Core *core)
{
if (!known_mutex)
known_mutex = new tthread::mutex();
// This cannot be done in the constructors, because
// they are called in an undefined order.
for (virtual_identity *p = list; p; p = p->next) {
p->has_children = false;
p->children.clear();
}
for (virtual_identity *p = list; p; p = p->next) {
if (p->parent) {
p->parent->children.push_back(p);
p->parent->has_children = true;
}
}
//FIXME: ... nuked. the group was empty...
/*
// Read pre-filled vtable ptrs
OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
for (virtual_identity *p = list; p; p = p->next) {
void * tmp;
if (ptr_table->getSafeAddress(p->getName(),tmp))
p->vtable_ptr = tmp;
}
*/
}
bool DFHack::findBitfieldField(unsigned *idx, const std::string &name,
unsigned size, const bitfield_item_info *items)
{

@ -7,6 +7,8 @@
#include "df/world_data.h"
#include "df/ui.h"
#include "DataIdentity.h"
namespace {
template<class T>
inline T &_toref(T &r) { return r; }
@ -21,6 +23,8 @@ namespace {
#define INIT_GLOBAL_FUNCTION_ITEM(type,name) \
if (global_table_->getAddress(#name,tmp_)) name = (type*)tmp_;
#define TID(type) (&identity_traits< type >::identity)
// Instantiate all the static objects
#include "df/static.inc"
#include "df/static.enums.inc"

@ -0,0 +1,49 @@
#include "Internal.h"
#include "DataDefs.h"
#include "MiscUtils.h"
#include "VersionInfo.h"
#include "df/world.h"
#include "df/world_data.h"
#include "df/ui.h"
#include "DataIdentity.h"
#include <stddef.h>
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
namespace df {
#define NUMBER_IDENTITY_TRAITS(type) \
number_identity<type> identity_traits<type>::identity(#type);
NUMBER_IDENTITY_TRAITS(char);
NUMBER_IDENTITY_TRAITS(int8_t);
NUMBER_IDENTITY_TRAITS(uint8_t);
NUMBER_IDENTITY_TRAITS(int16_t);
NUMBER_IDENTITY_TRAITS(uint16_t);
NUMBER_IDENTITY_TRAITS(int32_t);
NUMBER_IDENTITY_TRAITS(uint32_t);
NUMBER_IDENTITY_TRAITS(int64_t);
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
bool_identity identity_traits<bool>::identity;
stl_string_identity identity_traits<std::string>::identity;
pointer_identity identity_traits<void*>::identity;
stl_ptr_vector_identity identity_traits<std::vector<void*> >::identity;
stl_bit_vector_identity identity_traits<std::vector<bool> >::identity;
buffer_container_identity buffer_container_identity::base_instance;
#undef NUMBER_IDENTITY_TRAITS
}
#define TID(type) (&identity_traits< type >::identity)
#define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name)
#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
#define FLD_END struct_field_info::END
// Field definitions
#include "df/static.fields.inc"

File diff suppressed because it is too large Load Diff

@ -37,6 +37,8 @@ distribution.
#undef interface
#endif
typedef struct lua_State lua_State;
/*
* Definitions of DFHack namespace structs used by generated headers.
*/
@ -45,47 +47,252 @@ namespace DFHack
{
class virtual_class {};
enum identity_type {
IDTYPE_GLOBAL,
IDTYPE_PRIMITIVE,
IDTYPE_POINTER,
IDTYPE_CONTAINER,
IDTYPE_PTR_CONTAINER,
IDTYPE_BIT_CONTAINER,
IDTYPE_BITFIELD,
IDTYPE_ENUM,
IDTYPE_STRUCT,
IDTYPE_CLASS,
IDTYPE_STL_PTR_VECTOR
};
typedef void *(*TAllocateFn)(void*,const void*);
class DFHACK_EXPORT type_identity {
size_t size;
protected:
type_identity(size_t size) : size(size) {};
void *do_allocate_pod();
void do_copy_pod(void *tgt, const void *src);
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
public:
virtual ~type_identity() {}
size_t byte_size() { return size; }
virtual identity_type type() = 0;
virtual std::string getFullName() = 0;
// For internal use in the lua wrapper
virtual void lua_read(lua_State *state, int fname_idx, void *ptr) = 0;
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0;
virtual void build_metatable(lua_State *state);
virtual bool isContainer() { return false; }
void *allocate();
bool copy(void *tgt, const void *src);
};
class DFHACK_EXPORT constructed_identity : public type_identity {
TAllocateFn allocator;
protected:
constructed_identity(size_t size, TAllocateFn alloc)
: type_identity(size), allocator(alloc) {};
virtual bool can_allocate() { return (allocator != NULL); }
virtual void *do_allocate() { return allocator(NULL,NULL); }
virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class DFHACK_EXPORT compound_identity : public constructed_identity {
static compound_identity *list;
compound_identity *next;
const char *dfhack_name;
compound_identity *scope_parent;
std::vector<compound_identity*> scope_children;
static std::vector<compound_identity*> top_scope;
protected:
compound_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name);
virtual void doInit(Core *core);
public:
const char *getName() { return dfhack_name; }
virtual std::string getFullName();
compound_identity *getScopeParent() { return scope_parent; }
const std::vector<compound_identity*> &getScopeChildren() { return scope_children; }
static const std::vector<compound_identity*> &getTopScope() { return top_scope; }
static void Init(Core *core);
};
// Bitfields
struct bitfield_item_info {
const char *name;
int size;
};
class DFHACK_EXPORT bitfield_identity : public compound_identity {
const bitfield_item_info *bits;
int num_bits;
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
public:
bitfield_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
int num_bits, const bitfield_item_info *bits);
virtual identity_type type() { return IDTYPE_BITFIELD; }
int getNumBits() { return num_bits; }
const bitfield_item_info *getBits() { return bits; }
virtual void build_metatable(lua_State *state);
};
class DFHACK_EXPORT enum_identity : public compound_identity {
const char *const *keys;
int64_t first_item_value;
int64_t last_item_value;
type_identity *base_type;
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate();
virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
public:
enum_identity(size_t size,
compound_identity *scope_parent, const char *dfhack_name,
type_identity *base_type,
int64_t first_item_value, int64_t last_item_value,
const char *const *keys);
virtual identity_type type() { return IDTYPE_ENUM; }
int64_t getFirstItem() { return first_item_value; }
int64_t getLastItem() { return last_item_value; }
int getCount() { return int(last_item_value-first_item_value+1); }
const char *const *getKeys() { return keys; }
type_identity *getBaseType() { return base_type; }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
struct struct_field_info {
enum Mode {
END,
PRIMITIVE,
STATIC_STRING,
POINTER,
STATIC_ARRAY,
SUBSTRUCT,
CONTAINER,
STL_VECTOR_PTR
};
Mode mode;
const char *name;
size_t offset;
type_identity *type;
size_t count;
enum_identity *eid;
};
class DFHACK_EXPORT struct_identity : public compound_identity {
struct_identity *parent;
std::vector<struct_identity*> children;
bool has_children;
const struct_field_info *fields;
protected:
virtual void doInit(Core *core);
public:
struct_identity(size_t size, TAllocateFn alloc,
compound_identity *scope_parent, const char *dfhack_name,
struct_identity *parent, const struct_field_info *fields);
virtual identity_type type() { return IDTYPE_STRUCT; }
struct_identity *getParent() { return parent; }
const std::vector<struct_identity*> &getChildren() { return children; }
bool hasChildren() { return has_children; }
const struct_field_info *getFields() { return fields; }
bool is_subclass(struct_identity *subtype);
virtual void build_metatable(lua_State *state);
};
class DFHACK_EXPORT global_identity : public struct_identity {
public:
global_identity(const struct_field_info *fields)
: struct_identity(0,NULL,NULL,"global",NULL,fields) {}
virtual identity_type type() { return IDTYPE_GLOBAL; }
virtual void build_metatable(lua_State *state);
};
#ifdef _MSC_VER
typedef void *virtual_ptr;
#else
typedef virtual_class *virtual_ptr;
#endif
class DFHACK_EXPORT virtual_identity {
static virtual_identity *list;
class DFHACK_EXPORT virtual_identity : public struct_identity {
static std::map<void*, virtual_identity*> known;
virtual_identity *prev, *next;
const char *dfhack_name;
const char *original_name;
virtual_identity *parent;
std::vector<virtual_identity*> children;
void *vtable_ptr;
bool has_children;
protected:
virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent);
virtual void doInit(Core *core);
static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; }
bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); }
public:
const char *getName() { return dfhack_name; }
const char *getOriginalName() { return original_name ? original_name : dfhack_name; }
virtual_identity(size_t size, TAllocateFn alloc,
const char *dfhack_name, const char *original_name,
virtual_identity *parent, const struct_field_info *fields);
virtual_identity *getParent() { return parent; }
const std::vector<virtual_identity*> &getChildren() { return children; }
virtual identity_type type() { return IDTYPE_CLASS; }
const char *getOriginalName() { return original_name ? original_name : getName(); }
public:
static virtual_identity *get(virtual_ptr instance_ptr);
bool is_subclass(virtual_identity *subtype);
bool is_instance(virtual_ptr instance_ptr) {
if (!instance_ptr) return false;
if (vtable_ptr) {
void *vtable = get_vtable(instance_ptr);
if (vtable == vtable_ptr) return true;
if (!has_children) return false;
if (!hasChildren()) return false;
}
return is_subclass(get(instance_ptr));
}
@ -97,16 +304,11 @@ namespace DFHack
}
public:
bool can_instantiate() { return (vtable_ptr != NULL); }
virtual_ptr instantiate() { return can_instantiate() ? do_instantiate() : NULL; }
bool can_instantiate() { return can_allocate(); }
virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; }
static virtual_ptr clone(virtual_ptr obj);
protected:
virtual virtual_ptr do_instantiate() = 0;
virtual void do_copy(virtual_ptr tgt, virtual_ptr src) = 0;
public:
static void Init(Core *core);
// Strictly for use in virtual class constructors
void adjust_vtable(virtual_ptr obj, virtual_identity *main);
};
@ -127,6 +329,23 @@ namespace DFHack
void InitDataDefGlobals(Core *core);
// LUA wrapper
/**
* Make DF objects available to the given interpreter.
*/
DFHACK_EXPORT void AttachDFGlobals(lua_State *state);
/**
* Push the pointer onto the stack as a wrapped DF object of the given type.
*/
DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr);
/**
* Check that the value is a wrapped DF object of the given type, and if so return the pointer.
*/
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index);
template<class T>
T *ifnull(T *a, T *b) { return a ? a : b; }
@ -135,12 +354,6 @@ namespace DFHack
size_t size;
const T *items;
};
// Bitfields
struct bitfield_item_info {
const char *name;
int size;
};
}
template<class T>
@ -164,33 +377,37 @@ inline int linear_index(const DFHack::enum_list_attr<const char*> &lst, const st
namespace df
{
using DFHack::type_identity;
using DFHack::compound_identity;
using DFHack::virtual_ptr;
using DFHack::virtual_identity;
using DFHack::virtual_class;
using DFHack::global_identity;
using DFHack::struct_identity;
using DFHack::struct_field_info;
using DFHack::bitfield_item_info;
using DFHack::bitfield_identity;
using DFHack::enum_identity;
using DFHack::enum_list_attr;
using DFHack::BitArray;
using DFHack::DfArray;
template<class T>
struct enum_traits {};
void *allocator_fn(void *out, const void *in) {
if (out) { *(T*)out = *(const T*)in; return out; }
else return new T();
}
template<class T>
struct bitfield_traits {};
struct identity_traits {
static compound_identity *get() { return &T::_identity; }
};
template<class T>
class class_virtual_identity : public virtual_identity {
public:
class_virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent)
: virtual_identity(dfhack_name, original_name, parent) {};
T *instantiate() { return static_cast<T*>(virtual_identity::instantiate()); }
T *clone(T* obj) { return static_cast<T*>(virtual_identity::clone(obj)); }
struct enum_traits {};
protected:
virtual virtual_ptr do_instantiate() { return new T(); }
virtual void do_copy(virtual_ptr tgt, virtual_ptr src) { *static_cast<T*>(tgt) = *static_cast<T*>(src); }
};
template<class T>
struct bitfield_traits {};
template<class EnumType, class IntType = int32_t>
struct enum_field {
@ -427,6 +644,24 @@ namespace DFHack {
flagarray_to_string<T>(&tmp, val);
return join_strings(sep, tmp);
}
// LUA wrapper
/**
* Push the pointer onto the stack as a wrapped DF object of a specific type.
*/
template<class T>
void PushDFObject(lua_State *state, T *ptr) {
PushDFObject(state, df::identity_traits<T>::get(), ptr);
}
/**
* Check that the value is a wrapped DF object of the correct type, and if so return the pointer.
*/
template<class T>
T *GetDFObject(lua_State *state, int val_index) {
return GetDFObject(state, df::identity_traits<T>::get(), val_index);
}
}
#define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr)

@ -0,0 +1,438 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include "DataDefs.h"
/*
* Definitions of DFHack namespace structs used by generated headers.
*/
namespace DFHack
{
class DFHACK_EXPORT primitive_identity : public type_identity {
public:
primitive_identity(size_t size) : type_identity(size) {};
virtual identity_type type() { return IDTYPE_PRIMITIVE; }
};
class DFHACK_EXPORT pointer_identity : public primitive_identity {
type_identity *target;
public:
pointer_identity(type_identity *target = NULL)
: primitive_identity(sizeof(void*)), target(target) {};
virtual identity_type type() { return IDTYPE_POINTER; }
type_identity *getTarget() { return target; }
std::string getFullName();
static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target);
static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index);
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class DFHACK_EXPORT container_identity : public constructed_identity {
type_identity *item;
enum_identity *ienum;
public:
container_identity(size_t size, TAllocateFn alloc, type_identity *item, enum_identity *ienum = NULL)
: constructed_identity(size, alloc), item(item), ienum(ienum) {};
virtual identity_type type() { return IDTYPE_CONTAINER; }
std::string getFullName() { return getFullName(item); }
virtual void build_metatable(lua_State *state);
virtual bool isContainer() { return true; }
type_identity *getItemType() { return item; }
type_identity *getIndexEnumType() { return ienum; }
virtual std::string getFullName(type_identity *item);
int lua_item_count(lua_State *state, void *ptr);
virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
protected:
virtual int item_count(void *ptr) = 0;
virtual void *item_pointer(type_identity *item, void *ptr, int idx) = 0;
};
class DFHACK_EXPORT ptr_container_identity : public container_identity {
public:
ptr_container_identity(size_t size, TAllocateFn alloc,
type_identity *item, enum_identity *ienum = NULL)
: container_identity(size, alloc, item, ienum) {};
virtual identity_type type() { return IDTYPE_PTR_CONTAINER; }
std::string getFullName(type_identity *item);
virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
};
class DFHACK_EXPORT bit_container_identity : public container_identity {
public:
bit_container_identity(size_t size, TAllocateFn alloc, enum_identity *ienum = NULL)
: container_identity(size, alloc, NULL, ienum) {};
virtual identity_type type() { return IDTYPE_BIT_CONTAINER; }
std::string getFullName(type_identity *item);
virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
protected:
virtual void *item_pointer(type_identity *, void *, int) { return NULL; }
virtual bool get_item(void *ptr, int idx) = 0;
virtual void set_item(void *ptr, int idx, bool val) = 0;
};
}
namespace df
{
using DFHack::primitive_identity;
using DFHack::pointer_identity;
using DFHack::container_identity;
using DFHack::ptr_container_identity;
using DFHack::bit_container_identity;
class number_identity_base : public primitive_identity {
const char *name;
public:
number_identity_base(size_t size, const char *name)
: primitive_identity(size), name(name) {};
std::string getFullName() { return name; }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
protected:
virtual double read(void *ptr) = 0;
virtual void write(void *ptr, double val) = 0;
};
template<class T>
class number_identity : public number_identity_base {
public:
number_identity(const char *name) : number_identity_base(sizeof(T), name) {}
protected:
virtual double read(void *ptr) { return double(*(T*)ptr); }
virtual void write(void *ptr, double val) { *(T*)ptr = T(val); }
};
class bool_identity : public primitive_identity {
public:
bool_identity() : primitive_identity(sizeof(bool)) {};
std::string getFullName() { return "bool"; }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_string_identity : public primitive_identity {
public:
stl_string_identity() : primitive_identity(sizeof(std::string)) {};
std::string getFullName() { return "string"; }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class stl_ptr_vector_identity : public ptr_container_identity {
public:
/*
* This class assumes that std::vector<T*> is equivalent
* in layout and behavior to std::vector<void*> for any T.
*/
stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL)
: ptr_container_identity(sizeof(std::vector<void*>),allocator_fn<std::vector<void*> >,item, ienum)
{};
std::string getFullName(type_identity *item) {
return "vector" + ptr_container_identity::getFullName(item);
}
virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; }
protected:
virtual int item_count(void *ptr) {
return ((std::vector<void*>*)ptr)->size();
};
virtual void *item_pointer(type_identity *, void *ptr, int idx) {
return &(*(std::vector<void*>*)ptr)[idx];
}
};
class buffer_container_identity : public container_identity {
int size;
public:
buffer_container_identity()
: container_identity(0, NULL, NULL, NULL), size(0)
{}
buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL)
: container_identity(item->byte_size()*size, NULL, item, ienum), size(size)
{}
std::string getFullName(type_identity *item);
static buffer_container_identity base_instance;
protected:
virtual int item_count(void *ptr) { return size; }
virtual void *item_pointer(type_identity *item, void *ptr, int idx) {
return ((uint8_t*)ptr) + idx * item->byte_size();
}
};
template<class T>
class stl_container_identity : public container_identity {
const char *name;
public:
stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL)
: container_identity(sizeof(T), &allocator_fn<T>, item, ienum), name(name)
{}
std::string getFullName(type_identity *item) {
return name + container_identity::getFullName(item);
}
protected:
virtual int item_count(void *ptr) { return ((T*)ptr)->size(); }
virtual void *item_pointer(type_identity *item, void *ptr, int idx) {
return &(*(T*)ptr)[idx];
}
};
class bit_array_identity : public bit_container_identity {
public:
/*
* This class assumes that BitArray<T> is equivalent
* in layout and behavior to BitArray<int> for any T.
*/
typedef BitArray<int> container;
bit_array_identity(enum_identity *ienum = NULL)
: bit_container_identity(sizeof(container), &allocator_fn<container>, ienum)
{}
std::string getFullName(type_identity *item) {
return "BitArray<>";
}
protected:
virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; }
virtual bool get_item(void *ptr, int idx) {
return ((container*)ptr)->is_set(idx);
}
virtual void set_item(void *ptr, int idx, bool val) {
((container*)ptr)->set(idx, val);
}
};
class stl_bit_vector_identity : public bit_container_identity {
public:
typedef std::vector<bool> container;
stl_bit_vector_identity(enum_identity *ienum = NULL)
: bit_container_identity(sizeof(container), &allocator_fn<container>, ienum)
{}
std::string getFullName(type_identity *item) {
return "vector" + bit_container_identity::getFullName(item);
}
protected:
virtual int item_count(void *ptr) { return ((container*)ptr)->size(); }
virtual bool get_item(void *ptr, int idx) {
return (*(container*)ptr)[idx];
}
virtual void set_item(void *ptr, int idx, bool val) {
(*(container*)ptr)[idx] = val;
}
};
#define NUMBER_IDENTITY_TRAITS(type) \
template<> struct identity_traits<type> { \
static number_identity<type> identity; \
static number_identity_base *get() { return &identity; } \
};
NUMBER_IDENTITY_TRAITS(char);
NUMBER_IDENTITY_TRAITS(int8_t);
NUMBER_IDENTITY_TRAITS(uint8_t);
NUMBER_IDENTITY_TRAITS(int16_t);
NUMBER_IDENTITY_TRAITS(uint16_t);
NUMBER_IDENTITY_TRAITS(int32_t);
NUMBER_IDENTITY_TRAITS(uint32_t);
NUMBER_IDENTITY_TRAITS(int64_t);
NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float);
template<> struct identity_traits<bool> {
static bool_identity identity;
static bool_identity *get() { return &identity; }
};
template<> struct identity_traits<std::string> {
static stl_string_identity identity;
static stl_string_identity *get() { return &identity; }
};
template<> struct identity_traits<void*> {
static pointer_identity identity;
static pointer_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<void*> > {
static stl_ptr_vector_identity identity;
static stl_ptr_vector_identity *get() { return &identity; }
};
template<> struct identity_traits<std::vector<bool> > {
static stl_bit_vector_identity identity;
static stl_bit_vector_identity *get() { return &identity; }
};
#undef NUMBER_IDENTITY_TRAITS
// Container declarations
template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > {
static primitive_identity *get();
};
template<class T> struct identity_traits<T *> {
static pointer_identity *get();
};
template<class T, int sz> struct identity_traits<T [sz]> {
static container_identity *get();
};
template<class T> struct identity_traits<std::vector<T> > {
static container_identity *get();
};
template<class T> struct identity_traits<std::vector<T*> > {
static stl_ptr_vector_identity *get();
};
template<class T> struct identity_traits<std::deque<T> > {
static container_identity *get();
};
template<class T> struct identity_traits<BitArray<T> > {
static bit_container_identity *get();
};
template<class T> struct identity_traits<DfArray<T> > {
static container_identity *get();
};
// Container definitions
template<class Enum, class FT>
primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
return identity_traits<FT>::get();
}
template<class T>
pointer_identity *identity_traits<T *>::get() {
static pointer_identity identity(identity_traits<T>::get());
return &identity;
}
template<class T, int sz>
container_identity *identity_traits<T [sz]>::get() {
static buffer_container_identity identity(sz, identity_traits<T>::get());
return &identity;
}
template<class T>
container_identity *identity_traits<std::vector<T> >::get() {
typedef std::vector<T> container;
static stl_container_identity<container> identity("vector", identity_traits<T>::get());
return &identity;
}
template<class T>
stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
static stl_ptr_vector_identity identity(identity_traits<T>::get());
return &identity;
}
template<class T>
container_identity *identity_traits<std::deque<T> >::get() {
typedef std::deque<T> container;
static stl_container_identity<container> identity("deque", identity_traits<T>::get());
return &identity;
}
template<class T>
bit_container_identity *identity_traits<BitArray<T> >::get() {
static type_identity *eid = identity_traits<T>::get();
static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL;
static bit_array_identity identity(reid);
return &identity;
}
template<class T>
container_identity *identity_traits<DfArray<T> >::get() {
typedef DfArray<T> container;
static stl_container_identity<container> identity("DfArray", identity_traits<T>::get());
return &identity;
}
}

@ -4,6 +4,10 @@ option optimize_for = LITE_RUNTIME;
import "Basic.proto";
// RPC GetVersion : EmptyMessage -> StringMessage
// RPC GetDFVersion : EmptyMessage -> StringMessage
// RPC GetWorldInfo : EmptyMessage -> GetWorldInfoOut
message GetWorldInfoOut {
enum Mode {
MODE_DWARF = 1;
@ -27,6 +31,7 @@ message GetWorldInfoOut {
repeated int32 companion_histfig_ids = 10;
};
// RPC ListEnums : EmptyMessage -> ListEnumsOut
message ListEnumsOut {
repeated EnumItemName material_flags = 1;
repeated EnumItemName inorganic_flags = 2;
@ -46,6 +51,7 @@ message ListEnumsOut {
repeated EnumItemName profession = 11;
};
// RPC ListMaterials : ListMaterialsIn -> ListMaterialsOut
message ListMaterialsIn {
optional BasicMaterialInfoMask mask = 1;
@ -62,6 +68,7 @@ message ListMaterialsOut {
repeated BasicMaterialInfo value = 1;
};
// RPC ListUnits : ListUnitsIn -> ListUnitsOut
message ListUnitsIn {
optional BasicUnitInfoMask mask = 1;
@ -81,6 +88,7 @@ message ListUnitsOut {
repeated BasicUnitInfo value = 1;
};
// RPC ListSquads : ListSquadsIn -> ListSquadsOut
message ListSquadsIn {}
message ListSquadsOut {
repeated BasicSquadInfo value = 1;

@ -62,18 +62,22 @@ message StringListMessage {
repeated string value = 1;
}
// RPC BindMethod : CoreBindRequest -> CoreBindReply
message CoreBindRequest {
required string method = 1;
required string input_msg = 2;
required string output_msg = 3;
optional string plugin = 4;
}
message CoreBindReply {
required int32 assigned_id = 1;
}
// RPC RunCommand : CoreRunCommandRequest -> EmptyMessage
message CoreRunCommandRequest {
required string command = 1;
repeated string arguments = 2;
}
// RPC CoreSuspend : EmptyMessage -> IntMessage
// RPC CoreResume : EmptyMessage -> IntMessage

@ -1 +1 @@
Subproject commit 08e1f71e89c1af6b3bef940914ed7f3d8fed89b0
Subproject commit 1a6d5acf09ac4c11da62c5ed11a567e480d9fb59

@ -0,0 +1,178 @@
#pragma once
typedef vector <df::coord> coord_vec;
class Brush
{
public:
virtual ~Brush(){};
virtual coord_vec points(MapExtras::MapCache & mc,DFHack::DFCoord start) = 0;
};
/**
* generic 3D rectangle brush. you can specify the dimensions of
* the rectangle and optionally which tile is its 'center'
*/
class RectangleBrush : public Brush
{
public:
RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1)
{
if(centerx == -1)
cx_ = x/2;
else
cx_ = centerx;
if(centery == -1)
cy_ = y/2;
else
cy_ = centery;
if(centerz == -1)
cz_ = z/2;
else
cz_ = centerz;
x_ = x;
y_ = y;
z_ = z;
};
coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_);
DFHack::DFCoord iter = iterstart;
for(int xi = 0; xi < x_; xi++)
{
for(int yi = 0; yi < y_; yi++)
{
for(int zi = 0; zi < z_; zi++)
{
if(mc.testCoord(iter))
v.push_back(iter);
iter.z++;
}
iter.z = iterstart.z;
iter.y++;
}
iter.y = iterstart.y;
iter.x ++;
}
return v;
};
~RectangleBrush(){};
private:
int x_, y_, z_;
int cx_, cy_, cz_;
};
/**
* stupid block brush, legacy. use when you want to apply something to a whole DF map block.
*/
class BlockBrush : public Brush
{
public:
BlockBrush(){};
~BlockBrush(){};
coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord blockc = start / 16;
DFHack::DFCoord iterc = blockc * 16;
if( !mc.testCoord(start) )
return v;
auto starty = iterc.y;
for(int xi = 0; xi < 16; xi++)
{
for(int yi = 0; yi < 16; yi++)
{
v.push_back(iterc);
iterc.y++;
}
iterc.y = starty;
iterc.x ++;
}
return v;
};
};
/**
* Column from a position through open space tiles
* example: create a column of magma
*/
class ColumnBrush : public Brush
{
public:
ColumnBrush(){};
~ColumnBrush(){};
coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
bool juststarted = true;
while (mc.testCoord(start))
{
df::tiletype tt = mc.tiletypeAt(start);
if(DFHack::LowPassable(tt) || juststarted && DFHack::HighPassable(tt))
{
v.push_back(start);
juststarted = false;
start.z++;
}
else break;
}
return v;
};
};
/**
* Flood-fill water tiles from cursor (for wclean)
* example: remove salt flag from a river
*/
class FloodBrush : public Brush
{
public:
FloodBrush(Core *c){c_ = c;};
~FloodBrush(){};
coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
std::stack<DFCoord> to_flood;
to_flood.push(start);
std::set<DFCoord> seen;
while (!to_flood.empty()) {
DFCoord xy = to_flood.top();
to_flood.pop();
df::tile_designation des = mc.designationAt(xy);
if (seen.find(xy) == seen.end()
&& des.bits.flow_size
&& des.bits.liquid_type == tile_liquid::Water) {
v.push_back(xy);
seen.insert(xy);
maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc);
df::tiletype tt = mc.tiletypeAt(xy);
if (LowPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc);
}
if (HighPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc);
}
}
}
return v;
}
private:
void maybeFlood(DFCoord c, std::stack<DFCoord> &to_flood, MapExtras::MapCache &mc) {
if (mc.testCoord(c)) {
to_flood.push(c);
}
}
Core *c_;
};

@ -65,6 +65,10 @@ OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON)
if (BUILD_SUPPORTED)
DFHACK_PLUGIN(reveal reveal.cpp)
DFHACK_PLUGIN(probe probe.cpp)
# this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...)
DFHACK_PLUGIN(cursecheck cursecheck.cpp)
# automatically assign labors to dwarves!
DFHACK_PLUGIN(autolabor autolabor.cpp)
DFHACK_PLUGIN(drybuckets drybuckets.cpp)
DFHACK_PLUGIN(getplants getplants.cpp)
DFHACK_PLUGIN(plants plants.cpp)
@ -109,14 +113,3 @@ if(BUILD_SKELETON)
add_subdirectory(skeleton)
endif()
# this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...)
OPTION(BUILD_CURSECHECK "Build the cursecheck plugin." ON)
if(BUILD_CURSECHECK)
add_subdirectory(cursecheck)
endif()
# alternative version of liquids which can be used non-interactively after configuring it
OPTION(BUILD_LIQUIDSGO "Build the liquidsgo plugin." ON)
if(BUILD_LIQUIDSGO)
add_subdirectory(liquidsgo)
endif()

@ -476,28 +476,25 @@ function getSelectedUnit()
end
end
function getxyz() -- this will return pointers x,y and z coordinates.
local off=VersionInfo.getGroup("Position"):getAddress("cursor_xyz") -- lets find where in memory its being held
-- now lets read them (they are double words (or unsigned longs or 4 bits each) and go in sucesion
local x=engine.peekd(off)
local y=engine.peekd(off+4) --next is 4 from start
local z=engine.peekd(off+8) --next is 8 from start
--print("Pointer @:"..x..","..y..","..z)
local x=df.cursor.x
local y=df.cursor.y
local z=df.cursor.z
return x,y,z -- return the coords
end
function GetCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=engine.peek(VersionInfo.getGroup("Creatures"):getAddress("vector"),ptr_vector) -- load all creatures
for i = 0, vector:size()-1 do -- look into all creatures offsets
local curoff=vector:getval(i) -- get i-th creatures offset
local cx=engine.peek(curoff,ptr_Creature.x) --get its coordinates
local cy=engine.peek(curoff,ptr_Creature.y)
local cz=engine.peek(curoff,ptr_Creature.z)
local vector=df.world.units.all -- load all creatures
for i = 0, vector.size-1 do -- look into all creatures offsets
local curpos=vector[i]:deref().pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return i --return index
return vector[i]:deref() --return index
end
end
print("Creature not found!")
return -1
return nil
end
function Allocate(size)

@ -72,7 +72,8 @@ table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"})
table.insert(plugins,{"triggers","a function calling plug (discontinued...)"})
table.insert(plugins,{"migrants","multi race imigrations"})
table.insert(plugins,{"onfunction","run lua on some df function"})--]=]
--]=]
table.insert(plugins,{"onfunction","run lua on some df function"})
table.insert(plugins,{"editor","edit internals of df",EditDF})
table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved})
loadall(plugins)

@ -1,5 +1,5 @@
if WINDOWS then --windows function defintions
onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord"
--[=[onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord"
onfunction.AddFunction(0x275933+offsets.base(),"Die",{creature="edi"}) --on creature death? found by watching dead flag then stepping until new function
onfunction.AddFunction(0x2c1834+offsets.base(),"CreateCreature",{protocreature="eax"}) --arena
onfunction.AddFunction(0x349640+offsets.base(),"AddItem",{item="esp"}) --or esp
@ -7,8 +7,9 @@ if WINDOWS then --windows function defintions
onfunction.AddFunction(0x3d4301+offsets.base(),"Make_Item",{item_type="esp"})
onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}})
onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"})
onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")
onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=]
onfunction.AddFunction(4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx?
else --linux
onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch...
onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same
--[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch...
onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=]
end

@ -264,6 +264,11 @@ function tools.empregnate(unit)
if unit==nil then
unit=getSelectedUnit()
end
if unit==nil then
unit=getCreatureAtPos(getxyz())
end
if unit==nil then
error("Failed to empregnate. Unit not selected/valide")
end

@ -0,0 +1,874 @@
// This is a generic plugin that does nothing useful apart from acting as an example... of a plugin that does nothing :D
// some headers required for a plugin. Nothing special, just the basics.
#include "Core.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <vector>
#include <algorithm>
// DF data structure definition headers
#include "DataDefs.h"
#include <df/ui.h>
#include <df/world.h>
#include <df/unit.h>
#include <df/unit_soul.h>
#include <df/unit_labor.h>
#include <df/unit_skill.h>
#include <df/job.h>
#include <df/building.h>
#include <df/workshop_type.h>
#include <df/unit_misc_trait.h>
using namespace DFHack;
using namespace df::enums;
using df::global::ui;
using df::global::world;
#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
static int enable_autolabor;
// Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters);
// A plugin must be able to return its name and version.
// The name string provided must correspond to the filename - autolabor.plug.so or autolabor.plug.dll in this case
DFHACK_PLUGIN("autolabor");
enum labor_mode {
FIXED,
AUTOMATIC,
EVERYONE,
HAULERS,
};
enum dwarf_state {
// Ready for a new task
IDLE,
// Busy with a useful task
BUSY,
// In the military, can't work
MILITARY,
// Child or noble, can't work
CHILD,
// Doing something that precludes working, may be busy for a while
OTHER
};
static const dwarf_state dwarf_states[] = {
BUSY /* CarveFortification */,
BUSY /* DetailWall */,
BUSY /* DetailFloor */,
BUSY /* Dig */,
BUSY /* CarveUpwardStaircase */,
BUSY /* CarveDownwardStaircase */,
BUSY /* CarveUpDownStaircase */,
BUSY /* CarveRamp */,
BUSY /* DigChannel */,
BUSY /* FellTree */,
BUSY /* GatherPlants */,
BUSY /* RemoveConstruction */,
BUSY /* CollectWebs */,
BUSY /* BringItemToDepot */,
BUSY /* BringItemToShop */,
OTHER /* Eat */,
OTHER /* GetProvisions */,
OTHER /* Drink */,
OTHER /* Drink2 */,
OTHER /* FillWaterskin */,
OTHER /* FillWaterskin2 */,
OTHER /* Sleep */,
BUSY /* CollectSand */,
BUSY /* Fish */,
BUSY /* Hunt */,
OTHER /* HuntVermin */,
BUSY /* Kidnap */,
BUSY /* BeatCriminal */,
BUSY /* StartingFistFight */,
BUSY /* CollectTaxes */,
BUSY /* GuardTaxCollector */,
BUSY /* CatchLiveLandAnimal */,
BUSY /* CatchLiveFish */,
BUSY /* ReturnKill */,
BUSY /* CheckChest */,
BUSY /* StoreOwnedItem */,
BUSY /* PlaceItemInTomb */,
BUSY /* StoreItemInStockpile */,
BUSY /* StoreItemInBag */,
BUSY /* StoreItemInHospital */,
BUSY /* StoreItemInChest */,
BUSY /* StoreItemInCabinet */,
BUSY /* StoreWeapon */,
BUSY /* StoreArmor */,
BUSY /* StoreItemInBarrel */,
BUSY /* StoreItemInBin */,
BUSY /* SeekArtifact */,
BUSY /* SeekInfant */,
OTHER /* AttendParty */,
OTHER /* GoShopping */,
OTHER /* GoShopping2 */,
BUSY /* Clean */,
OTHER /* Rest */,
BUSY /* PickupEquipment */,
BUSY /* DumpItem */,
OTHER /* StrangeMoodCrafter */,
OTHER /* StrangeMoodJeweller */,
OTHER /* StrangeMoodForge */,
OTHER /* StrangeMoodMagmaForge */,
OTHER /* StrangeMoodBrooding */,
OTHER /* StrangeMoodFell */,
OTHER /* StrangeMoodCarpenter */,
OTHER /* StrangeMoodMason */,
OTHER /* StrangeMoodBowyer */,
OTHER /* StrangeMoodTanner */,
OTHER /* StrangeMoodWeaver */,
OTHER /* StrangeMoodGlassmaker */,
OTHER /* StrangeMoodMechanics */,
BUSY /* ConstructBuilding */,
BUSY /* ConstructDoor */,
BUSY /* ConstructFloodgate */,
BUSY /* ConstructBed */,
BUSY /* ConstructThrone */,
BUSY /* ConstructCoffin */,
BUSY /* ConstructTable */,
BUSY /* ConstructChest */,
BUSY /* ConstructBin */,
BUSY /* ConstructArmorStand */,
BUSY /* ConstructWeaponRack */,
BUSY /* ConstructCabinet */,
BUSY /* ConstructStatue */,
BUSY /* ConstructBlocks */,
BUSY /* MakeRawGlass */,
BUSY /* MakeCrafts */,
BUSY /* MintCoins */,
BUSY /* CutGems */,
BUSY /* CutGlass */,
BUSY /* EncrustWithGems */,
BUSY /* EncrustWithGlass */,
BUSY /* DestroyBuilding */,
BUSY /* SmeltOre */,
BUSY /* MeltMetalObject */,
BUSY /* ExtractMetalStrands */,
BUSY /* PlantSeeds */,
BUSY /* HarvestPlants */,
BUSY /* TrainHuntingAnimal */,
BUSY /* TrainWarAnimal */,
BUSY /* MakeWeapon */,
BUSY /* ForgeAnvil */,
BUSY /* ConstructCatapultParts */,
BUSY /* ConstructBallistaParts */,
BUSY /* MakeArmor */,
BUSY /* MakeHelm */,
BUSY /* MakePants */,
BUSY /* StudWith */,
BUSY /* ButcherAnimal */,
BUSY /* PrepareRawFish */,
BUSY /* MillPlants */,
BUSY /* BaitTrap */,
BUSY /* MilkCreature */,
BUSY /* MakeCheese */,
BUSY /* ProcessPlants */,
BUSY /* ProcessPlantsBag */,
BUSY /* ProcessPlantsVial */,
BUSY /* ProcessPlantsBarrel */,
BUSY /* PrepareMeal */,
BUSY /* WeaveCloth */,
BUSY /* MakeGloves */,
BUSY /* MakeShoes */,
BUSY /* MakeShield */,
BUSY /* MakeCage */,
BUSY /* MakeChain */,
BUSY /* MakeFlask */,
BUSY /* MakeGoblet */,
BUSY /* MakeInstrument */,
BUSY /* MakeToy */,
BUSY /* MakeAnimalTrap */,
BUSY /* MakeBarrel */,
BUSY /* MakeBucket */,
BUSY /* MakeWindow */,
BUSY /* MakeTotem */,
BUSY /* MakeAmmo */,
BUSY /* DecorateWith */,
BUSY /* MakeBackpack */,
BUSY /* MakeQuiver */,
BUSY /* MakeBallistaArrowHead */,
BUSY /* AssembleSiegeAmmo */,
BUSY /* LoadCatapult */,
BUSY /* LoadBallista */,
BUSY /* FireCatapult */,
BUSY /* FireBallista */,
BUSY /* ConstructMechanisms */,
BUSY /* MakeTrapComponent */,
BUSY /* LoadCageTrap */,
BUSY /* LoadStoneTrap */,
BUSY /* LoadWeaponTrap */,
BUSY /* CleanTrap */,
BUSY /* CastSpell */,
BUSY /* LinkBuildingToTrigger */,
BUSY /* PullLever */,
BUSY /* BrewDrink */,
BUSY /* ExtractFromPlants */,
BUSY /* ExtractFromRawFish */,
BUSY /* ExtractFromLandAnimal */,
BUSY /* TameVermin */,
BUSY /* TameAnimal */,
BUSY /* ChainAnimal */,
BUSY /* UnchainAnimal */,
BUSY /* UnchainPet */,
BUSY /* ReleaseLargeCreature */,
BUSY /* ReleasePet */,
BUSY /* ReleaseSmallCreature */,
BUSY /* HandleSmallCreature */,
BUSY /* HandleLargeCreature */,
BUSY /* CageLargeCreature */,
BUSY /* CageSmallCreature */,
BUSY /* RecoverWounded */,
BUSY /* DiagnosePatient */,
BUSY /* ImmobilizeBreak */,
BUSY /* DressWound */,
BUSY /* CleanPatient */,
BUSY /* Surgery */,
BUSY /* Suture */,
BUSY /* SetBone */,
BUSY /* PlaceInTraction */,
BUSY /* DrainAquarium */,
BUSY /* FillAquarium */,
BUSY /* FillPond */,
BUSY /* GiveWater */,
BUSY /* GiveFood */,
BUSY /* GiveWater2 */,
BUSY /* GiveFood2 */,
BUSY /* RecoverPet */,
BUSY /* PitLargeAnimal */,
BUSY /* PitSmallAnimal */,
BUSY /* SlaughterAnimal */,
BUSY /* MakeCharcoal */,
BUSY /* MakeAsh */,
BUSY /* MakeLye */,
BUSY /* MakePotashFromLye */,
BUSY /* FertilizeField */,
BUSY /* MakePotashFromAsh */,
BUSY /* DyeThread */,
BUSY /* DyeCloth */,
BUSY /* SewImage */,
BUSY /* MakePipeSection */,
BUSY /* OperatePump */,
OTHER /* ManageWorkOrders */,
OTHER /* UpdateStockpileRecords */,
OTHER /* TradeAtDepot */,
BUSY /* ConstructHatchCover */,
BUSY /* ConstructGrate */,
BUSY /* RemoveStairs */,
BUSY /* ConstructQuern */,
BUSY /* ConstructMillstone */,
BUSY /* ConstructSplint */,
BUSY /* ConstructCrutch */,
BUSY /* ConstructTractionBench */,
BUSY /* CleanSelf */,
BUSY /* BringCrutch */,
BUSY /* ApplyCast */,
BUSY /* CustomReaction */,
BUSY /* ConstructSlab */,
BUSY /* EngraveSlab */,
BUSY /* ShearCreature */,
BUSY /* SpinThread */,
BUSY /* PenLargeAnimal */,
BUSY /* PenSmallAnimal */,
BUSY /* MakeTool */,
BUSY /* CollectClay */,
BUSY /* InstallColonyInHive */,
BUSY /* CollectHiveProducts */,
OTHER /* CauseTrouble */,
OTHER /* DrinkBlood */,
OTHER /* ReportCrime */,
OTHER /* ExecuteCriminal */
};
struct labor_info
{
labor_mode mode;
bool is_exclusive;
int minimum_dwarfs;
};
static const struct labor_info labor_infos[] = {
/* MINE */ {AUTOMATIC, true, 2},
/* HAUL_STONE */ {HAULERS, false, 1},
/* HAUL_WOOD */ {HAULERS, false, 1},
/* HAUL_BODY */ {HAULERS, false, 1},
/* HAUL_FOOD */ {HAULERS, false, 1},
/* HAUL_REFUSE */ {HAULERS, false, 1},
/* HAUL_ITEM */ {HAULERS, false, 1},
/* HAUL_FURNITURE */ {HAULERS, false, 1},
/* HAUL_ANIMAL */ {HAULERS, false, 1},
/* CLEAN */ {HAULERS, false, 1},
/* CUTWOOD */ {AUTOMATIC, true, 1},
/* CARPENTER */ {AUTOMATIC, false, 1},
/* DETAIL */ {AUTOMATIC, false, 1},
/* MASON */ {AUTOMATIC, false, 1},
/* ARCHITECT */ {AUTOMATIC, false, 1},
/* ANIMALTRAIN */ {AUTOMATIC, false, 1},
/* ANIMALCARE */ {AUTOMATIC, false, 1},
/* DIAGNOSE */ {AUTOMATIC, false, 1},
/* SURGERY */ {AUTOMATIC, false, 1},
/* BONE_SETTING */ {AUTOMATIC, false, 1},
/* SUTURING */ {AUTOMATIC, false, 1},
/* DRESSING_WOUNDS */ {AUTOMATIC, false, 1},
/* FEED_WATER_CIVILIANS */ {EVERYONE, false, 1},
/* RECOVER_WOUNDED */ {HAULERS, false, 1},
/* BUTCHER */ {AUTOMATIC, false, 1},
/* TRAPPER */ {AUTOMATIC, false, 1},
/* DISSECT_VERMIN */ {AUTOMATIC, false, 1},
/* LEATHER */ {AUTOMATIC, false, 1},
/* TANNER */ {AUTOMATIC, false, 1},
/* BREWER */ {AUTOMATIC, false, 1},
/* ALCHEMIST */ {AUTOMATIC, false, 1},
/* SOAP_MAKER */ {AUTOMATIC, false, 1},
/* WEAVER */ {AUTOMATIC, false, 1},
/* CLOTHESMAKER */ {AUTOMATIC, false, 1},
/* MILLER */ {AUTOMATIC, false, 1},
/* PROCESS_PLANT */ {AUTOMATIC, false, 1},
/* MAKE_CHEESE */ {AUTOMATIC, false, 1},
/* MILK */ {AUTOMATIC, false, 1},
/* COOK */ {AUTOMATIC, false, 1},
/* PLANT */ {AUTOMATIC, false, 1},
/* HERBALIST */ {AUTOMATIC, false, 1},
/* FISH */ {FIXED, false, 1},
/* CLEAN_FISH */ {AUTOMATIC, false, 1},
/* DISSECT_FISH */ {AUTOMATIC, false, 1},
/* HUNT */ {FIXED, true, 1},
/* SMELT */ {AUTOMATIC, false, 1},
/* FORGE_WEAPON */ {AUTOMATIC, false, 1},
/* FORGE_ARMOR */ {AUTOMATIC, false, 1},
/* FORGE_FURNITURE */ {AUTOMATIC, false, 1},
/* METAL_CRAFT */ {AUTOMATIC, false, 1},
/* CUT_GEM */ {AUTOMATIC, false, 1},
/* ENCRUST_GEM */ {AUTOMATIC, false, 1},
/* WOOD_CRAFT */ {AUTOMATIC, false, 1},
/* STONE_CRAFT */ {AUTOMATIC, false, 1},
/* BONE_CARVE */ {AUTOMATIC, false, 1},
/* GLASSMAKER */ {AUTOMATIC, false, 1},
/* EXTRACT_STRAND */ {AUTOMATIC, false, 1},
/* SIEGECRAFT */ {AUTOMATIC, false, 1},
/* SIEGEOPERATE */ {AUTOMATIC, false, 1},
/* BOWYER */ {AUTOMATIC, false, 1},
/* MECHANIC */ {AUTOMATIC, false, 1},
/* POTASH_MAKING */ {AUTOMATIC, false, 1},
/* LYE_MAKING */ {AUTOMATIC, false, 1},
/* DYER */ {AUTOMATIC, false, 1},
/* BURN_WOOD */ {AUTOMATIC, false, 1},
/* OPERATE_PUMP */ {AUTOMATIC, false, 1},
/* SHEARER */ {AUTOMATIC, false, 1},
/* SPINNER */ {AUTOMATIC, false, 1},
/* POTTERY */ {AUTOMATIC, false, 1},
/* GLAZING */ {AUTOMATIC, false, 1},
/* PRESSING */ {AUTOMATIC, false, 1},
/* BEEKEEPING */ {AUTOMATIC, false, 1},
/* WAX_WORKING */ {AUTOMATIC, false, 1},
};
static const df::job_skill noble_skills[] = {
df::enums::job_skill::APPRAISAL,
df::enums::job_skill::ORGANIZATION,
df::enums::job_skill::RECORD_KEEPING,
};
struct dwarf_info
{
int highest_skill;
int total_skill;
bool is_best_noble;
int mastery_penalty;
int assigned_jobs;
dwarf_state state;
bool has_exclusive_labor;
};
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
assert(ARRAY_COUNT(labor_infos) > ENUM_LAST_ITEM(unit_labor));
// Fill the command list with your commands.
commands.clear();
commands.push_back(PluginCommand(
"autolabor", "Automatically manage dwarf labors.",
autolabor, false, /* true means that the command can't be used from non-interactive user interface */
// Extended help string. Used by CR_WRONG_USAGE and the help command:
" autolabor enable\n"
" autolabor disable\n"
" Enables or disables the plugin.\n"
"Function:\n"
" When enabled, autolabor periodically checks your dwarves and enables or\n"
" disables labors. It tries to keep as many dwarves as possible busy but\n"
" also tries to have dwarves specialize in specific skills.\n"
" Warning: autolabor will override any manual changes you make to labors\n"
" while it is enabled.\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
static int step_count = 0;
if (!enable_autolabor)
return CR_OK;
if (++step_count < 60)
return CR_OK;
step_count = 0;
uint32_t race = ui->race_id;
uint32_t civ = ui->civ_id;
std::vector<df::unit *> dwarfs;
bool has_butchers = false;
bool has_fishery = false;
for (int i = 0; i < world->buildings.all.size(); ++i)
{
df::building *build = world->buildings.all[i];
auto type = build->getType();
if (df::enums::building_type::Workshop == type)
{
auto subType = build->getSubtype();
if (df::enums::workshop_type::Butchers == subType)
has_butchers = true;
if (df::enums::workshop_type::Fishery == subType)
has_fishery = true;
}
}
for (int i = 0; i < world->units.all.size(); ++i)
{
df::unit* cre = world->units.all[i];
if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant && !cre->flags1.bits.dead) {
dwarfs.push_back(cre);
}
}
int n_dwarfs = dwarfs.size();
if (n_dwarfs == 0)
return CR_OK;
std::vector<dwarf_info> dwarf_info(n_dwarfs);
std::vector<int> best_noble(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_skill(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_experience(ARRAY_COUNT(noble_skills));
// Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
assert(dwarfs[dwarf]->status.souls.size() > 0);
for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++)
{
df::job_skill skill = (*s)->id;
df::job_skill_class skill_class = ENUM_ATTR(job_skill, type, skill);
int skill_level = (*s)->rating;
int skill_experience = (*s)->experience;
// Track the dwarfs with the best Appraisal, Organization, and Record Keeping skills.
// They are likely to have appointed noble positions, so should be kept free where possible.
int noble_skill_id = -1;
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
if (skill == noble_skills[i])
noble_skill_id = i;
}
if (noble_skill_id >= 0)
{
assert(noble_skill_id < ARRAY_COUNT(noble_skills));
if (highest_noble_skill[noble_skill_id] < skill_level ||
(highest_noble_skill[noble_skill_id] == skill_level &&
highest_noble_experience[noble_skill_id] < skill_experience))
{
highest_noble_skill[noble_skill_id] = skill_level;
highest_noble_experience[noble_skill_id] = skill_experience;
best_noble[noble_skill_id] = dwarf;
}
}
// Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.)
if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical)
continue;
if (dwarf_info[dwarf].highest_skill < skill_level)
dwarf_info[dwarf].highest_skill = skill_level;
dwarf_info[dwarf].total_skill += skill_level;
}
}
// Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.)
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
assert(best_noble[i] >= 0);
assert(best_noble[i] < n_dwarfs);
dwarf_info[best_noble[i]].is_best_noble = true;
}
// Calculate a base penalty for using each dwarf for a task he isn't good at.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill;
dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill;
if (dwarf_info[dwarf].is_best_noble)
dwarf_info[dwarf].mastery_penalty -= 250;
for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++)
{
if (labor == df::enums::unit_labor::NONE)
continue;
assert(labor >= 0);
assert(labor < ARRAY_COUNT(labor_infos));
if (labor_infos[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor])
dwarf_info[dwarf].mastery_penalty -= 100;
}
}
// Find the activity state for each dwarf. It's important to get this right - a dwarf who we think is IDLE but
// can't work will gum everything up. In the future I might add code to auto-detect slacker dwarves.
std::vector<int> state_count(5);
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
bool is_on_break = false;
for (auto p = dwarfs[dwarf]->status.misc_traits.begin(); p < dwarfs[dwarf]->status.misc_traits.end(); p++)
{
// 7 / 0x7 = Newly arrived migrant, will not work yet
// 17 / 0x11 = On break
if ((*p)->id == 0x07 || (*p)->id == 0x11)
is_on_break = true;
}
if (dwarfs[dwarf]->profession == df::enums::profession::BABY ||
dwarfs[dwarf]->profession == df::enums::profession::CHILD ||
dwarfs[dwarf]->profession == df::enums::profession::DRUNK)
{
dwarf_info[dwarf].state = CHILD;
}
else if (dwarfs[dwarf]->job.current_job == NULL)
{
if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession))
dwarf_info[dwarf].state = MILITARY;
else if (is_on_break)
dwarf_info[dwarf].state = OTHER;
else if (dwarfs[dwarf]->meetings.size() > 0)
dwarf_info[dwarf].state = OTHER;
else
dwarf_info[dwarf].state = IDLE;
}
else
{
int job = dwarfs[dwarf]->job.current_job->job_type;
assert(job >= 0);
assert(job < ARRAY_COUNT(dwarf_states));
dwarf_info[dwarf].state = dwarf_states[job];
}
state_count[dwarf_info[dwarf].state]++;
}
// Generate labor -> skill mapping
df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1];
for (int i = 0; i <= ENUM_LAST_ITEM(unit_labor); i++)
labor_to_skill[i] = df::enums::job_skill::NONE;
FOR_ENUM_ITEMS(job_skill, skill)
{
int labor = ENUM_ATTR(job_skill, labor, skill);
if (labor != df::enums::unit_labor::NONE)
{
assert(labor >= 0);
assert(labor < ARRAY_COUNT(labor_to_skill));
labor_to_skill[labor] = skill;
}
}
std::vector<df::unit_labor> labors;
FOR_ENUM_ITEMS(unit_labor, labor)
{
if (labor == df::enums::unit_labor::NONE)
continue;
assert(labor >= 0);
assert(labor < ARRAY_COUNT(labor_infos));
labors.push_back(labor);
}
std::sort(labors.begin(), labors.end(), [] (int i, int j) { return labor_infos[i].mode < labor_infos[j].mode; });
// Handle all skills except those marked HAULERS
for (auto lp = labors.begin(); lp != labors.end(); ++lp)
{
auto labor = *lp;
assert(labor >= 0);
assert(labor < ARRAY_COUNT(labor_infos));
df::job_skill skill = labor_to_skill[labor];
if (labor_infos[labor].mode == HAULERS)
continue;
int best_dwarf = 0;
int best_value = -10000;
std::vector<int> values(n_dwarfs);
std::vector<int> candidates;
std::vector<int> backup_candidates;
std::map<int, int> dwarf_skill;
auto mode = labor_infos[labor].mode;
if (AUTOMATIC == mode && state_count[IDLE] == 0)
mode = FIXED;
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
if (dwarf_info[dwarf].state != IDLE && dwarf_info[dwarf].state != BUSY && mode != EVERYONE)
continue;
if (labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor)
continue;
int value = dwarf_info[dwarf].mastery_penalty - dwarf_info[dwarf].assigned_jobs;
if (skill != df::enums::job_skill::NONE)
{
int skill_level = 0;
int skill_experience = 0;
for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++)
{
if ((*s)->id == skill)
{
skill_level = (*s)->rating;
skill_experience = (*s)->experience;
break;
}
}
dwarf_skill[dwarf] = skill_level;
value += skill_level * 100;
value += skill_experience / 20;
if (skill_level > 0 || skill_experience > 0)
value += 200;
if (skill_level >= 15)
value += 1000 * (skill_level - 14);
}
else
{
dwarf_skill[dwarf] = 0;
}
if (dwarfs[dwarf]->status.labors[labor])
{
value += 5;
if (labor_infos[labor].is_exclusive)
value += 350;
}
values[dwarf] = value;
if (mode == AUTOMATIC && dwarf_info[dwarf].state != IDLE)
backup_candidates.push_back(dwarf);
else
candidates.push_back(dwarf);
}
if (candidates.size() == 0)
{
candidates = backup_candidates;
mode = FIXED;
}
if (labor_infos[labor].mode != EVERYONE)
std::sort(candidates.begin(), candidates.end(), [&values] (int i, int j) { return values[i] > values[j]; });
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
bool allow_labor = false;
if (dwarf_info[dwarf].state == BUSY &&
mode == AUTOMATIC &&
(labor_infos[labor].is_exclusive || dwarf_skill[dwarf] > 0))
{
allow_labor = true;
}
if (dwarf_info[dwarf].state == OTHER &&
mode == AUTOMATIC &&
dwarf_skill[dwarf] > 0 &&
!dwarf_info[dwarf].is_best_noble)
{
allow_labor = true;
}
if (dwarfs[dwarf]->status.labors[labor] &&
allow_labor &&
!(labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor))
{
if (labor_infos[labor].is_exclusive)
dwarf_info[dwarf].has_exclusive_labor = true;
dwarf_info[dwarf].assigned_jobs++;
}
else
{
dwarfs[dwarf]->status.labors[labor] = false;
}
}
int minimum_dwarfs = labor_infos[labor].minimum_dwarfs;
if (labor_infos[labor].mode == EVERYONE)
minimum_dwarfs = n_dwarfs;
// Special - don't assign hunt without a butchers, or fish without a fishery
if (df::enums::unit_labor::HUNT == labor && !has_butchers)
minimum_dwarfs = 0;
if (df::enums::unit_labor::FISH == labor && !has_fishery)
minimum_dwarfs = 0;
for (int i = 0; i < candidates.size() && i < minimum_dwarfs; i++)
{
int dwarf = candidates[i];
assert(dwarf >= 0);
assert(dwarf < n_dwarfs);
if (!dwarfs[dwarf]->status.labors[labor])
dwarf_info[dwarf].assigned_jobs++;
dwarfs[dwarf]->status.labors[labor] = true;
if (labor_infos[labor].is_exclusive)
dwarf_info[dwarf].has_exclusive_labor = true;
}
}
// Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps
// make sure that hauling jobs are handled quickly rather than building up.
int num_haulers = state_count[IDLE] + state_count[BUSY] / 3;
if (num_haulers < 1)
num_haulers = 1;
std::vector<int> hauler_ids;
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY)
hauler_ids.push_back(dwarf);
}
// Idle dwarves come first, then we sort from least-skilled to most-skilled.
std::sort(hauler_ids.begin(), hauler_ids.end(), [&dwarf_info] (int i, int j) -> bool
{
if (dwarf_info[i].state == IDLE && dwarf_info[j].state != IDLE)
return true;
if (dwarf_info[i].state != IDLE && dwarf_info[j].state == IDLE)
return false;
return dwarf_info[i].mastery_penalty > dwarf_info[j].mastery_penalty;
});
FOR_ENUM_ITEMS(unit_labor, labor)
{
if (labor == df::enums::unit_labor::NONE)
continue;
assert(labor >= 0);
assert(labor < ARRAY_COUNT(labor_infos));
if (labor_infos[labor].mode != HAULERS)
continue;
for (int i = 0; i < num_haulers; i++)
{
assert(i < hauler_ids.size());
int dwarf = hauler_ids[i];
assert(dwarf >= 0);
assert(dwarf < n_dwarfs);
dwarfs[dwarf]->status.labors[labor] = true;
dwarf_info[dwarf].assigned_jobs++;
}
for (int i = num_haulers; i < hauler_ids.size(); i++)
{
assert(i < hauler_ids.size());
int dwarf = hauler_ids[i];
assert(dwarf >= 0);
assert(dwarf < n_dwarfs);
dwarfs[dwarf]->status.labors[labor] = false;
}
}
return CR_OK;
}
// A command! It sits around and looks pretty. And it's nice and friendly.
command_result autolabor (color_ostream &out, std::vector <std::string> & parameters)
{
if (parameters.size() == 1 && (parameters[0] == "0" || parameters[0] == "1"))
{
if (parameters[0] == "0")
enable_autolabor = 0;
else
enable_autolabor = 1;
out.print("autolabor %sactivated.\n", (enable_autolabor ? "" : "de"));
}
else
{
out.print("Automatically assigns labors to dwarves.\n"
"Activate with 'autolabor 1', deactivate with 'autolabor 0'.\n"
"Current state: %d.\n", enable_autolabor);
}
return CR_OK;
}

@ -1,33 +0,0 @@
PROJECT (cursecheck)
# A list of source files
SET(PROJECT_SRCS
cursecheck.cpp
)
# A list of headers
SET(PROJECT_HDRS
cursecheck.h
)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
# option to use a thread for no particular reason
#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON)
#linux
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
)
# windows
ELSE(UNIX)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
$(NOINHERIT)
)
ENDIF(UNIX)
# this makes sure all the stuff is put in proper places and linked to dfhack
DFHACK_PLUGIN(cursecheck ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})

@ -1,3 +1,25 @@
// plugin liquids
//
// This is a rewrite of the liquids module which can also be used non-interactively (hotkey).
// First the user sets the mode and other parameters with the interactive command liqiudsgo
// just like in the original liquids module.
// They are stored in statics to allow being used after the interactive session was closed.
// After defining an action the non-interactive command liquids-here can be used to call the
// execute method without the necessity to go back to the console. This allows convenient painting
// of liquids and obsidian using the ingame cursor and a hotkey.
//
// Commands:
// liquids - basically the original liquids with the map changing stuff moved to an execute method
// liquids-here - runs the execute method with the last settings from liquids
// (intended to be mapped to a hotkey)
// Options:
// ?, help - print some help
//
// TODO:
// - maybe allow all parameters be passed as command line options? tedious parsing but might be useful
// - grab the code from digcircle to get a circle brush - could be nice when painting with obsidian
// - maybe store the last parameters in a file to make them persistent after dfhack is closed?
#include <iostream>
#include <vector>
#include <stack>
@ -19,14 +41,16 @@ using std::set;
#include "modules/Gui.h"
#include "TileTypes.h"
#include "modules/MapCache.h"
#include "Brushes.h"
using namespace MapExtras;
using namespace DFHack;
using namespace df::enums;
typedef vector <df::coord> coord_vec;
CommandHistory liquids_hist;
command_result df_liquids (color_ostream &out, vector <string> & parameters);
command_result df_liquids_here (color_ostream &out, vector <string> & parameters);
command_result df_liquids_execute (color_ostream &out);
DFHACK_PLUGIN("liquids");
@ -34,7 +58,14 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
{
liquids_hist.load("liquids.history");
commands.clear();
commands.push_back(PluginCommand("liquids", "Place magma, water or obsidian.", df_liquids, true));
commands.push_back(PluginCommand(
"liquids", "Place magma, water or obsidian.",
df_liquids, true)); // interactive, needs console for prompt
commands.push_back(PluginCommand(
"liquids-here", "Use settings from liquids at cursor position.",
df_liquids_here, Gui::cursor_hotkey, // non-interactive, needs ingame cursor
" Identical to pressing enter in liquids, intended for use as keybinding.\n"
" Can (but doesn't need to) be called while liquids is running in the console."));
return CR_OK;
}
@ -44,196 +75,29 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
class Brush
{
public:
virtual ~Brush(){};
virtual coord_vec points(MapCache & mc,DFHack::DFCoord start) = 0;
};
/**
* generic 3D rectangle brush. you can specify the dimensions of
* the rectangle and optionally which tile is its 'center'
*/
class RectangleBrush : public Brush
{
public:
RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1)
{
if(centerx == -1)
cx_ = x/2;
else
cx_ = centerx;
if(centery == -1)
cy_ = y/2;
else
cy_ = centery;
if(centerz == -1)
cz_ = z/2;
else
cz_ = centerz;
x_ = x;
y_ = y;
z_ = z;
};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_);
DFHack::DFCoord iter = iterstart;
for(int xi = 0; xi < x_; xi++)
{
for(int yi = 0; yi < y_; yi++)
{
for(int zi = 0; zi < z_; zi++)
{
if(mc.testCoord(iter))
v.push_back(iter);
iter.z++;
}
iter.z = iterstart.z;
iter.y++;
}
iter.y = iterstart.y;
iter.x ++;
}
return v;
};
~RectangleBrush(){};
private:
int x_, y_, z_;
int cx_, cy_, cz_;
};
/**
* stupid block brush, legacy. use when you want to apply something to a whole DF map block.
*/
class BlockBrush : public Brush
{
public:
BlockBrush(){};
~BlockBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord blockc = start / 16;
DFHack::DFCoord iterc = blockc * 16;
if( !mc.testCoord(start) )
return v;
auto starty = iterc.y;
for(int xi = 0; xi < 16; xi++)
{
for(int yi = 0; yi < 16; yi++)
{
v.push_back(iterc);
iterc.y++;
}
iterc.y = starty;
iterc.x ++;
}
return v;
};
};
/**
* Column from a position through open space tiles
* example: create a column of magma
*/
class ColumnBrush : public Brush
{
public:
ColumnBrush(){};
~ColumnBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
bool juststarted = true;
while (mc.testCoord(start))
{
df::tiletype tt = mc.tiletypeAt(start);
if(DFHack::LowPassable(tt) || juststarted && DFHack::HighPassable(tt))
{
v.push_back(start);
juststarted = false;
start.z++;
}
else break;
}
return v;
};
};
/**
* Flood-fill water tiles from cursor (for wclean)
* example: remove salt flag from a river
*/
class FloodBrush : public Brush
{
public:
FloodBrush(Core *c){c_ = c;};
~FloodBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
std::stack<DFCoord> to_flood;
to_flood.push(start);
std::set<DFCoord> seen;
while (!to_flood.empty()) {
DFCoord xy = to_flood.top();
to_flood.pop();
df::tile_designation des = mc.designationAt(xy);
if (seen.find(xy) == seen.end()
&& des.bits.flow_size
&& des.bits.liquid_type == tile_liquid::Water) {
v.push_back(xy);
seen.insert(xy);
maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc);
df::tiletype tt = mc.tiletypeAt(xy);
if (LowPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc);
}
if (HighPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc);
}
}
}
return v;
}
private:
void maybeFlood(DFCoord c, std::stack<DFCoord> &to_flood, MapCache &mc) {
if (mc.testCoord(c)) {
to_flood.push(c);
}
}
Core *c_;
};
// static stuff to be remembered between sessions
static string brushname = "point";
static string mode="magma";
static string flowmode="f+";
static string setmode ="s.";
static unsigned int amount = 7;
static int width = 1, height = 1, z_levels = 1;
command_result df_liquids (color_ostream &out_, vector <string> & parameters)
{
int32_t x,y,z;
assert(out_.is_console());
if(!out_.is_console())
return CR_FAILURE;
Console &out = static_cast<Console&>(out_);
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out.print("This tool allows placing magma, water and other similar things.\n"
"It is interactive and further help is available when you run it.\n"
);
out.print( "This tool allows placing magma, water and other similar things.\n"
"It is interactive and further help is available when you run it.\n"
"The settings will be remembered until dfhack is closed and you can call\n"
"'liquids-here' (mapped to a hotkey) to paint liquids at the cursor position\n"
"without the need to go back to the dfhack console.\n");
return CR_OK;
}
}
@ -244,23 +108,22 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
return CR_FAILURE;
}
Brush * brush = new RectangleBrush(1,1);
string brushname = "point";
bool end = false;
out << "Welcome to the liquid spawner.\nType 'help' or '?' for a list of available commands, 'q' to quit.\nPress return after a command to confirm." << std::endl;
string mode="magma";
string flowmode="f+";
string setmode ="s.";
unsigned int amount = 7;
int width = 1, height = 1, z_levels = 1;
while(!end)
{
string command = "";
std::stringstream str;
str <<"[" << mode << ":" << brushname << ":" << amount << ":" << flowmode << ":" << setmode << "]#";
str <<"[" << mode << ":" << brushname;
if (brushname == "range")
str << "(w" << width << ":h" << height << ":z" << z_levels << ")";
str << ":" << amount << ":" << flowmode << ":" << setmode << "]#";
if(out.lineedit(str.str(),command,liquids_hist) == -1)
return CR_FAILURE;
if(command=="help" || command == "?")
{
out << "Modes:" << endl
@ -296,6 +159,7 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
<< endl
<< "Usage: point the DF cursor at a tile you want to modify" << endl
<< "and use the commands available :)" << endl;
out << endl << "Settings will be remembered until you quit DF. You can call liquids-here to execute the last configured action. Useful in combination with keybindings." << endl;
}
else if(command == "m")
{
@ -327,9 +191,7 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
}
else if(command == "point" || command == "p")
{
delete brush;
brushname = "point";
brush = new RectangleBrush(1,1);
}
else if(command == "range" || command == "r")
{
@ -354,35 +216,27 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
range_hist.add(command);
z_levels = command == "" ? z_levels : atoi (command.c_str());
if(z_levels < 1) z_levels = 1;
delete brush;
if(width == 1 && height == 1 && z_levels == 1)
{
brushname="point";
brushname = "point";
}
else
{
brushname = "range";
}
brush = new RectangleBrush(width,height,z_levels,0,0,0);
}
else if(command == "block")
{
delete brush;
brushname = "block";
brush = new BlockBrush();
}
else if(command == "column")
{
delete brush;
brushname = "column";
brush = new ColumnBrush();
}
else if(command == "flood")
{
delete brush;
brushname = "flood";
brush = new FloodBrush(&Core::getInstance());
}
else if(command == "flood")
{
brushname = "flood";
}
else if(command == "q")
{
end = true;
@ -430,184 +284,248 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
amount = 7;
else if(command.empty())
{
CoreSuspender suspend;
df_liquids_execute(out);
}
else
{
out << command << " : unknown command." << endl;
}
}
return CR_OK;
}
command_result df_liquids_here (color_ostream &out, vector <string> & parameters)
{
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out << "This command is supposed to be mapped to a hotkey." << endl;
out << "It will use the current/last parameters set in liquids." << endl;
return CR_OK;
}
}
do
out.print("Run liquids-here with these parameters: ");
out << "[" << mode << ":" << brushname;
if (brushname == "range")
out << "(w" << width << ":h" << height << ":z" << z_levels << ")";
out << ":" << amount << ":" << flowmode << ":" << setmode << "]\n";
return df_liquids_execute(out);
}
command_result df_liquids_execute(color_ostream &out)
{
// create brush type depending on old parameters
Brush * brush;
if (brushname == "point")
{
brush = new RectangleBrush(1,1,1,0,0,0);
//width = 1;
//height = 1;
//z_levels = 1;
}
else if (brushname == "range")
{
brush = new RectangleBrush(width,height,z_levels,0,0,0);
}
else if(brushname == "block")
{
brush = new BlockBrush();
}
else if(brushname == "column")
{
brush = new ColumnBrush();
}
else if(brushname == "flood")
{
brush = new FloodBrush(&Core::getInstance());
}
else
{
// this should never happen!
out << "Old brushtype is invalid! Resetting to point brush.\n";
brushname = "point";
width = 1;
height = 1;
z_levels = 1;
brush = new RectangleBrush(width,height,z_levels,0,0,0);
}
CoreSuspender suspend;
do
{
if (!Maps::IsValid())
{
out << "Can't see any DF map loaded." << endl;
break;;
}
int32_t x,y,z;
if(!Gui::getCursorCoords(x,y,z))
{
out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
}
out << "cursor coords: " << x << "/" << y << "/" << z << endl;
MapCache mcache;
DFHack::DFCoord cursor(x,y,z);
coord_vec all_tiles = brush->points(mcache,cursor);
out << "working..." << endl;
if(mode == "obsidian")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, tiletype::LavaWall);
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
df::tile_designation des = mcache.designationAt(*iter);
des.bits.flow_size = 0;
mcache.setDesignationAt(*iter, des);
iter ++;
}
}
if(mode == "obsidian_floor")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1));
iter ++;
}
}
else if(mode == "riversource")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, tiletype::RiverSource);
df::tile_designation a = mcache.designationAt(*iter);
a.bits.liquid_type = tile_liquid::Water;
a.bits.liquid_static = false;
a.bits.flow_size = 7;
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
mcache.setDesignationAt(*iter,a);
Block * b = mcache.BlockAt((*iter)/16);
DFHack::t_blockflags bf = b->BlockFlags();
bf.bits.liquid_1 = true;
bf.bits.liquid_2 = true;
b->setBlockFlags(bf);
iter++;
}
}
else if(mode=="wclean")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
if (!Maps::IsValid())
DFHack::DFCoord current = *iter;
df::tile_designation des = mcache.designationAt(current);
des.bits.water_salt = false;
des.bits.water_stagnant = false;
mcache.setDesignationAt(current,des);
iter++;
}
}
else if(mode== "magma" || mode== "water" || mode == "flowbits")
{
set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
DFHack::DFCoord current = *iter; // current tile coord
DFHack::DFCoord curblock = current /16; // current block coord
// check if the block is actually there
if(!mcache.BlockAt(curblock))
{
out << "Can't see any DF map loaded." << endl;
break;;
iter ++;
continue;
}
if(!Gui::getCursorCoords(x,y,z))
df::tile_designation des = mcache.designationAt(current);
df::tiletype tt = mcache.tiletypeAt(current);
// don't put liquids into places where they don't belong...
if(!DFHack::FlowPassable(tt))
{
out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
iter++;
continue;
}
out << "cursor coords: " << x << "/" << y << "/" << z << endl;
MapCache mcache;
DFHack::DFCoord cursor(x,y,z);
coord_vec all_tiles = brush->points(mcache,cursor);
out << "working..." << endl;
if(mode == "obsidian")
if(mode != "flowbits")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
if(setmode == "s.")
{
mcache.setTiletypeAt(*iter, tiletype::LavaWall);
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
df::tile_designation des = mcache.designationAt(*iter);
des.bits.flow_size = 0;
mcache.setDesignationAt(*iter, des);
iter ++;
des.bits.flow_size = amount;
}
}
if(mode == "obsidian_floor")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
else if(setmode == "s+")
{
mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1));
iter ++;
if(des.bits.flow_size < amount)
des.bits.flow_size = amount;
}
}
else if(mode == "riversource")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
else if(setmode == "s-")
{
mcache.setTiletypeAt(*iter, tiletype::RiverSource);
df::tile_designation a = mcache.designationAt(*iter);
a.bits.liquid_type = tile_liquid::Water;
a.bits.liquid_static = false;
a.bits.flow_size = 7;
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
mcache.setDesignationAt(*iter,a);
Block * b = mcache.BlockAt((*iter)/16);
DFHack::t_blockflags bf = b->BlockFlags();
bf.bits.liquid_1 = true;
bf.bits.liquid_2 = true;
b->setBlockFlags(bf);
iter++;
if (des.bits.flow_size > amount)
des.bits.flow_size = amount;
}
}
else if(mode=="wclean")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
if(amount != 0 && mode == "magma")
{
DFHack::DFCoord current = *iter;
df::tile_designation des = mcache.designationAt(current);
des.bits.water_salt = false;
des.bits.water_stagnant = false;
mcache.setDesignationAt(current,des);
iter++;
des.bits.liquid_type = tile_liquid::Magma;
mcache.setTemp1At(current,12000);
mcache.setTemp2At(current,12000);
}
}
else if(mode== "magma" || mode== "water" || mode == "flowbits")
{
set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
else if(amount != 0 && mode == "water")
{
DFHack::DFCoord current = *iter; // current tile coord
DFHack::DFCoord curblock = current /16; // current block coord
// check if the block is actually there
if(!mcache.BlockAt(curblock))
{
iter ++;
continue;
}
df::tile_designation des = mcache.designationAt(current);
df::tiletype tt = mcache.tiletypeAt(current);
// don't put liquids into places where they don't belong...
if(!DFHack::FlowPassable(tt))
{
iter++;
continue;
}
if(mode != "flowbits")
{
if(setmode == "s.")
{
des.bits.flow_size = amount;
}
else if(setmode == "s+")
{
if(des.bits.flow_size < amount)
des.bits.flow_size = amount;
}
else if(setmode == "s-")
{
if (des.bits.flow_size > amount)
des.bits.flow_size = amount;
}
if(amount != 0 && mode == "magma")
{
des.bits.liquid_type = tile_liquid::Magma;
mcache.setTemp1At(current,12000);
mcache.setTemp2At(current,12000);
}
else if(amount != 0 && mode == "water")
{
des.bits.liquid_type = tile_liquid::Water;
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
else if(amount == 0 && (mode == "water" || mode == "magma"))
{
// reset temperature to sane default
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
mcache.setDesignationAt(current,des);
}
seen_blocks.insert(mcache.BlockAt(current / 16));
iter++;
des.bits.liquid_type = tile_liquid::Water;
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
set <Block *>::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end())
else if(amount == 0 && (mode == "water" || mode == "magma"))
{
DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+")
{
bflags.bits.liquid_1 = true;
bflags.bits.liquid_2 = true;
(*biter)->setBlockFlags(bflags);
}
else if(flowmode == "f-")
{
bflags.bits.liquid_1 = false;
bflags.bits.liquid_2 = false;
(*biter)->setBlockFlags(bflags);
}
else
{
out << "flow bit 1 = " << bflags.bits.liquid_1 << endl;
out << "flow bit 2 = " << bflags.bits.liquid_2 << endl;
}
biter ++;
// reset temperature to sane default
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
mcache.setDesignationAt(current,des);
}
seen_blocks.insert(mcache.BlockAt(current / 16));
iter++;
}
set <Block *>::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end())
{
DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+")
{
bflags.bits.liquid_1 = true;
bflags.bits.liquid_2 = true;
(*biter)->setBlockFlags(bflags);
}
else if(flowmode == "f-")
{
bflags.bits.liquid_1 = false;
bflags.bits.liquid_2 = false;
(*biter)->setBlockFlags(bflags);
}
if(mcache.WriteAll())
out << "OK" << endl;
else
out << "Something failed horribly! RUN!" << endl;
} while (0);
{
out << "flow bit 1 = " << bflags.bits.liquid_1 << endl;
out << "flow bit 2 = " << bflags.bits.liquid_2 << endl;
}
biter ++;
}
}
if(mcache.WriteAll())
out << "OK" << endl;
else
{
out << command << " : unknown command." << endl;
}
}
//cleanup
delete brush;
out << "Something failed horribly! RUN!" << endl;
} while (0);
// cleanup
delete brush;
return CR_OK;
}

@ -1,33 +0,0 @@
PROJECT (liquidsgo)
# A list of source files
SET(PROJECT_SRCS
liquidsgo.cpp
)
# A list of headers
SET(PROJECT_HDRS
liquidsgo.h
)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
# option to use a thread for no particular reason
#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON)
#linux
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
)
# windows
ELSE(UNIX)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
$(NOINHERIT)
)
ENDIF(UNIX)
# this makes sure all the stuff is put in proper places and linked to dfhack
DFHACK_PLUGIN(liquidsgo ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})

@ -1,709 +0,0 @@
// plugin liquidsgo
//
// This is a rewrite of the liquids module which can also be used non-interactively (hotkey).
// First the user sets the mode and other parameters with the interactive command liqiudsgo
// just like in the original liquids module.
// They are stored in statics to allow being used after the interactive session was closed.
// After defining an action the non-interactive command liquidsgo-here can be used to call the
// execute method without the necessity to go back to the console. This allows convenient painting
// of liquids and obsidian using the ingame cursor and a hotkey.
//
// Commands:
// liquidsgo - basically the original liquids with the map changing stuff moved to an execute method
// liquidsgo-here - runs the execute method with the last settings from liquidsgo
// (intended to be mapped to a hotkey)
// Options:
// ?, help - print some help
//
// TODO:
// - maybe allow all parameters be passed as command line options? tedious parsing but might be useful
// - grab the code from digcircle to get a circle brush - could be nice when painting with obsidian
// - maybe store the last parameters in a file to make them persistent after dfhack is closed?
#include <iostream>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cstdlib>
#include <sstream>
using std::vector;
using std::string;
using std::endl;
using std::set;
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Vegetation.h"
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "TileTypes.h"
#include "modules/MapCache.h"
using namespace MapExtras;
using namespace DFHack;
using namespace df::enums;
typedef vector <df::coord> coord_vec;
CommandHistory liquidsgo_hist;
command_result df_liquidsgo (color_ostream &out, vector <string> & parameters);
command_result df_liquidsgo_here (color_ostream &out, vector <string> & parameters);
command_result df_liquidsgo_execute (color_ostream &out);
DFHACK_PLUGIN("liquidsgo");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
liquidsgo_hist.load("liquidsgo.history");
commands.clear();
commands.push_back(PluginCommand(
"liquidsgo", "Place magma, water or obsidian.",
df_liquidsgo, true)); // interactive, needs console for prompt
commands.push_back(PluginCommand(
"liquidsgo-here", "Use settings from liquidsgo at cursor position.",
df_liquidsgo_here, Gui::cursor_hotkey, // non-interactive, needs ingame cursor
" Identical to pressing enter in liquidsgo, intended for use as keybinding.\n"
" Can (but doesn't need to) be called while liquidsgo is running in the console."));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
liquidsgo_hist.save("liquidsgo.history");
return CR_OK;
}
class Brush
{
public:
virtual ~Brush(){};
virtual coord_vec points(MapCache & mc,DFHack::DFCoord start) = 0;
};
/**
* generic 3D rectangle brush. you can specify the dimensions of
* the rectangle and optionally which tile is its 'center'
*/
class RectangleBrush : public Brush
{
public:
RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1)
{
if(centerx == -1)
cx_ = x/2;
else
cx_ = centerx;
if(centery == -1)
cy_ = y/2;
else
cy_ = centery;
if(centerz == -1)
cz_ = z/2;
else
cz_ = centerz;
x_ = x;
y_ = y;
z_ = z;
};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_);
DFHack::DFCoord iter = iterstart;
for(int xi = 0; xi < x_; xi++)
{
for(int yi = 0; yi < y_; yi++)
{
for(int zi = 0; zi < z_; zi++)
{
if(mc.testCoord(iter))
v.push_back(iter);
iter.z++;
}
iter.z = iterstart.z;
iter.y++;
}
iter.y = iterstart.y;
iter.x ++;
}
return v;
};
~RectangleBrush(){};
private:
int x_, y_, z_;
int cx_, cy_, cz_;
};
/**
* stupid block brush, legacy. use when you want to apply something to a whole DF map block.
*/
class BlockBrush : public Brush
{
public:
BlockBrush(){};
~BlockBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
DFHack::DFCoord blockc = start / 16;
DFHack::DFCoord iterc = blockc * 16;
if( !mc.testCoord(start) )
return v;
auto starty = iterc.y;
for(int xi = 0; xi < 16; xi++)
{
for(int yi = 0; yi < 16; yi++)
{
v.push_back(iterc);
iterc.y++;
}
iterc.y = starty;
iterc.x ++;
}
return v;
};
};
/**
* Column from a position through open space tiles
* example: create a column of magma
*/
class ColumnBrush : public Brush
{
public:
ColumnBrush(){};
~ColumnBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
bool juststarted = true;
while (mc.testCoord(start))
{
df::tiletype tt = mc.tiletypeAt(start);
if(DFHack::LowPassable(tt) || juststarted && DFHack::HighPassable(tt))
{
v.push_back(start);
juststarted = false;
start.z++;
}
else break;
}
return v;
};
};
/**
* Flood-fill water tiles from cursor (for wclean)
* example: remove salt flag from a river
*/
class FloodBrush : public Brush
{
public:
FloodBrush(Core *c){c_ = c;};
~FloodBrush(){};
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
std::stack<DFCoord> to_flood;
to_flood.push(start);
std::set<DFCoord> seen;
while (!to_flood.empty()) {
DFCoord xy = to_flood.top();
to_flood.pop();
df::tile_designation des = mc.designationAt(xy);
if (seen.find(xy) == seen.end()
&& des.bits.flow_size
&& des.bits.liquid_type == tile_liquid::Water) {
v.push_back(xy);
seen.insert(xy);
maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc);
maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc);
df::tiletype tt = mc.tiletypeAt(xy);
if (LowPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc);
}
if (HighPassable(tt))
{
maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc);
}
}
}
return v;
}
private:
void maybeFlood(DFCoord c, std::stack<DFCoord> &to_flood, MapCache &mc) {
if (mc.testCoord(c)) {
to_flood.push(c);
}
}
Core *c_;
};
// static stuff to be remembered between sessions
static string brushname = "point";
static string mode="magma";
static string flowmode="f+";
static string setmode ="s.";
static unsigned int amount = 7;
static int width = 1, height = 1, z_levels = 1;
command_result df_liquidsgo (color_ostream &out_, vector <string> & parameters)
{
assert(out_.is_console());
Console &out = static_cast<Console&>(out_);
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out.print( "This tool allows placing magma, water and other similar things.\n"
"It is interactive and further help is available when you run it.\n"
"The settings will be remembered until dfhack is closed and you can call\n"
"'liquidsgo-here' (mapped to a hotkey) to paint liquids at the cursor position\n"
"without the need to go back to the dfhack console.\n");
return CR_OK;
}
}
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
bool end = false;
out << "Welcome to the liquid spawner.\nType 'help' or '?' for a list of available commands, 'q' to quit.\nPress return after a command to confirm." << std::endl;
while(!end)
{
string command = "";
std::stringstream str;
str <<"[" << mode << ":" << brushname;
if (brushname == "range")
str << "(w" << width << ":h" << height << ":z" << z_levels << ")";
str << ":" << amount << ":" << flowmode << ":" << setmode << "]#";
if(out.lineedit(str.str(),command,liquidsgo_hist) == -1)
return CR_FAILURE;
if(command=="help" || command == "?")
{
out << "Modes:" << endl
<< "m - switch to magma" << endl
<< "w - switch to water" << endl
<< "o - make obsidian wall instead" << endl
<< "of - make obsidian floors" << endl
<< "rs - make a river source" << endl
<< "f - flow bits only" << endl
<< "wclean - remove salt and stagnant flags from tiles" << endl
<< "Set-Modes (only for magma/water):" << endl
<< "s+ - only add" << endl
<< "s. - set" << endl
<< "s- - only remove" << endl
<< "Properties (only for magma/water):" << endl
<< "f+ - make the spawned liquid flow" << endl
<< "f. - don't change flow state (read state in flow mode)" << endl
<< "f- - make the spawned liquid static" << endl
<< "0-7 - set liquid amount" << endl
<< "Brush:" << endl
<< "point - single tile [p]" << endl
<< "range - block with cursor at bottom north-west [r]" << endl
<< " (any place, any size)" << endl
<< "block - DF map block with cursor in it" << endl
<< " (regular spaced 16x16x1 blocks)" << endl
<< "column - Column from cursor, up through free space" << endl
<< "flood - Flood-fill water tiles from cursor" << endl
<< " (only makes sense with wclean)" << endl
<< "Other:" << endl
<< "q - quit" << endl
<< "help or ? - print this list of commands" << endl
<< "empty line - put liquid" << endl
<< endl
<< "Usage: point the DF cursor at a tile you want to modify" << endl
<< "and use the commands available :)" << endl;
out << endl << "Settings will be remembered until you quit DF. You can call liquidsgo-here to execute the last configured action. Useful in combination with keybindings." << endl;
}
else if(command == "m")
{
mode = "magma";
}
else if(command == "o")
{
mode = "obsidian";
}
else if(command == "of")
{
mode = "obsidian_floor";
}
else if(command == "w")
{
mode = "water";
}
else if(command == "f")
{
mode = "flowbits";
}
else if(command == "rs")
{
mode = "riversource";
}
else if(command == "wclean")
{
mode = "wclean";
}
else if(command == "point" || command == "p")
{
brushname = "point";
}
else if(command == "range" || command == "r")
{
std::stringstream str;
CommandHistory range_hist;
str << " :set range width<" << width << "># ";
out.lineedit(str.str(),command,range_hist);
range_hist.add(command);
width = command == "" ? width : atoi (command.c_str());
if(width < 1) width = 1;
str.str("");
str << " :set range height<" << height << "># ";
out.lineedit(str.str(),command,range_hist);
range_hist.add(command);
height = command == "" ? height : atoi (command.c_str());
if(height < 1) height = 1;
str.str("");
str << " :set range z-levels<" << z_levels << "># ";
out.lineedit(str.str(),command,range_hist);
range_hist.add(command);
z_levels = command == "" ? z_levels : atoi (command.c_str());
if(z_levels < 1) z_levels = 1;
if(width == 1 && height == 1 && z_levels == 1)
{
brushname = "point";
}
else
{
brushname = "range";
}
}
else if(command == "block")
{
brushname = "block";
}
else if(command == "column")
{
brushname = "column";
}
else if(command == "flood")
{
brushname = "flood";
}
else if(command == "q")
{
end = true;
}
else if(command == "f+")
{
flowmode = "f+";
}
else if(command == "f-")
{
flowmode = "f-";
}
else if(command == "f.")
{
flowmode = "f.";
}
else if(command == "s+")
{
setmode = "s+";
}
else if(command == "s-")
{
setmode = "s-";
}
else if(command == "s.")
{
setmode = "s.";
}
// blah blah, bad code, bite me.
else if(command == "0")
amount = 0;
else if(command == "1")
amount = 1;
else if(command == "2")
amount = 2;
else if(command == "3")
amount = 3;
else if(command == "4")
amount = 4;
else if(command == "5")
amount = 5;
else if(command == "6")
amount = 6;
else if(command == "7")
amount = 7;
else if(command.empty())
{
df_liquidsgo_execute(out);
}
else
{
out << command << " : unknown command." << endl;
}
}
return CR_OK;
}
command_result df_liquidsgo_here (color_ostream &out, vector <string> & parameters)
{
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out << "This command is supposed to be mapped to a hotkey." << endl;
out << "It will use the current/last parameters set in liquidsgo." << endl;
return CR_OK;
}
}
out.print("Run liquidsgo-here with these parameters: ");
out << "[" << mode << ":" << brushname;
if (brushname == "range")
out << "(w" << width << ":h" << height << ":z" << z_levels << ")";
out << ":" << amount << ":" << flowmode << ":" << setmode << "]\n";
return df_liquidsgo_execute(out);
}
command_result df_liquidsgo_execute(color_ostream &out)
{
// create brush type depending on old parameters
Brush * brush;
if (brushname == "point")
{
brush = new RectangleBrush(1,1,1,0,0,0);
//width = 1;
//height = 1;
//z_levels = 1;
}
else if (brushname == "range")
{
brush = new RectangleBrush(width,height,z_levels,0,0,0);
}
else if(brushname == "block")
{
brush = new BlockBrush();
}
else if(brushname == "column")
{
brush = new ColumnBrush();
}
else if(brushname == "flood")
{
brush = new FloodBrush(&Core::getInstance());
}
else
{
// this should never happen!
out << "Old brushtype is invalid! Resetting to point brush.\n";
brushname = "point";
width = 1;
height = 1;
z_levels = 1;
brush = new RectangleBrush(width,height,z_levels,0,0,0);
}
CoreSuspender suspend;
do
{
if (!Maps::IsValid())
{
out << "Can't see any DF map loaded." << endl;
break;;
}
int32_t x,y,z;
if(!Gui::getCursorCoords(x,y,z))
{
out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
}
out << "cursor coords: " << x << "/" << y << "/" << z << endl;
MapCache mcache;
DFHack::DFCoord cursor(x,y,z);
coord_vec all_tiles = brush->points(mcache,cursor);
out << "working..." << endl;
if(mode == "obsidian")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, tiletype::LavaWall);
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
df::tile_designation des = mcache.designationAt(*iter);
des.bits.flow_size = 0;
mcache.setDesignationAt(*iter, des);
iter ++;
}
}
if(mode == "obsidian_floor")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1));
iter ++;
}
}
else if(mode == "riversource")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, tiletype::RiverSource);
df::tile_designation a = mcache.designationAt(*iter);
a.bits.liquid_type = tile_liquid::Water;
a.bits.liquid_static = false;
a.bits.flow_size = 7;
mcache.setTemp1At(*iter,10015);
mcache.setTemp2At(*iter,10015);
mcache.setDesignationAt(*iter,a);
Block * b = mcache.BlockAt((*iter)/16);
DFHack::t_blockflags bf = b->BlockFlags();
bf.bits.liquid_1 = true;
bf.bits.liquid_2 = true;
b->setBlockFlags(bf);
iter++;
}
}
else if(mode=="wclean")
{
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
DFHack::DFCoord current = *iter;
df::tile_designation des = mcache.designationAt(current);
des.bits.water_salt = false;
des.bits.water_stagnant = false;
mcache.setDesignationAt(current,des);
iter++;
}
}
else if(mode== "magma" || mode== "water" || mode == "flowbits")
{
set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
DFHack::DFCoord current = *iter; // current tile coord
DFHack::DFCoord curblock = current /16; // current block coord
// check if the block is actually there
if(!mcache.BlockAt(curblock))
{
iter ++;
continue;
}
df::tile_designation des = mcache.designationAt(current);
df::tiletype tt = mcache.tiletypeAt(current);
// don't put liquids into places where they don't belong...
if(!DFHack::FlowPassable(tt))
{
iter++;
continue;
}
if(mode != "flowbits")
{
if(setmode == "s.")
{
des.bits.flow_size = amount;
}
else if(setmode == "s+")
{
if(des.bits.flow_size < amount)
des.bits.flow_size = amount;
}
else if(setmode == "s-")
{
if (des.bits.flow_size > amount)
des.bits.flow_size = amount;
}
if(amount != 0 && mode == "magma")
{
des.bits.liquid_type = tile_liquid::Magma;
mcache.setTemp1At(current,12000);
mcache.setTemp2At(current,12000);
}
else if(amount != 0 && mode == "water")
{
des.bits.liquid_type = tile_liquid::Water;
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
else if(amount == 0 && (mode == "water" || mode == "magma"))
{
// reset temperature to sane default
mcache.setTemp1At(current,10015);
mcache.setTemp2At(current,10015);
}
mcache.setDesignationAt(current,des);
}
seen_blocks.insert(mcache.BlockAt(current / 16));
iter++;
}
set <Block *>::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end())
{
DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+")
{
bflags.bits.liquid_1 = true;
bflags.bits.liquid_2 = true;
(*biter)->setBlockFlags(bflags);
}
else if(flowmode == "f-")
{
bflags.bits.liquid_1 = false;
bflags.bits.liquid_2 = false;
(*biter)->setBlockFlags(bflags);
}
else
{
out << "flow bit 1 = " << bflags.bits.liquid_1 << endl;
out << "flow bit 2 = " << bflags.bits.liquid_2 << endl;
}
biter ++;
}
}
if(mcache.WriteAll())
out << "OK" << endl;
else
out << "Something failed horribly! RUN!" << endl;
} while (0);
// cleanup
delete brush;
return CR_OK;
}

@ -2,6 +2,7 @@ package dfproto;
option optimize_for = LITE_RUNTIME;
// RPC RenameSquad : RenameSquadIn -> EmptyMessage
message RenameSquadIn {
required int32 squad_id = 1;
@ -9,6 +10,7 @@ message RenameSquadIn {
optional string alias = 3;
}
// RPC RenameUnit : RenameUnitIn -> EmptyMessage
message RenameUnitIn {
required int32 unit_id = 1;

@ -1 +1 @@
Subproject commit 719dbc048a55ba1def2ce21e9cd29e33dbe833ce
Subproject commit 7525c003089367823183eaf5093a90271a5eb9b4