Merge remote-tracking branch 'angavrilov/master' into 0.34.11-r4
Conflicts: NEWS library/xml plugins/CMakeLists.txt plugins/autoSyndrome.cppdevelop
						commit
						57fc0f3e89
					
				| @ -0,0 +1,130 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | namespace DFHack { | ||||||
|  | namespace Random { | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * A good explanation: | ||||||
|  |  * http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Interpolation functions | ||||||
|  | 
 | ||||||
|  | template<class T> | ||||||
|  | inline T s_curve(T t) | ||||||
|  | { | ||||||
|  |     // Classical function | ||||||
|  |     //return t * t * (3 - 2*t); | ||||||
|  | 
 | ||||||
|  |     // 2002 version from http://mrl.nyu.edu/~perlin/paper445.pdf | ||||||
|  |     return t * t * t * (t * (t * 6 - 15) + 10); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class T> | ||||||
|  | inline T lerp(T s, T a, T b) | ||||||
|  | { | ||||||
|  |     return a + s * (b-a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Dot product of VSIZE vectors pointed by pa, pb | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned i> | ||||||
|  | struct DotProduct { | ||||||
|  |     static inline T eval(T *pa, T *pb); | ||||||
|  | }; | ||||||
|  | template<class T> | ||||||
|  | struct DotProduct<T,0> { | ||||||
|  |     static inline T eval(T *pa, T *pb) { return pa[0]*pb[0]; } | ||||||
|  | }; | ||||||
|  | template<class T, unsigned i> | ||||||
|  | inline T DotProduct<T,i>::eval(T *pa, T *pb) { | ||||||
|  |     return DotProduct<T,i-1>::eval(pa, pb) + pa[i]*pb[i]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Templates used to force unrolling and inlining of the loops | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | template<unsigned mask> | ||||||
|  | struct PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,-1> { | ||||||
|  |     typedef typename PerlinNoise<T,VSIZE,BITS,IDXT>::Temp Temp; | ||||||
|  |     static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *, const T *, Temp *) {} | ||||||
|  |     static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Initialization of the temporaries from input coordinates | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | template<unsigned mask, int i> | ||||||
|  | inline void PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::setup( | ||||||
|  |     PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt | ||||||
|  | ) { | ||||||
|  |     Impl<mask,i-1>::setup(self, pv, pt); | ||||||
|  | 
 | ||||||
|  |     T t = std::floor(pv[i]); | ||||||
|  |     pt[i].s = s_curve(pt[i].r0 = pv[i] - t); | ||||||
|  | 
 | ||||||
|  |     unsigned b = unsigned(int32_t(t)); | ||||||
|  |     pt[i].b0 = self->idxmap[i][b & mask]; | ||||||
|  |     pt[i].b1 = self->idxmap[i][(b+1) & mask]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Main recursion. Uses tables from self and pt. | ||||||
|  | // Recursion changes current index idx, and current offset vector pq. | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | template<unsigned mask> | ||||||
|  | inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask, -1>::eval( | ||||||
|  |     PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq | ||||||
|  | ) { | ||||||
|  |     return DotProduct<T,VSIZE-1>::eval(pq, self->gradients[idx]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | template<unsigned mask, int i> | ||||||
|  | inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::eval( | ||||||
|  |     PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq | ||||||
|  | ) { | ||||||
|  |     pq[i] = pt[i].r0; | ||||||
|  |     T u = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b0, pq); | ||||||
|  | 
 | ||||||
|  |     pq[i] -= 1; | ||||||
|  |     T v = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b1, pq); | ||||||
|  | 
 | ||||||
|  |     return lerp(pt[i].s, u, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Actual methods of the object | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | void PerlinNoise<T,VSIZE,BITS,IDXT>::init(MersenneRNG &rng) | ||||||
|  | { | ||||||
|  |     STATIC_ASSERT(VSIZE > 0 && BITS <= 8*sizeof(IDXT)); | ||||||
|  | 
 | ||||||
|  |     // Random unit gradient vectors | ||||||
|  |     for (unsigned i = 0; i < TSIZE; i++) | ||||||
|  |         rng.unitvector(gradients[i], VSIZE); | ||||||
|  | 
 | ||||||
|  |     // Random permutation tables | ||||||
|  |     for (unsigned j = 0; j < VSIZE; j++) | ||||||
|  |     { | ||||||
|  |         for (unsigned i = 0; i < TSIZE; i++) | ||||||
|  |             idxmap[j][i] = i; | ||||||
|  | 
 | ||||||
|  |         rng.permute(idxmap[j], TSIZE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class T, unsigned VSIZE, unsigned BITS, class IDXT> | ||||||
|  | T PerlinNoise<T,VSIZE,BITS,IDXT>::eval(const T coords[VSIZE]) | ||||||
|  | { | ||||||
|  |     // Precomputed properties from the coordinates | ||||||
|  |     Temp tmp[VSIZE]; | ||||||
|  |     // Temporary used to build the current offset vector | ||||||
|  |     T q[VSIZE]; | ||||||
|  | 
 | ||||||
|  |     Impl<TSIZE-1,VSIZE-1>::setup(this, coords, tmp); | ||||||
|  | 
 | ||||||
|  |     return Impl<TSIZE-1,VSIZE-1>::eval(this, tmp, 0, q); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }} // namespace | ||||||
| @ -0,0 +1,179 @@ | |||||||
|  | /*
 | ||||||
|  | https://github.com/peterix/dfhack
 | ||||||
|  | Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) | ||||||
|  | 
 | ||||||
|  | This software is provided 'as-is', without any express or implied | ||||||
|  | warranty. In no event will the authors be held liable for any | ||||||
|  | damages arising from the use of this software. | ||||||
|  | 
 | ||||||
|  | Permission is granted to anyone to use this software for any | ||||||
|  | purpose, including commercial applications, and to alter it and | ||||||
|  | redistribute it freely, subject to the following restrictions: | ||||||
|  | 
 | ||||||
|  | 1. The origin of this software must not be misrepresented; you must | ||||||
|  | not claim that you wrote the original software. If you use this | ||||||
|  | software in a product, an acknowledgment in the product documentation | ||||||
|  | would be appreciated but is not required. | ||||||
|  | 
 | ||||||
|  | 2. Altered source versions must be plainly marked as such, and | ||||||
|  | must not be misrepresented as being the original software. | ||||||
|  | 
 | ||||||
|  | 3. This notice may not be removed or altered from any source | ||||||
|  | distribution. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #ifndef CL_MOD_RANDOM | ||||||
|  | #define CL_MOD_RANDOM | ||||||
|  | /**
 | ||||||
|  |  * \defgroup grp_random Random: Random number and noise generation | ||||||
|  |  * @ingroup grp_modules | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "Export.h" | ||||||
|  | #include "Module.h" | ||||||
|  | #include "Types.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | namespace DFHack | ||||||
|  | { | ||||||
|  | namespace Random | ||||||
|  | { | ||||||
|  |     class DFHACK_EXPORT MersenneRNG | ||||||
|  |     { | ||||||
|  |         static const unsigned MT_LEN = 624; | ||||||
|  | 
 | ||||||
|  |         unsigned mt_index; | ||||||
|  |         uint32_t mt_buffer[MT_LEN]; | ||||||
|  | 
 | ||||||
|  |         void twist(); | ||||||
|  |         void prefill(unsigned step, int twist_cnt); | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         /* No constructor or destructor - safe to treat as data */ | ||||||
|  | 
 | ||||||
|  |         void init(const uint32_t *pseed, unsigned cnt, int twist_cnt = 1); | ||||||
|  | 
 | ||||||
|  |         void init(); // uses time
 | ||||||
|  |         void init(uint32_t seed, int twist_cnt = 1) { init(&seed, 1, twist_cnt); } | ||||||
|  | 
 | ||||||
|  |         // [0, 2^32)
 | ||||||
|  |         uint32_t random() { | ||||||
|  |             if (mt_index >= MT_LEN) twist(); | ||||||
|  |             return mt_buffer[mt_index++]; | ||||||
|  |         } | ||||||
|  |         // [0, limit)
 | ||||||
|  |         uint32_t random(uint32_t limit) { | ||||||
|  |             return uint32_t(uint64_t(random())*limit >> 32); | ||||||
|  |         } | ||||||
|  |         // (0, 1)
 | ||||||
|  |         double drandom0() { | ||||||
|  |             return (double(random())+1)/4294967297.0; | ||||||
|  |         } | ||||||
|  |         // [0, 1)
 | ||||||
|  |         double drandom() { | ||||||
|  |             return double(random())/4294967296.0; | ||||||
|  |         } | ||||||
|  |         // [0, 1]
 | ||||||
|  |         double drandom1() { | ||||||
|  |             return double(random())/4294967295.0; | ||||||
|  |         } | ||||||
|  |         // [-1, 1]
 | ||||||
|  |         double unitrandom() { | ||||||
|  |             return drandom1()*2.0 - 1.0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Two exact replicas of functions in DF code
 | ||||||
|  |         int32_t df_trandom(uint32_t max=2147483647LU); | ||||||
|  |         int32_t df_loadtrandom(uint32_t max=2147483647LU); | ||||||
|  | 
 | ||||||
|  |         template<class T> | ||||||
|  |         void unitvector(T *p, int size); | ||||||
|  | 
 | ||||||
|  |         template<class T> | ||||||
|  |         void permute(T *p, int size) { | ||||||
|  |             while(size > 1) | ||||||
|  |             { | ||||||
|  |                 int j = random(size--); | ||||||
|  |                 T c = p[j]; p[j] = p[size]; p[size] = c; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | #ifndef DFHACK_RANDOM_CPP | ||||||
|  |     extern template void MersenneRNG::unitvector<float>(float *p, int size); | ||||||
|  |     extern template void MersenneRNG::unitvector<double>(double *p, int size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     /*
 | ||||||
|  |      * Classical Perlin noise function in template form. | ||||||
|  |      * http://mrl.nyu.edu/~perlin/doc/oscar.html#noise
 | ||||||
|  |      * | ||||||
|  |      * Using an improved hash function from: | ||||||
|  |      * http://www.cs.utah.edu/~aek/research/noise.pdf
 | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     template<class T, unsigned VSIZE, unsigned BITS = 8, class IDXT = uint8_t> | ||||||
|  |     class PerlinNoise | ||||||
|  |     { | ||||||
|  |         // Size of randomness tables
 | ||||||
|  |         static const unsigned TSIZE = 1<<BITS; | ||||||
|  | 
 | ||||||
|  |         T gradients[TSIZE][VSIZE]; | ||||||
|  |         IDXT idxmap[VSIZE][TSIZE]; | ||||||
|  | 
 | ||||||
|  |         // Templates used to unwind and inline recursion and loops
 | ||||||
|  |         struct Temp { | ||||||
|  |             T r0, s; | ||||||
|  |             unsigned b0, b1; | ||||||
|  |         }; | ||||||
|  |         template<unsigned mask, int i> | ||||||
|  |         struct Impl { | ||||||
|  |             static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt); | ||||||
|  |             static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         /* No constructor or destructor - safe to treat as data */ | ||||||
|  | 
 | ||||||
|  |         void init(MersenneRNG &rng); | ||||||
|  | 
 | ||||||
|  |         T eval(const T coords[VSIZE]); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | #ifndef DFHACK_RANDOM_CPP | ||||||
|  |     extern template class DFHACK_IMPORT PerlinNoise<float, 1>; | ||||||
|  |     extern template class DFHACK_IMPORT PerlinNoise<float, 2>; | ||||||
|  |     extern template class DFHACK_IMPORT PerlinNoise<float, 3>; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     template<class T, unsigned BITS = 8, class IDXT = uint8_t> | ||||||
|  |     class PerlinNoise1D : public PerlinNoise<T, 1, BITS, IDXT> | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         T operator() (T x) { return this->eval(&x); } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     template<class T, unsigned BITS = 8, class IDXT = uint8_t> | ||||||
|  |     class PerlinNoise2D : public PerlinNoise<T, 2, BITS, IDXT> | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         T operator() (T x, T y) { | ||||||
|  |             T tmp[2] = { x, y }; | ||||||
|  |             return this->eval(tmp); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     template<class T, unsigned BITS = 8, class IDXT = uint8_t> | ||||||
|  |     class PerlinNoise3D : public PerlinNoise<T, 3, BITS, IDXT> | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         T operator() (T x, T y, T z) { | ||||||
|  |             T tmp[3] = { x, y, z }; | ||||||
|  |             return this->eval(tmp); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -0,0 +1,156 @@ | |||||||
|  | /*
 | ||||||
|  | https://github.com/peterix/dfhack
 | ||||||
|  | Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) | ||||||
|  | 
 | ||||||
|  | This software is provided 'as-is', without any express or implied | ||||||
|  | warranty. In no event will the authors be held liable for any | ||||||
|  | damages arising from the use of this software. | ||||||
|  | 
 | ||||||
|  | Permission is granted to anyone to use this software for any | ||||||
|  | purpose, including commercial applications, and to alter it and | ||||||
|  | redistribute it freely, subject to the following restrictions: | ||||||
|  | 
 | ||||||
|  | 1. The origin of this software must not be misrepresented; you must | ||||||
|  | not claim that you wrote the original software. If you use this | ||||||
|  | software in a product, an acknowledgment in the product documentation | ||||||
|  | would be appreciated but is not required. | ||||||
|  | 
 | ||||||
|  | 2. Altered source versions must be plainly marked as such, and | ||||||
|  | must not be misrepresented as being the original software. | ||||||
|  | 
 | ||||||
|  | 3. This notice may not be removed or altered from any source | ||||||
|  | distribution. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include "Internal.h" | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <map> | ||||||
|  | using namespace std; | ||||||
|  | 
 | ||||||
|  | #define DFHACK_RANDOM_CPP | ||||||
|  | 
 | ||||||
|  | #include "modules/Random.h" | ||||||
|  | #include "VersionInfo.h" | ||||||
|  | #include "MemAccess.h" | ||||||
|  | #include "Types.h" | ||||||
|  | #include "ModuleFactory.h" | ||||||
|  | #include "Core.h" | ||||||
|  | #include "Error.h" | ||||||
|  | #include "VTableInterpose.h" | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | using namespace df::enums; | ||||||
|  | 
 | ||||||
|  | using namespace DFHack::Random; | ||||||
|  | 
 | ||||||
|  | //public domain RNG stuff by Michael Brundage
 | ||||||
|  | //modified to be compatible with the version in DF
 | ||||||
|  | 
 | ||||||
|  | #define MT_IA           397 | ||||||
|  | #define MT_IB           (MT_LEN - MT_IA) | ||||||
|  | #define UPPER_MASK      0x80000000 | ||||||
|  | #define LOWER_MASK      0x7FFFFFFF | ||||||
|  | #define MATRIX_A        0x9908B0DF | ||||||
|  | #define TWIST(b,i,j)    ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) | ||||||
|  | #define MAGIC(s)        (((s)&1)*MATRIX_A) | ||||||
|  | 
 | ||||||
|  | void MersenneRNG::twist() | ||||||
|  | { | ||||||
|  |     uint32_t *b = mt_buffer; | ||||||
|  |     uint32_t s; | ||||||
|  |     unsigned i; | ||||||
|  | 
 | ||||||
|  |     i = 0; | ||||||
|  |     for (; i < MT_IB; i++) { | ||||||
|  |         s = TWIST(b, i, i+1); | ||||||
|  |         b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); | ||||||
|  |     } | ||||||
|  |     for (; i < MT_LEN-1; i++) { | ||||||
|  |         s = TWIST(b, i, i+1); | ||||||
|  |         b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     s = TWIST(b, MT_LEN-1, 0); | ||||||
|  |     b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); | ||||||
|  | 
 | ||||||
|  |     mt_index = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MersenneRNG::prefill(unsigned step, int twist_cnt) | ||||||
|  | { | ||||||
|  |     for(unsigned i=step;i<MT_LEN;i++) | ||||||
|  |     { | ||||||
|  |         //2010: better init line from wikipedia, ultimate source unknown
 | ||||||
|  |         mt_buffer[i]=1812433253UL * (mt_buffer[i-step] ^ (mt_buffer[i-step]>>30)) + i; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     mt_index = 0; | ||||||
|  | 
 | ||||||
|  |     for(int j=0;j<twist_cnt;j++) | ||||||
|  |         twist(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MersenneRNG::init() | ||||||
|  | { | ||||||
|  |     init(Core::getInstance().p->getTickCount(), 20); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MersenneRNG::init(const uint32_t *pseed, unsigned cnt, int twist_cnt) | ||||||
|  | { | ||||||
|  |     memcpy(mt_buffer, pseed, cnt*sizeof(uint32_t)); | ||||||
|  | 
 | ||||||
|  |     prefill(cnt, twist_cnt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t MersenneRNG::df_trandom(uint32_t max) | ||||||
|  | { | ||||||
|  |     if(max<=1)return 0; | ||||||
|  |     uint32_t seed=random(); | ||||||
|  |     seed=seed%2147483647LU; | ||||||
|  |     seed=seed/((2147483647LU/max)+1); | ||||||
|  | 
 | ||||||
|  |     return((int32_t)seed); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t MersenneRNG::df_loadtrandom(uint32_t max) | ||||||
|  | { | ||||||
|  |     uint32_t seed=random(); | ||||||
|  |     seed=seed%max; | ||||||
|  | 
 | ||||||
|  |     return((int32_t)seed); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class T> | ||||||
|  | void MersenneRNG::unitvector(T *p, int size) | ||||||
|  | { | ||||||
|  |     for (;;) | ||||||
|  |     { | ||||||
|  |         T rsqr = 0; | ||||||
|  |         for (int i = 0; i < size; i++) | ||||||
|  |         { | ||||||
|  |             p[i] = (T)unitrandom(); | ||||||
|  |             rsqr += p[i]*p[i]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (rsqr > 0 && rsqr <= 1) | ||||||
|  |         { | ||||||
|  |             rsqr = std::sqrt(rsqr); | ||||||
|  |             for (int i = 0; i < size; i++) | ||||||
|  |                 p[i] /= rsqr; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template void MersenneRNG::unitvector<float>(float *p, int size); | ||||||
|  | template void MersenneRNG::unitvector<double>(double *p, int size); | ||||||
|  | 
 | ||||||
|  | #include "modules/PerlinNoise.inc" | ||||||
|  | 
 | ||||||
|  | template class DFHACK_EXPORT PerlinNoise<float, 1>; | ||||||
|  | template class DFHACK_EXPORT PerlinNoise<float, 2>; | ||||||
|  | template class DFHACK_EXPORT PerlinNoise<float, 3>; | ||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 20ecaa0393df1ea111861d67c789aaaa56a37c58 | Subproject commit e62d498e68e3a87929b144220d03e691016f7aae | ||||||
| @ -0,0 +1,87 @@ | |||||||
|  | // Destroys items being used as part of constructions
 | ||||||
|  | // and flags the constructions to recreate their components upon disassembly
 | ||||||
|  | 
 | ||||||
|  | #include "Core.h" | ||||||
|  | #include "Console.h" | ||||||
|  | #include "Export.h" | ||||||
|  | #include "PluginManager.h" | ||||||
|  | #include "modules/Maps.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | #include "df/item.h" | ||||||
|  | #include "df/world.h" | ||||||
|  | #include "df/construction.h" | ||||||
|  | #include "df/map_block.h" | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace DFHack; | ||||||
|  | 
 | ||||||
|  | using df::global::world; | ||||||
|  | 
 | ||||||
|  | DFHACK_PLUGIN("cleanconst"); | ||||||
|  | 
 | ||||||
|  | command_result df_cleanconst(color_ostream &out, vector <string> & parameters) | ||||||
|  | { | ||||||
|  |     CoreSuspender suspend; | ||||||
|  | 
 | ||||||
|  |     if (!Maps::IsValid()) | ||||||
|  |     { | ||||||
|  |         out.printerr("Map is not available!\n"); | ||||||
|  |         return CR_FAILURE; | ||||||
|  |     } | ||||||
|  |     size_t numItems = world->items.all.size(); | ||||||
|  | 
 | ||||||
|  |     int cleaned_total = 0; | ||||||
|  | 
 | ||||||
|  |     // proceed with the cleanup operation
 | ||||||
|  |     for (size_t i = 0; i < numItems; i++) | ||||||
|  |     { | ||||||
|  |         df::item *item = world->items.all[i]; | ||||||
|  |         // only process items marked as "in construction"
 | ||||||
|  |         if (!item->flags.bits.construction) | ||||||
|  |             continue; | ||||||
|  |         df::coord pos(item->pos.x, item->pos.y, item->pos.z); | ||||||
|  | 
 | ||||||
|  |         df::construction *cons = df::construction::find(pos); | ||||||
|  |         if (!cons) | ||||||
|  |         { | ||||||
|  |             out.printerr("Item at %i,%i,%i marked as construction but no construction is present!\n", pos.x, pos.y, pos.z); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // if the construction is already labeled as "no build item", then leave it alone
 | ||||||
|  |         if (cons->flags.bits.no_build_item) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         // only destroy the item if the construction claims to be made of the exact same thing
 | ||||||
|  |         if (item->getType() != cons->item_type || | ||||||
|  |             item->getSubtype() != cons->item_subtype || | ||||||
|  |             item->getMaterial() != cons->mat_type || | ||||||
|  |             item->getMaterialIndex() != cons->mat_index) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         item->flags.bits.garbage_collect = 1; | ||||||
|  |         cons->flags.bits.no_build_item = 1; | ||||||
|  | 
 | ||||||
|  |         cleaned_total++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     out.print("Done. %d construction items cleaned up.\n", cleaned_total); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands) | ||||||
|  | { | ||||||
|  |     commands.push_back(PluginCommand( | ||||||
|  |         "cleanconst", "Cleans up construction materials.", | ||||||
|  |         df_cleanconst, false, | ||||||
|  |         "  This utility alters all constructions on the map so that they spawn their\n" | ||||||
|  |         "  building component when they are disassembled, allowing their actual\n" | ||||||
|  |         "  build items to be safely deleted.\n" | ||||||
|  |     )); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_shutdown ( color_ostream &out ) | ||||||
|  | { | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
| @ -0,0 +1,345 @@ | |||||||
|  | -- Exports an ini file for Dwarf Therapist. | ||||||
|  | 
 | ||||||
|  | local utils = require 'utils' | ||||||
|  | local ms = require 'memscan' | ||||||
|  | 
 | ||||||
|  | -- Utility functions | ||||||
|  | 
 | ||||||
|  | local globals = df.global | ||||||
|  | local global_addr = dfhack.internal.getAddress | ||||||
|  | local os_type = dfhack.getOSType() | ||||||
|  | local rdelta = dfhack.internal.getRebaseDelta() | ||||||
|  | 
 | ||||||
|  | local vbias = 0 | ||||||
|  | if os_type == 'windows' then vbias = -4 end | ||||||
|  | 
 | ||||||
|  | local lines = {} | ||||||
|  | local complete = true | ||||||
|  | 
 | ||||||
|  | local function header(name) | ||||||
|  |     table.insert(lines, '') | ||||||
|  |     table.insert(lines, '['..name..']') | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function value(name,addr) | ||||||
|  |     local line | ||||||
|  | 
 | ||||||
|  |     if not addr then | ||||||
|  |         complete = false | ||||||
|  |         line = name..'=0x0' | ||||||
|  |     elseif addr < 0x10000 then | ||||||
|  |         line = string.format('%s=0x%04x',name,addr) | ||||||
|  |     else | ||||||
|  |         line = string.format('%s=0x%08x',name,addr) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     table.insert(lines, line) | ||||||
|  | end | ||||||
|  | local function address(name,bias,base,field,...) | ||||||
|  |     local addr | ||||||
|  | 
 | ||||||
|  |     if base == globals then | ||||||
|  |         addr = global_addr(field) | ||||||
|  |         bias = bias - rdelta | ||||||
|  |         if addr and select('#',...) > 0 then | ||||||
|  |             _,addr = df.sizeof(ms.field_ref(base,field,...)) | ||||||
|  |         end | ||||||
|  |     elseif base._kind == 'class-type' then | ||||||
|  |         -- field_offset crashes with classes due to vtable problems, | ||||||
|  |         -- so we have to create a real temporary object here. | ||||||
|  |         local obj = df.new(base) | ||||||
|  |         if obj then | ||||||
|  |             local _,a1 = df.sizeof(obj) | ||||||
|  |             local _,a2 = df.sizeof(ms.field_ref(obj,field,...)) | ||||||
|  |             addr = a2-a1 | ||||||
|  |             obj:delete() | ||||||
|  |         end | ||||||
|  |     else | ||||||
|  |         addr = ms.field_offset(base,field,...) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     if addr then | ||||||
|  |         addr = addr + bias | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     value(name, addr) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function offset(name,base,...) | ||||||
|  |     address(name,0,base,...) | ||||||
|  | end | ||||||
|  | local function vector(name,base,...) | ||||||
|  |     address(name,vbias,base,...) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | -- List of actual values | ||||||
|  | 
 | ||||||
|  | header('addresses') | ||||||
|  | vector('translation_vector',globals,'world','raws','language','translations') | ||||||
|  | vector('language_vector',globals,'world','raws','language','words') | ||||||
|  | vector('creature_vector',globals,'world','units','all') | ||||||
|  | vector('active_creature_vector',globals,'world','units','active') | ||||||
|  | offset('dwarf_race_index',globals,'ui','race_id') | ||||||
|  | vector('squad_vector',globals,'world','squads','all') | ||||||
|  | offset('current_year',globals,'cur_year') | ||||||
|  | offset('cur_year_tick',globals,'cur_year_tick') | ||||||
|  | offset('dwarf_civ_index',globals,'ui','civ_id') | ||||||
|  | vector('races_vector',globals,'world','raws','creatures','all') | ||||||
|  | vector('reactions_vector',globals,'world','raws','reactions') | ||||||
|  | vector('historical_figures',globals,'world','history','figures') | ||||||
|  | vector('fake_identities',globals,'world','assumed_identities','all') | ||||||
|  | vector('historical_figures_vector',globals,'world','history','figures') | ||||||
|  | vector('fake_identities_vector',globals,'world','assumed_identities','all') | ||||||
|  | offset('fortress_entity',globals,'ui','main','fortress_entity') | ||||||
|  | vector('historical_entities_vector',globals,'world','entities','all') | ||||||
|  | vector('weapons_vector',globals,'world','raws','itemdefs','weapons') | ||||||
|  | vector('trap_vector',globals,'world','raws','itemdefs','trapcomps') | ||||||
|  | vector('toy_vector',globals,'world','raws','itemdefs','toys') | ||||||
|  | vector('tool_vector',globals,'world','raws','itemdefs','tools') | ||||||
|  | vector('instrument_vector',globals,'world','raws','itemdefs','instruments') | ||||||
|  | vector('armor_vector',globals,'world','raws','itemdefs','armor') | ||||||
|  | vector('ammo_vector',globals,'world','raws','itemdefs','ammo') | ||||||
|  | vector('siegeammo_vector',globals,'world','raws','itemdefs','siege_ammo') | ||||||
|  | vector('glove_vector',globals,'world','raws','itemdefs','gloves') | ||||||
|  | vector('shoe_vector',globals,'world','raws','itemdefs','shoes') | ||||||
|  | vector('shield_vector',globals,'world','raws','itemdefs','shields') | ||||||
|  | vector('helm_vector',globals,'world','raws','itemdefs','helms') | ||||||
|  | vector('pant_vector',globals,'world','raws','itemdefs','pants') | ||||||
|  | vector('food_vector',globals,'world','raws','itemdefs','food') | ||||||
|  | vector('colors_vector',globals,'world','raws','language','colors') | ||||||
|  | vector('shapes_vector',globals,'world','raws','language','shapes') | ||||||
|  | offset('base_materials',globals,'world','raws','mat_table','builtin') | ||||||
|  | vector('inorganics_vector',globals,'world','raws','inorganics') | ||||||
|  | vector('plants_vector',globals,'world','raws','plants','all') | ||||||
|  | vector('material_templates_vector',globals,'world','raws','material_templates') | ||||||
|  | offset('world_data',globals,'world','world_data') | ||||||
|  | vector('active_sites_vector',df.world_data,'active_site') | ||||||
|  | offset('world_site_type',df.world_site,'type') | ||||||
|  | 
 | ||||||
|  | header('offsets') | ||||||
|  | offset('word_table',df.language_translation,'words') | ||||||
|  | value('string_buffer_offset', 0x0000) | ||||||
|  | 
 | ||||||
|  | header('word_offsets') | ||||||
|  | offset('base',df.language_word,'word') | ||||||
|  | offset('noun_singular',df.language_word,'forms','Noun') | ||||||
|  | offset('noun_plural',df.language_word,'forms','NounPlural') | ||||||
|  | offset('adjective',df.language_word,'forms','Adjective') | ||||||
|  | offset('verb',df.language_word,'forms','Verb') | ||||||
|  | offset('present_simple_verb',df.language_word,'forms','Verb3rdPerson') | ||||||
|  | offset('past_simple_verb',df.language_word,'forms','VerbPast') | ||||||
|  | offset('past_participle_verb',df.language_word,'forms','VerbPassive') | ||||||
|  | offset('present_participle_verb',df.language_word,'forms','VerbGerund') | ||||||
|  | offset('words',df.language_name,'words') | ||||||
|  | offset('word_type',df.language_name,'parts_of_speech') | ||||||
|  | offset('language_id',df.language_name,'language') | ||||||
|  | 
 | ||||||
|  | header('race_offsets') | ||||||
|  | offset('name_singular',df.creature_raw,'name',0) | ||||||
|  | offset('name_plural',df.creature_raw,'name',1) | ||||||
|  | offset('adjective',df.creature_raw,'name',2) | ||||||
|  | offset('baby_name_singular',df.creature_raw,'general_baby_name',0) | ||||||
|  | offset('baby_name_plural',df.creature_raw,'general_baby_name',1) | ||||||
|  | offset('child_name_singular',df.creature_raw,'general_child_name',0) | ||||||
|  | offset('child_name_plural',df.creature_raw,'general_child_name',1) | ||||||
|  | vector('pref_string_vector',df.creature_raw,'prefstring') | ||||||
|  | vector('castes_vector',df.creature_raw,'caste') | ||||||
|  | vector('pop_ratio_vector',df.creature_raw,'pop_ratio') | ||||||
|  | vector('materials_vector',df.creature_raw,'material') | ||||||
|  | offset('flags',df.creature_raw,'flags') | ||||||
|  | 
 | ||||||
|  | header('caste_offsets') | ||||||
|  | offset('caste_name',df.caste_raw,'caste_name') | ||||||
|  | offset('caste_descr',df.caste_raw,'description') | ||||||
|  | offset('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') | ||||||
|  | offset('caste_ment_att_ranges',df.caste_raw,'attributes','ment_att_range') | ||||||
|  | offset('adult_size',df.caste_raw,'misc','adult_size') | ||||||
|  | offset('flags',df.caste_raw,'flags') | ||||||
|  | vector('extracts',df.caste_raw,'extracts','extract_matidx') | ||||||
|  | offset('skill_rates',df.caste_raw,'skill_rates') | ||||||
|  | offset('caste_att_rates',df.caste_raw,'attributes','phys_att_rates') | ||||||
|  | offset('caste_att_caps',df.caste_raw,'attributes','phys_att_cap_perc') | ||||||
|  | 
 | ||||||
|  | header('hist_entity_offsets') | ||||||
|  | vector('squads',df.historical_entity,'squads') | ||||||
|  | vector('positions',df.historical_entity,'positions','own') | ||||||
|  | vector('assignments',df.historical_entity,'positions','assignments') | ||||||
|  | offset('assign_hist_id',df.entity_position_assignment,'histfig') | ||||||
|  | offset('assign_position_id',df.entity_position_assignment,'position_id') | ||||||
|  | offset('position_id',df.entity_position,'id') | ||||||
|  | offset('position_name',df.entity_position,'name') | ||||||
|  | offset('position_female_name',df.entity_position,'name_female') | ||||||
|  | offset('position_male_name',df.entity_position,'name_male') | ||||||
|  | 
 | ||||||
|  | header('hist_figure_offsets') | ||||||
|  | offset('hist_race',df.historical_figure,'race') | ||||||
|  | offset('hist_name',df.historical_figure,'name') | ||||||
|  | offset('id',df.historical_figure,'id') | ||||||
|  | offset('hist_fig_info',df.historical_figure,'info') | ||||||
|  | offset('reputation',df.historical_figure_info,'reputation') | ||||||
|  | offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity') | ||||||
|  | offset('fake_name',df.assumed_identity,'name') | ||||||
|  | offset('fake_birth_year',df.assumed_identity,'birth_year') | ||||||
|  | offset('fake_birth_time',df.assumed_identity,'birth_second') | ||||||
|  | 
 | ||||||
|  | header('weapon_offsets') | ||||||
|  | offset('name_plural',df.itemdef_weaponst,'name_plural') | ||||||
|  | offset('single_size',df.itemdef_weaponst,'two_handed') | ||||||
|  | offset('multi_size',df.itemdef_weaponst,'minimum_size') | ||||||
|  | offset('ammo',df.itemdef_weaponst,'ranged_ammo') | ||||||
|  | 
 | ||||||
|  | header('material_offsets') | ||||||
|  | offset('solid_name',df.material_common,'state_name','Solid') | ||||||
|  | offset('liquid_name',df.material_common,'state_name','Liquid') | ||||||
|  | offset('gas_name',df.material_common,'state_name','Gas') | ||||||
|  | offset('powder_name',df.material_common,'state_name','Powder') | ||||||
|  | offset('paste_name',df.material_common,'state_name','Paste') | ||||||
|  | offset('pressed_name',df.material_common,'state_name','Pressed') | ||||||
|  | offset('inorganic_materials_vector',df.inorganic_raw,'material') | ||||||
|  | offset('flags',df.material_common,'flags') | ||||||
|  | 
 | ||||||
|  | header('plant_offsets') | ||||||
|  | offset('name',df.plant_raw,'name') | ||||||
|  | offset('name_plural',df.plant_raw,'name_plural') | ||||||
|  | offset('name_leaf_plural',df.plant_raw,'leaves_plural') | ||||||
|  | offset('name_seed_plural',df.plant_raw,'seed_plural') | ||||||
|  | vector('materials_vector',df.plant_raw,'material') | ||||||
|  | offset('flags',df.plant_raw,'flags') | ||||||
|  | 
 | ||||||
|  | header('item_offsets') | ||||||
|  | offset('name_plural',df.itemdef_armorst,'name_plural') | ||||||
|  | offset('adjective',df.itemdef_armorst,'name_preplural') | ||||||
|  | offset('mat_name',df.itemdef_armorst,'material_placeholder') | ||||||
|  | 
 | ||||||
|  | header('descriptor_offsets') | ||||||
|  | offset('color_name',df.descriptor_color,'name') | ||||||
|  | offset('shape_name_plural',df.descriptor_shape,'name_plural') | ||||||
|  | 
 | ||||||
|  | header('dwarf_offsets') | ||||||
|  | offset('first_name',df.unit,'name','first_name') | ||||||
|  | offset('nick_name',df.unit,'name','nickname') | ||||||
|  | offset('last_name',df.unit,'name','words') | ||||||
|  | offset('custom_profession',df.unit,'custom_profession') | ||||||
|  | offset('profession',df.unit,'profession') | ||||||
|  | offset('race',df.unit,'race') | ||||||
|  | offset('flags1',df.unit,'flags1') | ||||||
|  | offset('flags2',df.unit,'flags2') | ||||||
|  | offset('flags3',df.unit,'flags3') | ||||||
|  | offset('caste',df.unit,'caste') | ||||||
|  | offset('sex',df.unit,'sex') | ||||||
|  | offset('id',df.unit,'id') | ||||||
|  | offset('animal_type',df.unit,'training_level') | ||||||
|  | offset('civ',df.unit,'civ_id') | ||||||
|  | vector('specific_refs',df.unit,'specific_refs') | ||||||
|  | offset('squad_id',df.unit,'military','squad_id') | ||||||
|  | offset('squad_position',df.unit,'military','squad_position') | ||||||
|  | offset('recheck_equipment',df.unit,'military','pickup_flags') | ||||||
|  | offset('mood',df.unit,'mood') | ||||||
|  | offset('birth_year',df.unit,'relations','birth_year') | ||||||
|  | offset('birth_time',df.unit,'relations','birth_time') | ||||||
|  | offset('current_job',df.unit,'job','current_job') | ||||||
|  | offset('physical_attrs',df.unit,'body','physical_attrs') | ||||||
|  | vector('body_size',df.unit,'appearance','body_modifiers') | ||||||
|  | offset('curse',df.unit,'curse','name') | ||||||
|  | offset('curse_add_flags1',df.unit,'curse','add_tags1') | ||||||
|  | offset('turn_count',df.unit,'curse','time_on_site') | ||||||
|  | vector('souls',df.unit,'status','souls') | ||||||
|  | vector('states',df.unit,'status','misc_traits') | ||||||
|  | offset('labors',df.unit,'status','labors') | ||||||
|  | offset('happiness',df.unit,'status','happiness') | ||||||
|  | vector('thoughts',df.unit,'status','recent_events') | ||||||
|  | offset('squad_ref_id',df.unit,'hist_figure_id') | ||||||
|  | offset('hist_id',df.unit,'hist_figure_id') | ||||||
|  | offset('artifact_name',df.unit,'status','artifact_name') | ||||||
|  | 
 | ||||||
|  | header('soul_details') | ||||||
|  | offset('name',df.unit_soul,'name') | ||||||
|  | offset('mental_attrs',df.unit_soul,'mental_attrs') | ||||||
|  | vector('skills',df.unit_soul,'skills') | ||||||
|  | offset('traits',df.unit_soul,'traits') | ||||||
|  | vector('preferences',df.unit_soul,'preferences') | ||||||
|  | 
 | ||||||
|  | header('job_details') | ||||||
|  | offset('id',df.job,'job_type') | ||||||
|  | value('on_break_flag',df.misc_trait_type.OnBreak) | ||||||
|  | offset('sub_job_id',df.job,'reaction_name') | ||||||
|  | offset('reaction',df.reaction,'name') | ||||||
|  | offset('reaction_skill',df.reaction,'skill') | ||||||
|  | offset('mat_type',df.job,'mat_type') | ||||||
|  | offset('mat_index',df.job,'mat_index') | ||||||
|  | offset('mat_category',df.job,'material_category') | ||||||
|  | 
 | ||||||
|  | header('squad_offsets') | ||||||
|  | offset('id',df.squad,'id') | ||||||
|  | offset('name',df.squad,'name') | ||||||
|  | offset('name_old',df.squad,'name','words') | ||||||
|  | offset('alias',df.squad,'alias') | ||||||
|  | vector('members',df.squad,'positions') | ||||||
|  | 
 | ||||||
|  | -- Final creation of the file | ||||||
|  | 
 | ||||||
|  | local out = io.open('therapist.ini', 'w') | ||||||
|  | 
 | ||||||
|  | out:write('[info]\n') | ||||||
|  | -- TODO: add an api function to retrieve the checksum | ||||||
|  | out:write('checksum=<<fillme>>\n') | ||||||
|  | out:write('version_name='..dfhack.getDFVersion()..'\n') | ||||||
|  | out:write('complete='..(complete and 'true' or 'false')..'\n') | ||||||
|  | 
 | ||||||
|  | for i,v in ipairs(lines) do | ||||||
|  |     out:write(v..'\n') | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | out:write[[ | ||||||
|  | 
 | ||||||
|  | [valid_flags_1] | ||||||
|  | size=1 | ||||||
|  | 1\name=Not from around these parts | ||||||
|  | 1\value=0x80000000 | ||||||
|  | 
 | ||||||
|  | [valid_flags_2] | ||||||
|  | size=0 | ||||||
|  | 
 | ||||||
|  | [invalid_flags_1] | ||||||
|  | size=10 | ||||||
|  | 1\name=a zombie | ||||||
|  | 1\value=0x00001000 | ||||||
|  | 2\name=a skeleton | ||||||
|  | 2\value=0x00002000 | ||||||
|  | 3\name=a merchant or diplomat | ||||||
|  | 3\value=0x00000040 | ||||||
|  | 4\name=outpost liason | ||||||
|  | 4\value=0x00000800 | ||||||
|  | 5\name=an invader or hostile | ||||||
|  | 5\value=0x00020000 | ||||||
|  | 6\name=an invader or hostile | ||||||
|  | 6\value=0x00080000 | ||||||
|  | 7\name=an invader or hostile | ||||||
|  | 7\value=0x000C0000 | ||||||
|  | 8\name=a merchant escort | ||||||
|  | 8\value=0x00000080 | ||||||
|  | 9\name="Dead, Jim." | ||||||
|  | 9\value=0x00000002 | ||||||
|  | 10\name=marauder | ||||||
|  | 10\value=0x00000010 | ||||||
|  | 
 | ||||||
|  | [invalid_flags_2] | ||||||
|  | size=5 | ||||||
|  | 1\name="killed, Jim." | ||||||
|  | 1\value=0x00000080 | ||||||
|  | 2\name=from the Underworld. SPOOKY! | ||||||
|  | 2\value=0x00040000 | ||||||
|  | 3\name=resident | ||||||
|  | 3\value=0x00080000 | ||||||
|  | 4\name=visitor_uninvited | ||||||
|  | 4\value=0x00400000 | ||||||
|  | 5\name=visitor | ||||||
|  | 5\value=0x00800000 | ||||||
|  | 
 | ||||||
|  | [invalid_flags_3] | ||||||
|  | size=1 | ||||||
|  | 1\name=a ghost | ||||||
|  | 1\value=0x00001000 | ||||||
|  | ]] | ||||||
|  | 
 | ||||||
|  | out:close() | ||||||
| @ -0,0 +1,132 @@ | |||||||
|  | -- Generates an image using multiple octaves of perlin noise. | ||||||
|  | 
 | ||||||
|  | local args = {...} | ||||||
|  | local rng = dfhack.random.new(3) | ||||||
|  | 
 | ||||||
|  | if #args < 3 then | ||||||
|  |     qerror('Usage: devel/test-perlin <fname.pgm> <density> <coeff|expr...x[...y[...z]]>...') | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local fname = table.remove(args,1) | ||||||
|  | local goal = tonumber(table.remove(args,1)) or qerror('Invalid density') | ||||||
|  | 
 | ||||||
|  | local zscale = 6 | ||||||
|  | 
 | ||||||
|  | local coeffs = {} | ||||||
|  | local fns = {} | ||||||
|  | 
 | ||||||
|  | local env = copyall(math) | ||||||
|  | env.apow = function(x,y) return math.pow(math.abs(x),y) end | ||||||
|  | 
 | ||||||
|  | for i = 1,#args do | ||||||
|  |     local fn = rng:perlin(3); | ||||||
|  |     local fn2 = rng:perlin(3); | ||||||
|  |     local fn3 = rng:perlin(3); | ||||||
|  |     local fn4 = rng:perlin(3); | ||||||
|  | 
 | ||||||
|  |     fns[i] = fn; | ||||||
|  | 
 | ||||||
|  |     local val = string.match(args[i],'^([+-]?[%d.]+)$') | ||||||
|  |     if val then | ||||||
|  |         coeffs[i] = function(x) return x * val end | ||||||
|  |     else | ||||||
|  |         local argstr = 'x' | ||||||
|  |         if string.match(args[i], '%f[%w]w%f[^%w]') then | ||||||
|  |             argstr = 'x,y,z,w' | ||||||
|  |             fns[i] = function(x,y,z) | ||||||
|  |                 return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5), fn4(x+0.5,y+0.5,z+0.5) | ||||||
|  |             end | ||||||
|  |         elseif string.match(args[i], '%f[%w]z%f[^%w]') then | ||||||
|  |             argstr = 'x,y,z' | ||||||
|  |             fns[i] = function(x,y,z) | ||||||
|  |                 return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5) | ||||||
|  |             end | ||||||
|  |         elseif string.match(args[i], '%f[%w]y%f[^%w]') then | ||||||
|  |             argstr = 'x,y' | ||||||
|  |             fns[i] = function(x,y,z) | ||||||
|  |                 return fn(x,y,z), fn2(x,y,z) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         local f,err = load( | ||||||
|  |             'return function('..argstr..') return ('..args[i]..') end', | ||||||
|  |             '=(expr)', 't', env | ||||||
|  |         ) | ||||||
|  |         if not f then | ||||||
|  |             qerror(err) | ||||||
|  |         end | ||||||
|  |         coeffs[i] = f() | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function render(thresh,file) | ||||||
|  |     local area = 0 | ||||||
|  |     local line, arr = '', {} | ||||||
|  | 
 | ||||||
|  |     for zy = 0,1 do | ||||||
|  |         for y = 0,48*4-1 do | ||||||
|  |             line = '' | ||||||
|  |             for zx = 0,1 do | ||||||
|  |                 for x = 0,48*4-1 do | ||||||
|  |                     local tx = (0.5+x)/(48*2) | ||||||
|  |                     local ty = (0.5+y)/(48*2) | ||||||
|  |                     local tz = 0.5+(zx+zy*2)/(48*2/zscale) | ||||||
|  |                     local v1 = 0 | ||||||
|  |                     for i = 1,#coeffs do | ||||||
|  |                         v1 = v1 + coeffs[i](fns[i](tx,ty,tz)) | ||||||
|  |                         tx = tx*2 | ||||||
|  |                         ty = ty*2 | ||||||
|  |                         tz = tz*2 | ||||||
|  |                     end | ||||||
|  |                     local v = -1 | ||||||
|  |                     if v1 > thresh then | ||||||
|  |                         v = v1; | ||||||
|  |                         area = area + 1 | ||||||
|  |                     end | ||||||
|  |                     if file then | ||||||
|  |                         local c = math.max(0, math.min(255, v * 127 + 128)) | ||||||
|  |                         arr[2*x+1] = c | ||||||
|  |                         arr[2*x+2] = c | ||||||
|  |                     end | ||||||
|  |                 end | ||||||
|  |                 if file then | ||||||
|  |                     line = line..string.char(table.unpack(arr)) | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |             if file then | ||||||
|  |                 file:write(line,line) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     return area/4/(48*4)/(48*4) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function search(fn,min,max,goal,eps) | ||||||
|  |     while true do | ||||||
|  |         local center = (max+min)/2 | ||||||
|  |         local cval = fn(center) | ||||||
|  |         print('At '..center..': '..cval) | ||||||
|  |         if math.abs(cval-goal) < eps then | ||||||
|  |             return center | ||||||
|  |         end | ||||||
|  |         if cval > goal then | ||||||
|  |             min = center | ||||||
|  |         else | ||||||
|  |             max = center | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local thresh = search(render, -1, 1, goal, 0.001) | ||||||
|  | 
 | ||||||
|  | local file,err = io.open(fname, 'wb') | ||||||
|  | if not file then | ||||||
|  |     print('error: ',err) | ||||||
|  |     return | ||||||
|  | end | ||||||
|  | file:write('P5\n768 768\n255\n') | ||||||
|  | local area = render(thresh, file) | ||||||
|  | file:close() | ||||||
|  | 
 | ||||||
|  | print('Area fraction: '..area) | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # patch start dwarf count | ||||||
|  | 
 | ||||||
|  | nr = $script_args[0].to_i | ||||||
|  | 
 | ||||||
|  | raise 'too low' if nr < 7 | ||||||
|  | 
 | ||||||
|  | addr = df.get_global_address('start_dwarf_count') | ||||||
|  | df.memory_patch(addr, [nr].pack('L')) | ||||||
|  | 
 | ||||||
		Loading…
	
		Reference in New Issue