Merge branch 'develop' of dfhack/dfhack into develop

develop
lethosor 2016-07-29 19:15:53 -04:00
commit 60cf860450
23 changed files with 1361 additions and 249 deletions

@ -56,6 +56,15 @@ endif()
if(MSVC)
# disable C4819 code-page warning
add_definitions( "/wd4819" )
# Disable use of POSIX name warnings
add_definitions ( "/D_CRT_NONSTDC_NO_WARNINGS")
# supress C4503 - VC++ dislikes if a name is too long. If you get
# weird and mysterious linking errors, you can disable this, but you'll have to
# deal with a LOT of compiler noise over it
# see https://msdn.microsoft.com/en-us/library/074af4b6.aspx
add_definitions( "/wd4503")
endif()
SET(DFHACK_BUILD_ARCH "32" CACHE STRING "Architecture to build ('32' or '64')")

@ -37,6 +37,10 @@ Lua
---
- Label widgets can now easily register handlers for mouse clicks
New Plugins
-----------
- `dwarfvet` enables animal caretaking.
New Features
------------
- `add-thought`: allow syndrome name as ``-thought`` argument

@ -1,4 +1,4 @@
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=RelWithDebInfo ALL_BUILD.vcxproj
cd ..

@ -1,5 +1,5 @@
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=Release ALL_BUILD.vcxproj
cd ..
pause

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

@ -1,6 +1,6 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
mkdir VC2015_32
cd VC2015_32
echo generating a build folder
cmake ..\.. -G"Visual Studio 10" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=1 -DBUILD_DEV_PLUGINS=1 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1
cmake ..\.. -G"Visual Studio 14" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=1 -DBUILD_DEV_PLUGINS=1 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1

@ -1,7 +1,7 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
mkdir VC2015_32
cd VC2015_32
echo Pre-generating a build folder
cmake ..\.. -G"Visual Studio 10" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%"
cmake ..\.. -G"Visual Studio 14" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%"
cmake-gui .

@ -1,6 +1,6 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
mkdir VC2015_32
cd VC2015_32
echo generating a build folder
cmake ..\.. -G"Visual Studio 10" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DF2MC=0 -DBUILD_DFUSION=0 -DBUILD_STONESENSE=0 -DBUILD_SERVER=0
cmake ..\.. -G"Visual Studio 14" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DF2MC=0 -DBUILD_DFUSION=0 -DBUILD_STONESENSE=0 -DBUILD_SERVER=0

@ -1,6 +1,6 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
mkdir VC2015_32
cd VC2015_32
echo generating a build folder
cmake ..\.. -G"Visual Studio 10" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1
cmake ..\.. -G"Visual Studio 14" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1

@ -1,4 +1,4 @@
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=RelWithDebInfo INSTALL.vcxproj
cd ..

@ -1,4 +1,4 @@
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=Release INSTALL.vcxproj
cd ..

@ -1,6 +1,6 @@
@echo off
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=RelWithDebInfo PACKAGE.vcxproj
cd ..
exit %ERRORLEVEL%

@ -1,5 +1,5 @@
@echo off
call "%VS100COMNTOOLS%vsvars32.bat"
cd VC2010
call "%VS140COMNTOOLS%vsvars32.bat"
cd VC2015_32
msbuild /m /p:Platform=Win32 /p:Configuration=Release PACKAGE.vcxproj
cd ..

@ -13,6 +13,8 @@ IF(CMAKE_COMPILER_IS_GNUCC)
#ENDIF()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
ENDIF()
SET(HAVE_HASH_MAP 0)
SET(HASH_MAP_CLASS unordered_map)
@ -65,16 +67,8 @@ IF(CMAKE_COMPILER_IS_GNUCC)
MESSAGE(SEND_ERROR "Could not find a working hash map implementation. Please install GCC >= 4.4, and all necessary 32-bit C++ development libraries.")
ENDIF()
IF(UNIX)
FIND_PACKAGE(Threads)
ELSE()
SET(HASH_MAP_H <hash_map>)
SET(HASH_NAMESPACE std)
SET(HASH_SET_H <hash_set>)
SET(HAVE_HASH_MAP 1)
SET(HAVE_HASH_SET 1)
SET(HASH_MAP_CLASS hash_map)
SET(HASH_SET_CLASS hash_set)
ENDIF()
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h")

@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
@ -37,13 +37,134 @@
#include <string.h>
#include <google/protobuf/stubs/common.h>
#include "config.h"
#if defined(HAVE_HASH_MAP) && defined(HAVE_HASH_SET)
#include HASH_MAP_H
#include HASH_SET_H
#define GOOGLE_PROTOBUF_HAVE_HASH_MAP 1
#define GOOGLE_PROTOBUF_HAVE_HASH_SET 1
// Android
#if defined(__ANDROID__)
# undef GOOGLE_PROTOBUF_HAVE_HASH_MAP
# undef GOOGLE_PROTOBUF_HAVE_HASH_MAP
// Use C++11 unordered_{map|set} if available.
#elif ((_LIBCPP_STD_VER >= 11) || \
(((__cplusplus >= 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X)) && \
(__GLIBCXX__ > 20090421)))
# define GOOGLE_PROTOBUF_HAS_CXX11_HASH
// For XCode >= 4.6: the compiler is clang with libc++.
// For earlier XCode version: the compiler is gcc-4.2.1 with libstdc++.
// libc++ provides <unordered_map> and friends even in non C++11 mode,
// and it does not provide the tr1 library. Therefore the following macro
// checks against this special case.
// Note that we should not test the __APPLE_CC__ version number or the
// __clang__ macro, since the new compiler can still use -stdlib=libstdc++, in
// which case <unordered_map> is not compilable without -std=c++11
#elif defined(__APPLE_CC__)
# if __GNUC__ >= 4
# define GOOGLE_PROTOBUF_HAS_TR1
# else
#define MISSING_HASH
// Not tested for gcc < 4... These setting can compile under 4.2.1 though.
# define GOOGLE_PROTOBUF_HASH_NAMESPACE __gnu_cxx
# include <ext/hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <ext/hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# endif
// Version checks for gcc.
#elif defined(__GNUC__)
// For GCC 4.x+, use tr1::unordered_map/set; otherwise, follow the
// instructions from:
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/backwards.html
# if __GNUC__ >= 4
# define GOOGLE_PROTOBUF_HAS_TR1
# elif __GNUC__ >= 3
# include <backward/hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <backward/hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# if __GNUC__ == 3 && __GNUC_MINOR__ == 0
# define GOOGLE_PROTOBUF_HASH_NAMESPACE std // GCC 3.0
# else
# define GOOGLE_PROTOBUF_HASH_NAMESPACE __gnu_cxx // GCC 3.1 and later
# endif
# else
# define GOOGLE_PROTOBUF_HASH_NAMESPACE
# include <hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# endif
// Version checks for MSC.
// Apparently Microsoft decided to move hash_map *back* to the std namespace in
// MSVC 2010:
// http://blogs.msdn.com/vcblog/archive/2009/05/25/stl-breaking-changes-in-visual-studio-2010-beta-1.aspx
// And.. they are moved back to stdext in MSVC 2013 (haven't checked 2012). That
// said, use unordered_map for MSVC 2010 and beyond is our safest bet.
#elif defined(_MSC_VER)
# if _MSC_VER >= 1600 // Since Visual Studio 2010
# define GOOGLE_PROTOBUF_HAS_CXX11_HASH
# define GOOGLE_PROTOBUF_HASH_COMPARE std::hash_compare
# elif _MSC_VER >= 1500 // Since Visual Studio 2008
# define GOOGLE_PROTOBUF_HASH_NAMESPACE stdext
# include <hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# define GOOGLE_PROTOBUF_HASH_COMPARE stdext::hash_compare
# define GOOGLE_PROTOBUF_CONTAINERS_NEED_HASH_COMPARE
# elif _MSC_VER >= 1310
# define GOOGLE_PROTOBUF_HASH_NAMESPACE stdext
# include <hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# define GOOGLE_PROTOBUF_HASH_COMPARE stdext::hash_compare
# else
# define GOOGLE_PROTOBUF_HASH_NAMESPACE std
# include <hash_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
# include <hash_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
# define GOOGLE_PROTOBUF_HASH_COMPARE stdext::hash_compare
# endif
// **ADD NEW COMPILERS SUPPORT HERE.**
// For other compilers, undefine the macro and fallback to use std::map, in
// google/protobuf/stubs/hash.h
#else
# undef GOOGLE_PROTOBUF_HAVE_HASH_MAP
# undef GOOGLE_PROTOBUF_HAVE_HASH_SET
#endif
#if defined(GOOGLE_PROTOBUF_HAS_CXX11_HASH)
# define GOOGLE_PROTOBUF_HASH_NAMESPACE std
# include <unordered_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS unordered_map
# include <unordered_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS unordered_set
#elif defined(GOOGLE_PROTOBUF_HAS_TR1)
# define GOOGLE_PROTOBUF_HASH_NAMESPACE std::tr1
# include <tr1/unordered_map>
# define GOOGLE_PROTOBUF_HASH_MAP_CLASS unordered_map
# include <tr1/unordered_set>
# define GOOGLE_PROTOBUF_HASH_SET_CLASS unordered_set
#endif
# define GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_START \
namespace google { \
namespace protobuf {
# define GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_END }}
#undef GOOGLE_PROTOBUF_HAS_CXX11_HASH
#undef GOOGLE_PROTOBUF_HAS_TR1
#if defined(GOOGLE_PROTOBUF_HAVE_HASH_MAP) && \
defined(GOOGLE_PROTOBUF_HAVE_HASH_SET)
#else
#define GOOGLE_PROTOBUF_MISSING_HASH
#include <map>
#include <set>
#endif
@ -51,7 +172,8 @@
namespace google {
namespace protobuf {
#ifdef MISSING_HASH
#ifdef GOOGLE_PROTOBUF_MISSING_HASH
#undef GOOGLE_PROTOBUF_MISSING_HASH
// This system doesn't have hash_map or hash_set. Emulate them using map and
// set.
@ -88,20 +210,33 @@ struct hash<const char*> {
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_map : public std::map<Key, Data, HashFcn> {
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator< std::pair<const Key, Data> > >
class hash_map : public std::map<Key, Data, HashFcn, Alloc> {
typedef std::map<Key, Data, HashFcn, Alloc> BaseClass;
public:
hash_map(int a = 0, const HashFcn& b = HashFcn(),
const EqualKey& c = EqualKey(),
const Alloc& d = Alloc()) : BaseClass(b, d) {}
HashFcn hash_function() const { return HashFcn(); }
};
template <typename Key,
typename HashFcn = hash<Key>,
typename EqualKey = int >
typename EqualKey = std::equal_to<Key> >
class hash_set : public std::set<Key, HashFcn> {
public:
hash_set(int = 0) {}
HashFcn hash_function() const { return HashFcn(); }
};
#elif defined(_MSC_VER) && !defined(_STLPORT_VERSION)
template <typename Key>
struct hash : public HASH_NAMESPACE::hash_compare<Key> {
struct hash : public GOOGLE_PROTOBUF_HASH_COMPARE<Key> {
};
// MSVC's hash_compare<const char*> hashes based on the string contents but
@ -115,27 +250,88 @@ class CstringLess {
template <>
struct hash<const char*>
: public HASH_NAMESPACE::hash_compare<const char*, CstringLess> {
: public GOOGLE_PROTOBUF_HASH_COMPARE<const char*, CstringLess> {};
#ifdef GOOGLE_PROTOBUF_CONTAINERS_NEED_HASH_COMPARE
template <typename Key, typename HashFcn, typename EqualKey>
struct InternalHashCompare : public GOOGLE_PROTOBUF_HASH_COMPARE<Key> {
InternalHashCompare() {}
InternalHashCompare(HashFcn hashfcn, EqualKey equalkey)
: hashfcn_(hashfcn), equalkey_(equalkey) {}
size_t operator()(const Key& key) const { return hashfcn_(key); }
bool operator()(const Key& key1, const Key& key2) const {
return !equalkey_(key1, key2);
}
HashFcn hashfcn_;
EqualKey equalkey_;
};
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_map : public HASH_NAMESPACE::hash_map<
Key, Data, HashFcn> {
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator< std::pair<const Key, Data> > >
class hash_map
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, InternalHashCompare<Key, HashFcn, EqualKey>, Alloc> {
typedef GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, InternalHashCompare<Key, HashFcn, EqualKey>, Alloc> BaseClass;
public:
hash_map(int a = 0, const HashFcn& b = HashFcn(),
const EqualKey& c = EqualKey(), const Alloc& d = Alloc())
: BaseClass(InternalHashCompare<Key, HashFcn, EqualKey>(b, c), d) {}
HashFcn hash_function() const { return HashFcn(); }
};
template <typename Key,
template <typename Key, typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_set
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_SET_CLASS<
Key, InternalHashCompare<Key, HashFcn, EqualKey> > {
public:
hash_set(int = 0) {}
HashFcn hash_function() const { return HashFcn(); }
};
#else // GOOGLE_PROTOBUF_CONTAINERS_NEED_HASH_COMPARE
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_set : public HASH_NAMESPACE::hash_set<
Key, HashFcn> {
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator< std::pair<const Key, Data> > >
class hash_map
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey, Alloc> {
typedef GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey, Alloc> BaseClass;
public:
hash_map(int a = 0, const HashFcn& b = HashFcn(),
const EqualKey& c = EqualKey(),
const Alloc& d = Alloc()) : BaseClass(a, b, c, d) {}
HashFcn hash_function() const { return HashFcn(); }
};
#else
template <typename Key, typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_set
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_SET_CLASS<
Key, HashFcn, EqualKey> {
public:
hash_set(int = 0) {}
HashFcn hash_function() const { return HashFcn(); }
};
#endif // GOOGLE_PROTOBUF_CONTAINERS_NEED_HASH_COMPARE
#else // defined(_MSC_VER) && !defined(_STLPORT_VERSION)
template <typename Key>
struct hash : public HASH_NAMESPACE::hash<Key> {
struct hash : public GOOGLE_PROTOBUF_HASH_NAMESPACE::hash<Key> {
};
template <typename Key>
@ -158,21 +354,43 @@ struct hash<const char*> {
}
};
template<>
struct hash<bool> {
size_t operator()(bool x) const {
return static_cast<size_t>(x);
}
};
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_map : public HASH_NAMESPACE::HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey> {
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator< std::pair<const Key, Data> > >
class hash_map
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey, Alloc> {
typedef GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey, Alloc> BaseClass;
public:
hash_map(int a = 0, const HashFcn& b = HashFcn(),
const EqualKey& c = EqualKey(),
const Alloc& d = Alloc()) : BaseClass(a, b, c, d) {}
HashFcn hash_function() const { return HashFcn(); }
};
template <typename Key,
typename HashFcn = hash<Key>,
template <typename Key, typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_set : public HASH_NAMESPACE::HASH_SET_CLASS<
class hash_set
: public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_SET_CLASS<
Key, HashFcn, EqualKey> {
public:
hash_set(int = 0) {}
HashFcn hash_function() const { return HashFcn(); }
};
#endif
#endif // !GOOGLE_PROTOBUF_MISSING_HASH
template <>
struct hash<string> {
@ -182,7 +400,7 @@ struct hash<string> {
static const size_t bucket_size = 4;
static const size_t min_buckets = 8;
inline size_t operator()(const string& a, const string& b) const {
inline bool operator()(const string& a, const string& b) const {
return a < b;
}
};
@ -200,7 +418,7 @@ struct hash<pair<First, Second> > {
static const size_t bucket_size = 4;
static const size_t min_buckets = 8;
inline size_t operator()(const pair<First, Second>& a,
inline bool operator()(const pair<First, Second>& a,
const pair<First, Second>& b) const {
return a < b;
}

@ -57,6 +57,7 @@ Max maxthyme Max^TM
melkor217 melkor217
Meneth32
Meph
Michael Casadevall NCommander
Michon van Dooren MaienM
miffedmap miffedmap
Mike Stewart thewonderidiot

@ -1109,7 +1109,6 @@ Example:
This will confiscate rotten and dropped food, garbage on the floors and any
worn items with 'X' damage and above.
.. _dwarfmonitor:
dwarfmonitor
@ -1187,6 +1186,28 @@ Some widgets support additional options:
displayed as ``-1`` when the cursor is outside of the DF window; otherwise,
nothing will be displayed.
.. _dwarfvet:
dwarfvet
============
Enables Animal Caretaker functionality
Always annoyed your dragons become useless after a minor injury? Well, with
dwarfvet, your animals become first rate members of your fort. It can also
be used to train medical skills.
Animals need to be treated in an animal hospital, which is simply a hospital
that is also an animal training zone. The console will print out a list on game
load, and whenever one is added or removed. Dwarfs must have the Animal Caretaker
labor to treat animals. Normal medical skills are used (and no experience is given
to the Animal Caretaker skill).
Options:
:enable: Enables Animal Caretakers to treat and manage animals
:disable: Turns off the plguin
:report: Reports all zones that the game considers animal hospitals
.. _workNow:
workNow

@ -38,8 +38,21 @@ namespace DFHack
* our wrapper for the C++ exception. used to differentiate
* the whole array of DFHack exceptions from the rest
*/
#ifdef _MSC_VER
#pragma push
/**
* C4275 is - The warning officially is non dll-interface class 'std::exception' used as base for
* dll-interface class
*
* Basically, its saying that you might have an ABI problem if you mismatch compilers. We don't
* care since we build all of DFhack at once against whatever Toady is using
*/
#pragma warning(disable: 4275)
#endif
class DFHACK_EXPORT All : public std::exception{};
#ifdef _MSC_VER
#pragma pop
#endif
class DFHACK_EXPORT NullPointer : public All {
const char *varname_;
public:

@ -287,7 +287,7 @@ namespace DFHack {namespace Lua {
#undef NUMBER_PUSH
#else
template<class T>
inline typename std::enable_if<std::is_integral<T>::value>::type
inline typename std::enable_if<std::is_integral<T>::value || std::is_enum<T>::value>::type
Push(lua_State *state, T value) {
lua_pushinteger(state, value);
}

@ -292,6 +292,8 @@ DFHACK_EXPORT bool isActivityZone(df::building * building);
DFHACK_EXPORT bool isPenPasture(df::building * building);
DFHACK_EXPORT bool isPitPond(df::building * building);
DFHACK_EXPORT bool isActive(df::building * building);
DFHACK_EXPORT bool isHospital(df::building * building);
DFHACK_EXPORT bool isAnimalTraining(df::building * building);
DFHACK_EXPORT df::building* findPenPitAt(df::coord coord);
}

@ -1263,6 +1263,20 @@ bool Buildings::isActive(df::building * building)
return ((df::building_civzonest*) building)->zone_flags.bits.active != 0;
}
bool Buildings::isHospital(df::building * building)
{
if (!isActivityZone(building))
return false;
return ((df::building_civzonest*) building)->zone_flags.bits.hospital != 0;
}
bool Buildings::isAnimalTraining(df::building * building)
{
if (!isActivityZone(building))
return false;
return ((df::building_civzonest*) building)->zone_flags.bits.animal_training != 0;
}
// returns building of pen/pit at cursor position (NULL if nothing found)
df::building* Buildings::findPenPitAt(df::coord coord)
{

@ -92,6 +92,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(dig dig.cpp)
DFHACK_PLUGIN(digFlood digFlood.cpp)
# add_subdirectory(diggingInvaders)
DFHACK_PLUGIN(dwarfvet dwarfvet.cpp)
DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)

@ -0,0 +1,835 @@
/**
* Copyright (c) 2015, Michael Casadevall
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
**/
#include "Console.h"
#include "Core.h"
#include "DataDefs.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/EventManager.h"
#include "modules/Units.h"
#include "modules/Buildings.h"
#include "modules/Maps.h"
#include "modules/Job.h"
#include "df/animal_training_level.h"
#include "df/building_type.h"
#include "df/caste_raw.h"
#include "df/caste_raw_flags.h"
#include "df/creature_raw.h"
#include "df/job.h"
#include "df/general_ref_unit_workerst.h"
#include "df/profession.h"
#include "df/ui.h"
#include "df/unit.h"
#include "df/unit_health_info.h"
#include "df/unit_health_flags.h"
#include "df/world.h"
#include <map>
#include <vector>
using namespace DFHack;
using namespace DFHack::Units;
using namespace DFHack::Buildings;
using namespace std;
DFHACK_PLUGIN("dwarfvet");
DFHACK_PLUGIN_IS_ENABLED(dwarfvet_enabled);
REQUIRE_GLOBAL(ui);
REQUIRE_GLOBAL(world);
static vector<int32_t> tracked_units;
static int32_t howOften = 100;
struct hospital_spot {
int32_t x;
int32_t y;
int32_t z;
};
class Patient {
public:
// Constructor/Deconstrctor
Patient(int32_t id, int spot_index, int32_t x, int32_t y, int32_t z);
int32_t getID() { return this->id; };
int32_t getSpotIndex() { return this->spot_index; };
int32_t returnX() { return this->spot_in_hospital.x; };
int32_t returnY() { return this->spot_in_hospital.y; };
int32_t returnZ() { return this->spot_in_hospital.z; };
private:
struct hospital_spot spot_in_hospital;
int id;
int spot_index;
};
Patient::Patient(int32_t id, int32_t spot_index, int32_t x, int32_t y, int32_t z){
this->id = id;
this->spot_in_hospital.x = x;
this->spot_in_hospital.y = y;
this->spot_in_hospital.z = z;
}
class AnimalHospital {
public:
// Constructor
AnimalHospital(df::building *, color_ostream &out);
~AnimalHospital();
int32_t getID() { return id; }
bool acceptPatient(int32_t id, color_ostream&);
void processPatients(color_ostream &out);
void dischargePatient(Patient * patient, color_ostream &out);
void calculateHospital(bool force, color_ostream &out);
void reportUsage(color_ostream &out);
// GC
bool to_be_deleted;
private:
int spots_open;
int32_t id;
int32_t x1;
int32_t x2;
int32_t y1;
int32_t y2;
int32_t z;
int height;
int length;
// Doing an actual array in C++ is *annoying*, bloody copy constructors */
vector<bool> spots_in_use;
vector<int32_t> building_in_hospital_notification; /* If present, we already notified about this */
vector<Patient*> accepted_patients;
};
AnimalHospital::AnimalHospital(df::building * building, color_ostream &out) {
// Copy in what we need to know
id = building->id;
x1 = building->x1;
x2 = building->x2;
y1 = building->y1;
y2 = building->y2;
z = building->z;
// Determine how many spots we have for animals
this->length = x2-x1;
this->height = y2-y1;
// And calculate the hospital!
this->calculateHospital(true, out);
}
AnimalHospital::~AnimalHospital() {
// Go through and delete all the patients
for (vector<Patient*>::iterator accepted_patient = this->accepted_patients.begin(); accepted_patient != this->accepted_patients.end(); accepted_patient++) {
delete (*accepted_patient);
}
}
bool AnimalHospital::acceptPatient(int32_t id, color_ostream &out) {
// This function determines if we can accept a patient, and if we will.
this->calculateHospital(true, out);
// First, do we have room?
if (!spots_open) return false;
// Yup, let's find the next open spot,
// and give it to our patient
int spot_cur = 0; // fuck the STL for requiring a second counter to make this usable
for (vector<bool>::iterator spot = this->spots_in_use.begin(); spot != this->spots_in_use.end(); spot++) {
if (*spot == false) {
*spot = true;
break;
}
spot_cur++;
}
spots_open--;
// Convert the spot into x/y/z cords.
int offset_y = spot_cur/length;
int offset_x = spot_cur%length;
// Create the patient!
Patient * patient = new Patient(id,
spot_cur,
this->x1+offset_x,
this->y1+offset_y,
this->z
);
accepted_patients.push_back(patient);
return true;
}
// Before any use of the hospital, we need to make calculate open spots
// and such. This can change (i.e. stuff built in hospital) and
// such so it should be called on each function.
void AnimalHospital::reportUsage(color_ostream &out) {
// Debugging tool to see parts of the hospital in use
int length_cursor = this->length;
for (vector<bool>::iterator spot = this->spots_in_use.begin(); spot != this->spots_in_use.end(); spot++) {
if (*spot) out.print("t");
if (!(*spot)) out.print("f");
length_cursor--;
if (length_cursor < 0) {
out.print("\n");
length_cursor = this->length;
}
}
out.print("\n");
}
void AnimalHospital::calculateHospital(bool force, color_ostream &out) {
// Only calculate out the hospital if we actually have a patient in it
// (acceptPatient will forcibly rerun this to make sure everything OK
// Should reduce FPS impact of each calculation tick when the hospitals
// are not in use
//if (!force || (spots_open == length*height)) {
// Hospital is idle, don't recalculate
// return;
//}
// Calculate out the total area of the hospital
// This can change if a hospital has been resized
this->spots_open = length*height;
this->spots_in_use.assign(this->spots_open, false);
// The spots_in_use maps one to one with a spot
// starting at the upper-left hand corner, then
// across, then down. i.e.
//
// given hospital zone:
//
// UU
// uU
//
// where U is in use, and u isn't, the array
// would be t,t,f,t
// Walk the building array and see what stuff is in the hospital,
// then walk the patient array and remark those spots as used.
// If a patient is in an invalid spot, reassign it
for (size_t b =0 ; b < world->buildings.all.size(); b++) {
df::building* building = world->buildings.all[b];
// Check that we're not comparing ourselves;
if (building->id == this->id) {
continue;
}
// Check if the building is on our z level, if it isn't
// then it can't overlap the hospital (until Toady implements
// multi-z buildings
if (building->z != this->z) {
continue;
}
// DF defines activity zones multiple times in the building structure
// If axises agree with each other, we're looking at a reflection of
// ourselves
if (building->x1 == this->x1 &&
building->x2 == this->x2 &&
building->y1 == this->y1 &&
building->y2 == this->y2) {
continue;
}
// Check for X/Y overlap
// I can't believe I had to look this up -_-;
// http://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other
if ((this->x1 > building->x2 ||
building->x1 > this->x2 ||
this->y1 > building->y2 ||
building->y1 > this->y2)) {
continue;
}
// Crap, building overlaps, we need to figure out where it is in the hospital
// NOTE: under some conditions, this generates a false warning. Not a lot I can do about it
// Mark spots used by that building as used; FIXME: handle special logic for traction benches and such
int building_offset_x = building->x1 - this->x1;
int building_offset_y = building->y1 - this->y1;
int building_length = building->x2 - building->x1 + 1;
int building_height = building->y2 - building->y1 + 1;
// Cap the used calculation to only include the part in the hospital
if (this->x1 > building->x1) {
building_offset_x -= (this->x1 - building->x1);
}
if (this->y1 > building->y1) {
building_offset_y -= (building->y1 - this->y1);
}
if ((this->x2 < building->x2) && building_offset_x) {
building_length -= (this->x2 - building->x2) + 1;
}
if ((this->y2 < building->y2) && building_offset_y) {
building_height = (building->y2 - this->y2) + 1;
}
// Quick explination, if a building is north or east of the activity zone,
// we get a negative offset, we'll just skip those lines below. If its
// south or west, we make the building length/height lower to compinsate.
/* if we have a negative x offset, we correct that */
if (building_offset_x < 0) {
building_height += building_offset_x;
building_offset_x = 0;
}
/* Handle negative y offset */
if (building_offset_y < 0) {
building_length += building_offset_y;
building_offset_y = 0;
};
/* Advance the pointer to first row we need to mark */
int spot_cur = 0;
if (building_offset_y) {
spot_cur = (length+1) * building_offset_y;
}
spot_cur += building_offset_x;
/* Start marking! */
for (int i = 0; i != building_height; i++) {
for (int j = 0; j != building_length; j++) {
spots_in_use[spot_cur+j] = true;
}
// Wind the cursor to the start of the next row
spot_cur += length+1;
}
// *phew*, done. Now repeat the process for the next building!
}
}
// Self explanatory
void AnimalHospital::dischargePatient(Patient * patient, color_ostream &out) {
int32_t id = patient->getID();
// Remove them from the hospital
// We can safely iterate here because once we delete the unit
// we no longer use the iterator
for (vector<Patient*>::iterator accepted_patient = this->accepted_patients.begin(); accepted_patient != this->accepted_patients.end(); accepted_patient++) {
if ( (*accepted_patient)->getID() == id) {
out.print("Discharging unit %d from hospital %d\n", id, this->id);
// Reclaim their spot
spots_in_use[(*accepted_patient)->getSpotIndex()] = false;
this->spots_open++;
delete (*accepted_patient);
this->accepted_patients.erase(accepted_patient);
break;
}
}
// And the master list
for (vector<int32_t>::iterator it = tracked_units.begin(); it != tracked_units.end(); it++) {
if ((*it) == id) {
tracked_units.erase(it);
break;
}
}
return;
}
void AnimalHospital::processPatients(color_ostream &out) {
// Where the magic happens
for (vector<Patient*>::iterator patient = this->accepted_patients.begin(); patient != this->accepted_patients.end(); patient++) {
int id = (*patient)->getID();
df::unit * real_unit;
// Appears the health bits can get freed/realloced too -_-;, Find the unit from the main
// index and check it there.
auto units = world->units.all;
for ( size_t a = 0; a < units.size(); a++ ) {
real_unit = units[a];
if (real_unit->id == id) {
break;
}
}
// Check to make sure the unit hasn't expired before assigning a job, or if they've been healed
if (real_unit->flags1.bits.dead || !real_unit->health->flags.bits.needs_healthcare) {
// discharge the patient from the hospital
this->dischargePatient(*patient, out);
return;
}
// Give the unit a job if they don't have any
if (!real_unit->job.current_job) {
// Create REST struct
df::job * job = new df::job;
DFHack::Job::linkIntoWorld(job);
job->pos.x = (*patient)->returnX();
job->pos.y = (*patient)->returnY();
job->pos.z = (*patient)->returnZ();
job->flags.bits.special = 1;
job->job_type = df::enums::job_type::Rest;
df::general_ref *ref = df::allocate<df::general_ref_unit_workerst>();
ref->setID(real_unit->id);
job->general_refs.push_back(ref);
real_unit->job.current_job = job;
job->wait_timer = 1600;
out.print("Telling intelligent unit %d to report to the hospital!\n", real_unit->id);
}
}
}
static vector<AnimalHospital*> animal_hospital_zones;
void delete_animal_hospital_vector(color_ostream &out) {
out.print("Clearing all animal hospitals\n");
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
delete (*animal_hospital);
}
animal_hospital_zones.clear();
}
command_result dwarfvet(color_ostream &out, std::vector <std::string> & parameters);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"dwarfvet",
"Allows animals to be cared for in animal hospitals (activity zones that are animal training + hospital combined).",
dwarfvet,
false, //allow non-interactive use
"dwarfvet enable\n"
" enables animals to use animal hospitals (requires dwarf with Animal Caretaker labor enabled)\n"
"dwarfvet report\n"
" displays all zones dwarfvet considers animal hospitals and their current location on the map\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
bool isActiveAnimalHospital(df::building * building) {
if (Buildings::isHospital(building) && Buildings::isAnimalTraining(building) && Buildings::isActive(building)) {
return true;
}
return false;
}
bool compareAnimalHospitalZones(df::building * hospital1, df::building * hospital2) {
// We compare hospitals by checking if positions are identical, not by ID
// since activity zones can easily be changed in size
if ( hospital1->x1 == hospital2->x1 &&
hospital1->x2 == hospital2->x2 &&
hospital1->y1 == hospital2->y1 &&
hospital1->y2 == hospital2->y2 &&
hospital1->z == hospital1->z) {
return true;
}
return false;
}
void tickHandler(color_ostream& out, void* data) {
if ( !dwarfvet_enabled )
return;
CoreSuspender suspend;
int32_t own_race_id = df::global::ui->race_id;
int32_t own_civ_id = df::global::ui->civ_id;
auto units = world->units.all;
/**
* Generate a list of animal hospitals on the map
*
* Since activity zones can change any instant given user interaction
* we need to be constantly on the lookout for changed zones, and update
* our cached list on the fly if necessary.
**/
vector<df::building*> hospitals_on_map;
// Because C++ iterators suck, we're going to build a temporary vector with the AHZ, and then
// copy it for my own bloody sanity (and compilance with the STL spec)
vector<AnimalHospital*> ahz_scratch;
// Holding area for things to be added to the scratch
vector<df::building*> to_be_added;
// Walk the building tree, and generate a list of animal hospitals on the map
for (size_t b =0 ; b < world->buildings.all.size(); b++) {
df::building* building = world->buildings.all[b];
if (isActiveAnimalHospital(building)) {
hospitals_on_map.push_back(building);
}
}
int count_of_hospitals = hospitals_on_map.size();
int hospitals_cached = animal_hospital_zones.size();
//out.print ("count_of_Hospitals: %d, hospitals_cached: %d\n", count_of_hospitals, hospitals_cached);
// It's possible our hospital cache is empty, if so, simply copy it, and jump to the main logic
if (!hospitals_cached && count_of_hospitals) {
out.print("Populating hospital cache:\n");
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
AnimalHospital * hospital = new AnimalHospital(*current_hospital, out);
out.print(" Found animal hospital %d at x1: %d, y1: %d from valid hospital list\n",
hospital->getID(),
(*current_hospital)->x1,
(*current_hospital)->y1,
(*current_hospital)->z
);
animal_hospital_zones.push_back(hospital);
}
goto processUnits;
}
if (!count_of_hospitals && !hospitals_cached) {
// No hospitals found, delete any cache, and return
delete_animal_hospital_vector(out);
out.print("No hospitals found, plugin sleeping ...\n");
goto cleanup;
}
// Now walk our list of known hospitals, do a bit of checking, then compare
// TODO: this doesn't handle zone resizes at all
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
// If a zone is changed at all, DF seems to reallocate it.
//
// Each AnimalHospital has a "to_be_deleted" bool. We're going to set that to true, and clear it if we can't
// find a matching hospital. This limits the number of times we need to walk through the AHZ list to twice, and
// lets us cleanly report it later
//
// Surviving hospitals will be copied to scratch which will become the new AHZ vector
(*animal_hospital)->to_be_deleted = true;
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
/* Keep the hospital if its still valid */
if ((*animal_hospital)->getID() == (*current_hospital)->id) {
ahz_scratch.push_back(*animal_hospital);
(*animal_hospital)->to_be_deleted = false;
break;
}
}
}
// Report what we're deleting by checking the to_be_deleted bool.
//
// Whatsever left is added to the pending add list
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
if ((*animal_hospital)->to_be_deleted) {
out.print("Hospital #%d removed\n", (*animal_hospital)->getID());
delete *animal_hospital;
}
}
/* Now we need to walk the scratch and add anything that is a hospital and wasn't in the vector */
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
bool new_hospital = true;
for (vector<AnimalHospital*>::iterator animal_hospital = ahz_scratch.begin(); animal_hospital != ahz_scratch.end(); animal_hospital++) {
if ((*animal_hospital)->getID() == (*current_hospital)->id) {
// Next if we're already here
new_hospital = false;
break;
}
}
// Add it if its new
if (new_hospital == true) to_be_added.push_back(*current_hospital);
}
/* Now add it to the scratch AHZ */
for (vector<df::building*>::iterator current_hospital = to_be_added.begin(); current_hospital != to_be_added.end(); current_hospital++) {
// Add it to the vector
out.print("Adding new hospital #id at x1 %d y1: %d z: %d\n",
(*current_hospital)->id,
(*current_hospital)->x1,
(*current_hospital)->y1,
(*current_hospital)->z
);
AnimalHospital * hospital = new AnimalHospital(*current_hospital, out);
ahz_scratch.push_back(hospital);
}
/* Copy the scratch to the AHZ */
animal_hospital_zones = ahz_scratch;
// We always recheck the cache instead of counts because someone might have removed then added a hospital
/* if (hospitals_cached != count_of_hospitals) {
out.print("Hospitals on the map changed, rebuilding cache\n");
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
bool add_hospital = true;
for (vector<AnimalHospital*>::iterator map_hospital = animal_hospital_zones.begin(); map_hospital != animal_hospital_zones.end(); map_hospital++) {
if (compareAnimalHospitalZones(*map_hospital, *current_hospital)) {
// Same hospital, we're good
add_hospital = false;
break;
}
}
// Add it to the list
if (add_hospital) {
out.print("Adding zone at x1: %d, y1: %d to valid hospital list\n", (*current_hospital)->x1, (*current_hospital)->y1);
animal_hospital_zones.push_back(*current_hospital);
}
}
}
*/
processUnits:
/* Code borrowed from petcapRemover.cpp */
for ( size_t a = 0; a < units.size(); a++ ) {
df::unit* unit = units[a];
/* As hilarious as it would be, lets not treat FB :) */
if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) {
continue;
}
if ( !Units::isTamable(unit)) {
continue;
}
/**
* So, for a unit to be elligable for the hospital, all the following must be true
*
* 1. It must be a member of our civilization
* 2. It must be tame (semi-wild counts for this)
* 2.1 If its not a dwarf, AND untame clear its civ out so traps work
* 3. It must have a health struct (which is generated by combat)
* 4. health->needs_healthcare must be set to true
* 5. If health->requires_recovery is set, the creature can't move under its own power
* and a Recover Wounded or Pen/Pasture job MUST be created by hand - TODO
* 6. An open spot in the "Animal Hospital" (activity zone with hospital+animal training set)
* must be available
*
* I apologize if this excessively verbose, but the healthcare system is stupidly conplex
* and there's tons of edgecases to watch out for, and I want someone else to ACTUALLY
* beside me able to understand what's going on
*/
// 1. Make sure its our own civ
if (!Units::isOwnCiv(unit)) {
continue;
}
// 2. Check for tameness
if (unit->training_level == df::animal_training_level::WildUntamed) {
// We don't IMMEDIATELY continue here, if the unit is
// part of our civ, it indiciates it WAS tamed, and reverted
// from SemiWild. Clear its civ flag so it looses TRAPAVOID
//
// Unfortunately, dwarves (or whatever is CIV_SELECTABLE)
// also have a default taming level of WildUntamed so
// check for this case
//
// Furthermore, it MIGHT be a werebeast, so check THAT too
// and exclude those as well.
//
// Finally, this breaks makeown. I might need to write a patch
// to set the tameness of "makeowned" units so dwarfvet can notice
// it
if (unit->race == own_race_id || unit->enemy.normal_race == own_race_id) {
continue;
} else {
unit->civ_id = -1;
out.print ("Clearing civ on unit: %d", unit->id);
}
}
// 3. Check for health struct
if (!unit->health) {
// Unit has not been injured ever; health struct MIA
continue;
}
// 4. Check the healthcare flags
if (unit->health->flags.bits.needs_healthcare) {
/**
* So, for dwarves to care for a unit it must be resting in
* in a hospital zone. Since non-dwarves never take jobs
* this why animal healthcare doesn't work for animals despite
* animal caretaker being coded in DF itself
*
* How a unit gets there is dependent on several factors. If
* a unit can move under its own power, it will take the rest
* job, with a position of a bed in the hospital, then move
* into that bed and fall asleep. This triggers a doctor to
* treat the unit.
*
* If a unit *can't* move, it will set needs_recovery, which
* creates a "Recover Wounded" job in the job list, and then
* create the "Rest" job as listed above. Another dwarf with
* the right labors will go recover the unit, then the above
* logic kicks off.
*
* The necessary flags seem to be properly set for all units
* on the map, so in theory, we just need to make the jobs and
* we're in business, but from a realism POV, I don't think
* non-sentient animals would be smart enough to go to the
* hospital on their own, so instead, we're going to do the following
*
* If a unit CAN_THINK, and can move let it act like a dwarf,
* it will try and find an open spot in the hospital, and if so,
* go there to be treated. In vanilla, the only tamable animal
* with CAN_THINK are Gremlins, so this is actually an edge case
* but its the easiest to code.
*
* TODO: figure out exact logic for non-thinking critters.
*/
// Now we need to find if this unit can be accepted at a hospital
bool awareOfUnit = false;
for (vector<int32_t>::iterator it = tracked_units.begin(); it != tracked_units.end(); it++) {
if ((*it) == unit->id) {
awareOfUnit = true;
}
}
// New unit for dwarfvet to be aware of!
if (!awareOfUnit) {
// The master list handles all patients which are accepted
// Check if this is a unit we're already aware of
bool patient_accepted = false;
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end();) {
if ((*animal_hospital)->acceptPatient(unit->id, out)) {
out.print("Accepted patient %d at hospital %d\n", unit->id, (*animal_hospital)->getID());
patient_accepted = true;
tracked_units.push_back(unit->id);
break;
}
}
}
}
}
// The final step, process all patients!
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
(*animal_hospital)->calculateHospital(true, out);
(*animal_hospital)->processPatients(out);
}
cleanup:
EventManager::unregisterAll(plugin_self);
EventManager::EventHandler handle(tickHandler, howOften);
EventManager::registerTick(handle, howOften, plugin_self);
}
command_result dwarfvet (color_ostream &out, std::vector <std::string> & parameters)
{
CoreSuspender suspend;
for ( size_t a = 0; a < parameters.size(); a++ ) {
if ( parameters[a] == "enable" ) {
out.print("dwarfvet enabled!\n");
dwarfvet_enabled = true;
}
if ( parameters[a] == "disable") {
out.print("dwarvet disabled!\n");
dwarfvet_enabled = false;
}
if ( parameters[a] == "report") {
out.print("Current animal hospitals are:\n");
for (size_t b =0 ; b < world->buildings.all.size(); b++) {
df::building* building = world->buildings.all[b];
if (isActiveAnimalHospital(building)) {
out.print(" at x1: %d, x2%: %d, y1: %d, y2: %d, z: %d\n", building->x1, building->x2, building->y1, building->y2, building->z);
}
}
return CR_OK;
}
if ( parameters[a] == "report-usage") {
out.print("Current animal hospitals are:\n");
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
(*animal_hospital)->calculateHospital(true, out);
(*animal_hospital)->reportUsage(out);
}
return CR_OK;
}
}
if ( !dwarfvet_enabled ) {
return CR_OK;
}
EventManager::unregisterAll(plugin_self);
EventManager::EventHandler handle(tickHandler, howOften);
EventManager::registerTick(handle, howOften, plugin_self);
return CR_OK;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enable && !dwarfvet_enabled) {
dwarfvet_enabled = true;
}
else if (!enable && dwarfvet_enabled) {
delete_animal_hospital_vector(out);
dwarfvet_enabled = false;
}
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event)
{
case DFHack::SC_MAP_LOADED:
break;
case DFHack::SC_MAP_UNLOADED:
delete_animal_hospital_vector(out);
dwarfvet_enabled = false;
break;
default:
break;
}
return CR_OK;
}