Merge branch 'develop' of https://github.com/DFHack/dfhack
commit
ae2e4f99eb
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,123 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This implements steps 7 and 8 of the OSX compilation procedure described in Compile.rst
|
||||||
|
# If build-osx does not exist in the parent directory, it will be created.
|
||||||
|
|
||||||
|
LUA_PATCH=1
|
||||||
|
ME=$PWD/`basename $0`
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [options] {DF_OSX_PATH}"
|
||||||
|
echo -e "\told\t- use on pre-Snow Leopard OSX installations"
|
||||||
|
echo -e "\tbrew\t- if GCC 4.5 was installed with homebrew"
|
||||||
|
echo -e "\tport\t- if GCC 4.5 was insalled with macports"
|
||||||
|
echo -e "\tclean\t- delete ../build-osx before compiling"
|
||||||
|
echo "Example:"
|
||||||
|
echo -e "\t$0 old brew ../../../personal/df_osx"
|
||||||
|
echo -e "\t$0 port clean /Users/dfplayer/df_osx"
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
options() {
|
||||||
|
case $1 in
|
||||||
|
brew)
|
||||||
|
echo "Using homebrew gcc."
|
||||||
|
export CC=/usr/local/bin/gcc-4.5
|
||||||
|
export CXX=/usr/local/bin/g++-4.5
|
||||||
|
targetted=1
|
||||||
|
;;
|
||||||
|
port)
|
||||||
|
echo "Using macports gcc."
|
||||||
|
export CC=/opt/local/bin/gcc-mp-4.5
|
||||||
|
export CXX=/opt/local/bin/g++-mp-4.5
|
||||||
|
targetted=1
|
||||||
|
;;
|
||||||
|
old)
|
||||||
|
LUA_PATCH=0
|
||||||
|
;;
|
||||||
|
clean)
|
||||||
|
echo "Deleting ../build-osx"
|
||||||
|
rm -rf ../build-osx
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# sanity checks
|
||||||
|
if [[ $# -lt 1 ]]
|
||||||
|
then
|
||||||
|
echo "Not enough arguments."
|
||||||
|
usage 0
|
||||||
|
fi
|
||||||
|
if [[ $# -gt 4 ]]
|
||||||
|
then
|
||||||
|
echo "Too many arguments."
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run through the arguments
|
||||||
|
for last
|
||||||
|
do
|
||||||
|
options $last
|
||||||
|
done
|
||||||
|
# last keeps the last argument
|
||||||
|
|
||||||
|
if [[ $targetted -eq 0 ]]
|
||||||
|
then
|
||||||
|
echo "You did not specify whether you intalled GCC 4.5 from brew or ports."
|
||||||
|
echo "If you continue, your default compiler will be used."
|
||||||
|
read -p "Are you sure you want to continue? [y/N] " -n 1 -r
|
||||||
|
echo # (optional) move to a new line
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]
|
||||||
|
then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check for build folder and start working there
|
||||||
|
if [[ ! -d ../build-osx ]]
|
||||||
|
then
|
||||||
|
mkdir ../build-osx
|
||||||
|
fi
|
||||||
|
cd ../build-osx
|
||||||
|
|
||||||
|
# patch if necessary
|
||||||
|
if [[ $LUA_PATCH -ne 0 ]]
|
||||||
|
then
|
||||||
|
cd ..
|
||||||
|
echo "$PWD"
|
||||||
|
sed -e '1,/'"PATCH""CODE"'/d' "$ME" | patch -p0
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Generate"
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX="$last"
|
||||||
|
echo "Build"
|
||||||
|
make
|
||||||
|
echo "Install"
|
||||||
|
make install
|
||||||
|
|
||||||
|
# unpatch if /libarary/luaTypes.cpp was patched
|
||||||
|
if [[ $LUA_PATCH -ne 0 ]]
|
||||||
|
then
|
||||||
|
cd ..
|
||||||
|
echo -n "un"
|
||||||
|
sed -e '1,/'"PATCH""CODE"'/d' "$ME" | patch -p0 -R
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# PATCHCODE - everything below this line is fed into patch
|
||||||
|
--- library/LuaTypes.cpp 2014-08-20 00:13:17.000000000 -0700
|
||||||
|
+++ library/LuaTypes.cpp 2014-08-31 23:31:00.000000000 -0700
|
||||||
|
@@ -464,7 +464,7 @@
|
||||||
|
{
|
||||||
|
case struct_field_info::STATIC_STRING:
|
||||||
|
{
|
||||||
|
- int len = strnlen((char*)ptr, field->count);
|
||||||
|
+ int len = strlen((char*)ptr);
|
||||||
|
lua_pushlstring(state, (char*)ptr, len);
|
||||||
|
return;
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "DFHack.h"
|
|
||||||
#include "PluginManager.h"
|
|
||||||
#include "Hooks.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Plugin loading functions
|
|
||||||
*/
|
|
||||||
namespace DFHack
|
|
||||||
{
|
|
||||||
DFLibrary * OpenPlugin (const char * filename)
|
|
||||||
{
|
|
||||||
dlerror();
|
|
||||||
DFLibrary * ret = (DFLibrary *) dlopen(filename, RTLD_NOW);
|
|
||||||
if(!ret)
|
|
||||||
{
|
|
||||||
std::cerr << dlerror() << std::endl;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
void * LookupPlugin (DFLibrary * plugin ,const char * function)
|
|
||||||
{
|
|
||||||
return (DFLibrary *) dlsym((void *)plugin, function);
|
|
||||||
}
|
|
||||||
void ClosePlugin (DFLibrary * plugin)
|
|
||||||
{
|
|
||||||
dlclose((void *) plugin);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
Subproject commit 55b60e3aa99aece7e9a239b041dc4f95a58dcef3
|
Subproject commit 20014d2f501b76b697e594d56e594ea8f143ea24
|
@ -1,543 +0,0 @@
|
|||||||
#ifndef SEGMENTED_FINDER_H
|
|
||||||
#define SEGMENTED_FINDER_H
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <iosfwd>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
class SegmentedFinder;
|
|
||||||
class SegmentFinder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SegmentFinder(DFHack::t_memrange & mr, DFHack::Context * DF, SegmentedFinder * SF)
|
|
||||||
{
|
|
||||||
_DF = DF;
|
|
||||||
mr_ = mr;
|
|
||||||
valid=false;
|
|
||||||
if(mr.valid)
|
|
||||||
{
|
|
||||||
mr_.buffer = (uint8_t *)malloc (mr_.end - mr_.start);
|
|
||||||
_SF = SF;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DF->ReadRaw(mr_.start,(mr_.end - mr_.start),mr_.buffer);
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
catch (DFHack::Error::MemoryAccessDenied &)
|
|
||||||
{
|
|
||||||
free(mr_.buffer);
|
|
||||||
valid = false;
|
|
||||||
mr.valid = false; // mark the range passed in as bad
|
|
||||||
cout << "Range 0x" << hex << mr_.start << " - 0x" << mr_.end;
|
|
||||||
|
|
||||||
if (strlen(mr_.name) != 0)
|
|
||||||
cout << " (" << mr_.name << ")";
|
|
||||||
|
|
||||||
cout << dec << " not readable." << endl;
|
|
||||||
cout << "Skipping this range on future scans." << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~SegmentFinder()
|
|
||||||
{
|
|
||||||
if(valid)
|
|
||||||
free(mr_.buffer);
|
|
||||||
}
|
|
||||||
bool isValid()
|
|
||||||
{
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
template <class needleType, class hayType, typename comparator >
|
|
||||||
bool Find (needleType needle, const uint8_t increment , vector <uint64_t> &newfound, comparator oper)
|
|
||||||
{
|
|
||||||
if(!valid) return !newfound.empty();
|
|
||||||
//loop
|
|
||||||
for(uint64_t offset = 0; offset < (mr_.end - mr_.start) - sizeof(hayType); offset += increment)
|
|
||||||
{
|
|
||||||
if( oper(_SF,(hayType *)(mr_.buffer + offset), needle) )
|
|
||||||
newfound.push_back(mr_.start + offset);
|
|
||||||
}
|
|
||||||
return !newfound.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
template < class needleType, class hayType, typename comparator >
|
|
||||||
uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length)
|
|
||||||
{
|
|
||||||
if(!valid) return 0;
|
|
||||||
uint64_t stopper = min((mr_.end - mr_.start) - sizeof(hayType), (start - mr_.start) - sizeof(hayType) + length);
|
|
||||||
//loop
|
|
||||||
for(uint64_t offset = start - mr_.start; offset < stopper; offset +=1)
|
|
||||||
{
|
|
||||||
if( oper(_SF,(hayType *)(mr_.buffer + offset), needle) )
|
|
||||||
return mr_.start + offset;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class needleType, class hayType, typename comparator >
|
|
||||||
bool Filter (needleType needle, vector <uint64_t> &found, vector <uint64_t> &newfound, comparator oper)
|
|
||||||
{
|
|
||||||
if(!valid) return !newfound.empty();
|
|
||||||
for( uint64_t i = 0; i < found.size(); i++)
|
|
||||||
{
|
|
||||||
if(mr_.isInRange(found[i]))
|
|
||||||
{
|
|
||||||
uint64_t corrected = found[i] - mr_.start;
|
|
||||||
if( oper(_SF,(hayType *)(mr_.buffer + corrected), needle) )
|
|
||||||
newfound.push_back(found[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !newfound.empty();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
friend class SegmentedFinder;
|
|
||||||
SegmentedFinder * _SF;
|
|
||||||
DFHack::Context * _DF;
|
|
||||||
DFHack::t_memrange mr_;
|
|
||||||
bool valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SegmentedFinder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SegmentedFinder(vector <DFHack::t_memrange>& ranges, DFHack::Context * DF)
|
|
||||||
{
|
|
||||||
_DF = DF;
|
|
||||||
for(size_t i = 0; i < ranges.size(); i++)
|
|
||||||
{
|
|
||||||
segments.push_back(new SegmentFinder(ranges[i], DF, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~SegmentedFinder()
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < segments.size(); i++)
|
|
||||||
{
|
|
||||||
delete segments[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SegmentFinder * getSegmentForAddress (uint64_t addr)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < segments.size(); i++)
|
|
||||||
{
|
|
||||||
if(segments[i]->mr_.isInRange(addr))
|
|
||||||
{
|
|
||||||
return segments[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
template <class needleType, class hayType, typename comparator >
|
|
||||||
bool Find (const needleType needle, const uint8_t increment, vector <uint64_t> &found, comparator oper)
|
|
||||||
{
|
|
||||||
found.clear();
|
|
||||||
for(size_t i = 0; i < segments.size(); i++)
|
|
||||||
{
|
|
||||||
segments[i]->Find<needleType,hayType,comparator>(needle, increment, found, oper);
|
|
||||||
}
|
|
||||||
return !(found.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
template < class needleType, class hayType, typename comparator >
|
|
||||||
uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length)
|
|
||||||
{
|
|
||||||
SegmentFinder * sf = getSegmentForAddress(start);
|
|
||||||
if(sf)
|
|
||||||
{
|
|
||||||
return sf->FindInRange<needleType,hayType,comparator>(needle, oper, start, length);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class needleType, class hayType, typename comparator >
|
|
||||||
bool Filter (const needleType needle, vector <uint64_t> &found, comparator oper)
|
|
||||||
{
|
|
||||||
vector <uint64_t> newfound;
|
|
||||||
for(size_t i = 0; i < segments.size(); i++)
|
|
||||||
{
|
|
||||||
segments[i]->Filter<needleType,hayType,comparator>(needle, found, newfound, oper);
|
|
||||||
}
|
|
||||||
found.clear();
|
|
||||||
found = newfound;
|
|
||||||
return !(found.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class needleType, class hayType, typename comparator >
|
|
||||||
bool Incremental (needleType needle, const uint8_t increment ,vector <uint64_t> &found, comparator oper)
|
|
||||||
{
|
|
||||||
if(found.empty())
|
|
||||||
{
|
|
||||||
return Find <needleType, hayType, comparator>(needle,increment,found,oper);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Filter <needleType, hayType, comparator>(needle,found,oper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T * Translate(uint64_t address)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < segments.size(); i++)
|
|
||||||
{
|
|
||||||
if(segments[i]->mr_.isInRange(address))
|
|
||||||
{
|
|
||||||
return (T *) (segments[i]->mr_.buffer + address - segments[i]->mr_.start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T Read(uint64_t address)
|
|
||||||
{
|
|
||||||
return *Translate<T>(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool Read(uint64_t address, T& target)
|
|
||||||
{
|
|
||||||
T * test = Translate<T>(address);
|
|
||||||
if(test)
|
|
||||||
{
|
|
||||||
target = *test;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
DFHack::Context * _DF;
|
|
||||||
vector <SegmentFinder *> segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool equalityP (SegmentedFinder* s, T *x, T y)
|
|
||||||
{
|
|
||||||
return (*x) == y;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vecTriplet
|
|
||||||
{
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t finish;
|
|
||||||
uint32_t alloc_finish;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Needle>
|
|
||||||
bool vectorLength (SegmentedFinder* s, vecTriplet *x, Needle &y)
|
|
||||||
{
|
|
||||||
if(x->start <= x->finish && x->finish <= x->alloc_finish)
|
|
||||||
if((x->finish - x->start) == y)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find a vector of 32bit pointers, where an object pointed to has a string 'y' as the first member
|
|
||||||
bool vectorString (SegmentedFinder* s, vecTriplet *x, const char *y)
|
|
||||||
{
|
|
||||||
uint32_t object_ptr;
|
|
||||||
// iterate over vector of pointers
|
|
||||||
for(uint32_t idx = x->start; idx < x->finish; idx += sizeof(uint32_t))
|
|
||||||
{
|
|
||||||
// deref ptr idx, get ptr to object
|
|
||||||
if(!s->Read(idx,object_ptr))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// deref ptr to first object, get ptr to string
|
|
||||||
uint32_t string_ptr;
|
|
||||||
if(!s->Read(object_ptr,string_ptr))
|
|
||||||
return false;
|
|
||||||
// get string location in our local cache
|
|
||||||
char * str = s->Translate<char>(string_ptr);
|
|
||||||
if(!str)
|
|
||||||
return false;
|
|
||||||
if(strcmp(y, str) == 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find a vector of 32bit pointers, where the first object pointed to has a string 'y' as the first member
|
|
||||||
bool vectorStringFirst (SegmentedFinder* s, vecTriplet *x, const char *y)
|
|
||||||
{
|
|
||||||
uint32_t object_ptr;
|
|
||||||
uint32_t idx = x->start;
|
|
||||||
// deref ptr idx, get ptr to object
|
|
||||||
if(!s->Read(idx,object_ptr))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// deref ptr to first object, get ptr to string
|
|
||||||
uint32_t string_ptr;
|
|
||||||
if(!s->Read(object_ptr,string_ptr))
|
|
||||||
return false;
|
|
||||||
// get string location in our local cache
|
|
||||||
char * str = s->Translate<char>(string_ptr);
|
|
||||||
if(!str)
|
|
||||||
return false;
|
|
||||||
if(strcmp(y, str) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test if the address is between vector.start and vector.finish
|
|
||||||
// not very useful alone, but could be a good step to filter some things
|
|
||||||
bool vectorAddrWithin (SegmentedFinder* s, vecTriplet *x, uint32_t address)
|
|
||||||
{
|
|
||||||
if(address < x->finish && address >= x->start)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test if an object address is within the vector of pointers
|
|
||||||
//
|
|
||||||
bool vectorOfPtrWithin (SegmentedFinder* s, vecTriplet *x, uint32_t address)
|
|
||||||
{
|
|
||||||
uint32_t object_ptr;
|
|
||||||
for(uint32_t idx = x->start; idx < x->finish; idx += sizeof(uint32_t))
|
|
||||||
{
|
|
||||||
if(!s->Read(idx,object_ptr))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(object_ptr == address)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool vectorAll (SegmentedFinder* s, vecTriplet *x, int )
|
|
||||||
{
|
|
||||||
if(x->start <= x->finish && x->finish <= x->alloc_finish)
|
|
||||||
{
|
|
||||||
if(s->getSegmentForAddress(x->start) == s->getSegmentForAddress(x->finish)
|
|
||||||
&& s->getSegmentForAddress(x->finish) == s->getSegmentForAddress(x->alloc_finish))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Bytestreamdata
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void * object;
|
|
||||||
uint32_t length;
|
|
||||||
uint32_t allocated;
|
|
||||||
uint32_t n_used;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Bytestream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Bytestream(void * obj, uint32_t len, bool alloc = false)
|
|
||||||
{
|
|
||||||
d = new Bytestreamdata();
|
|
||||||
d->allocated = alloc;
|
|
||||||
d->object = obj;
|
|
||||||
d->length = len;
|
|
||||||
d->n_used = 1;
|
|
||||||
constant = false;
|
|
||||||
}
|
|
||||||
Bytestream()
|
|
||||||
{
|
|
||||||
d = new Bytestreamdata();
|
|
||||||
d->allocated = false;
|
|
||||||
d->object = 0;
|
|
||||||
d->length = 0;
|
|
||||||
d->n_used = 1;
|
|
||||||
constant = false;
|
|
||||||
}
|
|
||||||
Bytestream( Bytestream & bs)
|
|
||||||
{
|
|
||||||
d =bs.d;
|
|
||||||
d->n_used++;
|
|
||||||
constant = false;
|
|
||||||
}
|
|
||||||
Bytestream( const Bytestream & bs)
|
|
||||||
{
|
|
||||||
d =bs.d;
|
|
||||||
d->n_used++;
|
|
||||||
constant = true;
|
|
||||||
}
|
|
||||||
~Bytestream()
|
|
||||||
{
|
|
||||||
d->n_used --;
|
|
||||||
if(d->allocated && d->object && d->n_used == 0)
|
|
||||||
{
|
|
||||||
free (d->object);
|
|
||||||
free (d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool Allocate(size_t bytes)
|
|
||||||
{
|
|
||||||
if(constant)
|
|
||||||
return false;
|
|
||||||
if(d->allocated)
|
|
||||||
{
|
|
||||||
d->object = realloc(d->object, bytes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->object = malloc( bytes );
|
|
||||||
}
|
|
||||||
|
|
||||||
if(d->object)
|
|
||||||
{
|
|
||||||
d->allocated = bytes;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->allocated = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template < class T >
|
|
||||||
bool insert( T what )
|
|
||||||
{
|
|
||||||
if(constant)
|
|
||||||
return false;
|
|
||||||
if(d->length+sizeof(T) >= d->allocated)
|
|
||||||
Allocate((d->length+sizeof(T)) * 2);
|
|
||||||
(*(T *)( (uint64_t)d->object + d->length)) = what;
|
|
||||||
d->length += sizeof(T);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Bytestreamdata * d;
|
|
||||||
bool constant;
|
|
||||||
};
|
|
||||||
std::ostream& operator<< ( std::ostream& out, Bytestream& bs )
|
|
||||||
{
|
|
||||||
if(bs.d->object)
|
|
||||||
{
|
|
||||||
out << "bytestream " << dec << bs.d->length << "/" << bs.d->allocated << " bytes" << endl;
|
|
||||||
for(size_t i = 0; i < bs.d->length; i++)
|
|
||||||
{
|
|
||||||
out << hex << (int) ((uint8_t *) bs.d->object)[i] << " ";
|
|
||||||
}
|
|
||||||
out << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out << "empty bytestresm" << endl;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::istream& operator>> ( std::istream& out, Bytestream& bs )
|
|
||||||
{
|
|
||||||
string read;
|
|
||||||
while(!out.eof())
|
|
||||||
{
|
|
||||||
string tmp;
|
|
||||||
out >> tmp;
|
|
||||||
read.append(tmp);
|
|
||||||
}
|
|
||||||
cout << read << endl;
|
|
||||||
bs.d->length = 0;
|
|
||||||
size_t first = read.find_first_of("\"");
|
|
||||||
size_t last = read.find_last_of("\"");
|
|
||||||
size_t start = first + 1;
|
|
||||||
if(first == read.npos)
|
|
||||||
{
|
|
||||||
std::transform(read.begin(), read.end(), read.begin(), (int(*)(int)) tolower);
|
|
||||||
bs.Allocate(read.size()); // overkill. size / 2 should be good, but this is safe
|
|
||||||
int state = 0;
|
|
||||||
char big = 0;
|
|
||||||
char small = 0;
|
|
||||||
string::iterator it = read.begin();
|
|
||||||
// iterate through string, construct a bytestream out of 00-FF bytes
|
|
||||||
while(it != read.end())
|
|
||||||
{
|
|
||||||
char reads = *it;
|
|
||||||
if((reads >='0' && reads <= '9'))
|
|
||||||
{
|
|
||||||
if(state == 0)
|
|
||||||
{
|
|
||||||
big = reads - '0';
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
else if(state == 1)
|
|
||||||
{
|
|
||||||
small = reads - '0';
|
|
||||||
state = 0;
|
|
||||||
bs.insert<char>(big*16 + small);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if((reads >= 'a' && reads <= 'f'))
|
|
||||||
{
|
|
||||||
if(state == 0)
|
|
||||||
{
|
|
||||||
big = reads - 'a' + 10;
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
else if(state == 1)
|
|
||||||
{
|
|
||||||
small = reads - 'a' + 10;
|
|
||||||
state = 0;
|
|
||||||
bs.insert<char>(big*16 + small);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
// we end in state= 1. should we add or should we trim... or throw errors?
|
|
||||||
// I decided on adding
|
|
||||||
if (state == 1)
|
|
||||||
{
|
|
||||||
small = 0;
|
|
||||||
bs.insert<char>(big*16 + small);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(last == first)
|
|
||||||
{
|
|
||||||
// only one "
|
|
||||||
last = read.size();
|
|
||||||
}
|
|
||||||
size_t length = last - start;
|
|
||||||
// construct bytestream out of stuff between ""
|
|
||||||
bs.d->length = length;
|
|
||||||
if(length)
|
|
||||||
{
|
|
||||||
// todo: Bytestream should be able to handle this without external code
|
|
||||||
bs.Allocate(length);
|
|
||||||
bs.d->length = length;
|
|
||||||
const char* strstart = read.c_str();
|
|
||||||
memcpy(bs.d->object, strstart + start, length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bs.d->object = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cout << bs;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findBytestream (SegmentedFinder* s, void *addr, Bytestream compare )
|
|
||||||
{
|
|
||||||
if(memcmp(addr, compare.d->object, compare.d->length) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findString (SegmentedFinder* s, uint32_t *addr, const char * compare )
|
|
||||||
{
|
|
||||||
// read string pointer, translate to local scheme
|
|
||||||
char *str = s->Translate<char>(*addr);
|
|
||||||
// verify
|
|
||||||
if(!str)
|
|
||||||
return false;
|
|
||||||
if(strcmp(str, compare) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findStrBuffer (SegmentedFinder* s, uint32_t *addr, const char * compare )
|
|
||||||
{
|
|
||||||
if(memcmp((const char *)addr, compare, strlen(compare)) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SEGMENTED_FINDER_H
|
|
@ -1,30 +0,0 @@
|
|||||||
Notes by PeridexisErrant, on the needs_porting scripts and plugins:
|
|
||||||
|
|
||||||
I deleted:
|
|
||||||
attachtest.py obsolete
|
|
||||||
digger.cpp less useful than digger2, replaced by autochop
|
|
||||||
digger2.cpp replaced by digfort
|
|
||||||
dfstatus.cpp replaced by dfstatus.lua
|
|
||||||
drawtile.cpp replaced by tiletypes
|
|
||||||
fix-3708.cpp obsolete, bug fixed in vanilla
|
|
||||||
lair.cpp replaced by lair
|
|
||||||
reveal.py replaced by reveal
|
|
||||||
treedump.py replaced by prospect & autochop
|
|
||||||
veinlook.cpp replaced by prospect
|
|
||||||
veinswap.cpp replaced by changevein
|
|
||||||
|
|
||||||
|
|
||||||
To investigate further:
|
|
||||||
creaturemanager.cpp modify skills and labors of creatures, kill creatures, etc; impressive but I suspect most functions implemented elsewhere
|
|
||||||
digpattern.cpp allows multi-Z designations. Obsolete, or is there more here?
|
|
||||||
incrementalsearch.cpp linux-only memory stuff; unqualified to judge
|
|
||||||
SegementedFinder.h more memory stuff
|
|
||||||
|
|
||||||
|
|
||||||
To port:
|
|
||||||
copypaste.cpp high value target - a proof of concept plugin to allow copy-pasting in DF; does both terrain and buildings/constructions
|
|
||||||
dfbauxtite.cpp changes material of mechanisms to bauxtite (ie magma-safe)
|
|
||||||
hellhole.cpp instantly creates a hole to the HFS
|
|
||||||
hotkeynotedump.py outputs a list of hotkeys and names; most useful before keybindings were possible. Trival to port but low value.
|
|
||||||
itemdesignator.cpp mostly replaced by Falconne's enhanced stocks, but could port the interesting 'set fire to things' function
|
|
||||||
position.py outputs very detailed time& place info
|
|
@ -1,479 +0,0 @@
|
|||||||
// Console version of DF copy paste, proof of concept
|
|
||||||
// By belal
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <climits>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <ctime>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#define DFHACK_WANT_MISCUTILS
|
|
||||||
#define DFHACK_WANT_TILETYPES
|
|
||||||
#include <DFHack.h>
|
|
||||||
#include "modules/WindowIO.h"
|
|
||||||
|
|
||||||
using namespace DFHack;
|
|
||||||
//bool waitTillCursorState(DFHack::Context *DF, bool On);
|
|
||||||
//bool waitTillCursorPositionState(DFHack::Context *DF, int32_t x,int32_t y, int32_t z);
|
|
||||||
|
|
||||||
//change this if you are having problems getting correct results, lower if you would like to go faster
|
|
||||||
//const int WAIT_AMT = 25;
|
|
||||||
|
|
||||||
void sort(uint32_t &a,uint32_t &b)
|
|
||||||
{
|
|
||||||
if(a > b){
|
|
||||||
uint32_t c = b;
|
|
||||||
b = a;
|
|
||||||
a = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void sort(int32_t &a,int32_t &b)
|
|
||||||
{
|
|
||||||
if(a > b){
|
|
||||||
int16_t c = b;
|
|
||||||
b = a;
|
|
||||||
a = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void printVecOfVec(ostream &out, vector<vector<vector<string> > >vec,char sep)
|
|
||||||
{
|
|
||||||
for(size_t k=0;k<vec.size();k++)
|
|
||||||
{
|
|
||||||
for(size_t i =0;i<vec[k].size();i++)
|
|
||||||
{
|
|
||||||
for(size_t j=0;j<vec[k][i].size();j++)
|
|
||||||
{
|
|
||||||
out << vec[k][i][j];
|
|
||||||
if(j==vec[k][i].size()-1)
|
|
||||||
{
|
|
||||||
out << "\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out << sep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out << "#<\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int main (int numargs, const char ** args)
|
|
||||||
{
|
|
||||||
map<string, string> buildCommands;
|
|
||||||
buildCommands["building_stockpilest"]="";
|
|
||||||
buildCommands["building_zonest"]="";
|
|
||||||
buildCommands["building_construction_blueprintst"]="";
|
|
||||||
buildCommands["building_wagonst"]="";
|
|
||||||
buildCommands["building_armor_standst"]="a";
|
|
||||||
buildCommands["building_bedst"]="b";
|
|
||||||
buildCommands["building_seatst"]="c";
|
|
||||||
buildCommands["building_burial_receptaclest"]="n";
|
|
||||||
buildCommands["building_doorst"]="d";
|
|
||||||
buildCommands["building_floodgatest"]="x";
|
|
||||||
buildCommands["building_floor_hatchst"]="H";
|
|
||||||
buildCommands["building_wall_gratest"]="W";
|
|
||||||
buildCommands["building_floor_gratest"]="G";
|
|
||||||
buildCommands["building_vertical_barsst"]="B";
|
|
||||||
buildCommands["building_floor_barsst"]="alt-b";
|
|
||||||
buildCommands["building_cabinetst"]="f";
|
|
||||||
buildCommands["building_containerst"]="h";
|
|
||||||
buildCommands["building_shopst"]="";
|
|
||||||
buildCommands["building_workshopst"]="";
|
|
||||||
buildCommands["building_alchemists_laboratoryst"]="wa";
|
|
||||||
buildCommands["building_carpenters_workshopst"]="wc";
|
|
||||||
buildCommands["building_farmers_workshopst"]="ww";
|
|
||||||
buildCommands["building_masons_workshopst"]="wm";
|
|
||||||
buildCommands["building_craftdwarfs_workshopst"]="wr";
|
|
||||||
buildCommands["building_jewelers_workshopst"]="wj";
|
|
||||||
buildCommands["building_metalsmiths_workshopst"]="wf";
|
|
||||||
buildCommands["building_magma_forgest"]="";
|
|
||||||
buildCommands["building_bowyers_workshopst"]="wb";
|
|
||||||
buildCommands["building_mechanics_workshopst"]="wt";
|
|
||||||
buildCommands["building_siege_workshopst"]="ws";
|
|
||||||
buildCommands["building_butchers_shopst"]="wU";
|
|
||||||
buildCommands["building_leather_worksst"]="we";
|
|
||||||
buildCommands["building_tanners_shopst"]="wn";
|
|
||||||
buildCommands["building_clothiers_shopst"]="wk";
|
|
||||||
buildCommands["building_fisheryst"]="wh";
|
|
||||||
buildCommands["building_stillst"]="wl";
|
|
||||||
buildCommands["building_loomst"]="wo";
|
|
||||||
buildCommands["building_quernst"]="wq";
|
|
||||||
buildCommands["building_kennelsst"]="k";
|
|
||||||
buildCommands["building_kitchenst"]="wz";
|
|
||||||
buildCommands["building_asheryst"]="wy";
|
|
||||||
buildCommands["building_dyers_shopst"]="wd";
|
|
||||||
buildCommands["building_millstonest"]="wM";
|
|
||||||
buildCommands["building_farm_plotst"]="p";
|
|
||||||
buildCommands["building_weapon_rackst"]="r";
|
|
||||||
buildCommands["building_statuest"]="s";
|
|
||||||
buildCommands["building_tablest"]="t";
|
|
||||||
buildCommands["building_paved_roadst"]="o";
|
|
||||||
buildCommands["building_bridgest"]="g";
|
|
||||||
buildCommands["building_wellst"]="l";
|
|
||||||
buildCommands["building_siege enginest"]="i";
|
|
||||||
buildCommands["building_catapultst"]="ic";
|
|
||||||
buildCommands["building_ballistast"]="ib";
|
|
||||||
buildCommands["building_furnacest"]="";
|
|
||||||
buildCommands["building_wood_furnacest"]="ew";
|
|
||||||
buildCommands["building_smelterst"]="es";
|
|
||||||
buildCommands["building_glass_furnacest"]="ek";
|
|
||||||
buildCommands["building_kilnst"]="ek";
|
|
||||||
buildCommands["building_magma_smelterst"]="es";
|
|
||||||
buildCommands["building_magma_glass_furnacest"]="ek";
|
|
||||||
buildCommands["building_magma_kilnst"]="ek";
|
|
||||||
buildCommands["building_glass_windowst"]="y";
|
|
||||||
buildCommands["building_gem_windowst"]="Y";
|
|
||||||
buildCommands["building_tradedepotst"]="D";
|
|
||||||
buildCommands["building_mechanismst"]="";
|
|
||||||
buildCommands["building_leverst"]="Tl";
|
|
||||||
buildCommands["building_pressure_platest"]="Tp";
|
|
||||||
buildCommands["building_cage_trapst"]="Tc";
|
|
||||||
buildCommands["building_stonefall_trapst"]="Ts";
|
|
||||||
buildCommands["building_weapon_trapst"]="Tw";
|
|
||||||
buildCommands["building_spikest"]="";
|
|
||||||
buildCommands["building_animal_trapst"]="m";
|
|
||||||
buildCommands["building_screw_pumpst"]="Ms";
|
|
||||||
buildCommands["building_water_wheelst"]="Mw";
|
|
||||||
buildCommands["building_windmillst"]="Mm";
|
|
||||||
buildCommands["building_gear_assemblyst"]="Mg";
|
|
||||||
buildCommands["building_horizontal_axlest"]="Mh";
|
|
||||||
buildCommands["building_vertical_axlest"]="Mv";
|
|
||||||
buildCommands["building_supportst"]="S";
|
|
||||||
buildCommands["building_cagest"]="j";
|
|
||||||
buildCommands["building_archery_targetst"]="A";
|
|
||||||
buildCommands["building_restraintst"]="v";
|
|
||||||
|
|
||||||
DFHack::ContextManager DFMgr("Memory.xml");
|
|
||||||
DFHack::Context *DF = DFMgr.getSingleContext();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DF->Attach();
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
std::cerr << e.what() << std::endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DFHack::Gui *Gui = DF->getGui();
|
|
||||||
DFHack::VersionInfo* mem = DF->getMemoryInfo();
|
|
||||||
DFHack::Process * p = DF->getProcess();
|
|
||||||
OffsetGroup * OG_Maps = mem->getGroup("Maps");
|
|
||||||
OffsetGroup * OG_MapBlock = OG_Maps->getGroup("block");
|
|
||||||
OffsetGroup * OG_LocalFt = OG_Maps->getGroup("features")->getGroup("local");
|
|
||||||
uint32_t designations = OG_MapBlock->getOffset("designation");
|
|
||||||
uint32_t block_feature1 = OG_MapBlock->getOffset("feature_local");
|
|
||||||
uint32_t block_feature2 = OG_MapBlock->getOffset("feature_global");
|
|
||||||
uint32_t region_x_offset = OG_Maps->getAddress("region_x");
|
|
||||||
uint32_t region_y_offset = OG_Maps->getAddress("region_y");
|
|
||||||
uint32_t region_z_offset = OG_Maps->getAddress("region_z");
|
|
||||||
uint32_t feature1_start_ptr = OG_LocalFt->getAddress("start_ptr");
|
|
||||||
int32_t regionX, regionY, regionZ;
|
|
||||||
|
|
||||||
// read position of the region inside DF world
|
|
||||||
p->readDWord (region_x_offset, (uint32_t &)regionX);
|
|
||||||
p->readDWord (region_y_offset, (uint32_t &)regionY);
|
|
||||||
p->readDWord (region_z_offset, (uint32_t &)regionZ);
|
|
||||||
while(1){
|
|
||||||
int32_t cx1,cy1,cz1;
|
|
||||||
cx1 = -30000;
|
|
||||||
while(cx1 == -30000)
|
|
||||||
{
|
|
||||||
DF->ForceResume();
|
|
||||||
cout << "Set cursor at first position, then press any key";
|
|
||||||
cin.ignore();
|
|
||||||
DF->Suspend();
|
|
||||||
Gui->getCursorCoords(cx1,cy1,cz1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t tx1,ty1,tz1;
|
|
||||||
tx1 = cx1/16;
|
|
||||||
ty1 = cy1/16;
|
|
||||||
tz1 = cz1;
|
|
||||||
|
|
||||||
int32_t cx2,cy2,cz2;
|
|
||||||
cx2 = -30000;
|
|
||||||
while(cx2 == -30000)
|
|
||||||
{
|
|
||||||
DF->Resume();
|
|
||||||
cout << "Set cursor at second position, then press any key";
|
|
||||||
cin.ignore();
|
|
||||||
DF->Suspend();
|
|
||||||
Gui->getCursorCoords(cx2,cy2,cz2);
|
|
||||||
}
|
|
||||||
uint32_t tx2,ty2,tz2;
|
|
||||||
tx2 = cx2/16;
|
|
||||||
ty2 = cy2/16;
|
|
||||||
tz2 = cz2;
|
|
||||||
sort(tx1,tx2);
|
|
||||||
sort(ty1,ty2);
|
|
||||||
sort(tz1,tz2);
|
|
||||||
sort(cx1,cx2);
|
|
||||||
sort(cy1,cy2);
|
|
||||||
sort(cz1,cz2);
|
|
||||||
|
|
||||||
vector <vector<vector<string> > >dig(cz2-cz1+1,vector<vector<string> >(cy2-cy1+1,vector<string>(cx2-cx1+1)));
|
|
||||||
vector <vector<vector<string> > >build(cz2-cz1+1,vector<vector<string> >(cy2-cy1+1,vector<string>(cx2-cx1+1)));
|
|
||||||
mapblock40d block;
|
|
||||||
DFHack::Maps *Maps = DF->getMaps();
|
|
||||||
Maps->Start();
|
|
||||||
for(uint32_t y = ty1;y<=ty2;y++)
|
|
||||||
{
|
|
||||||
for(uint32_t x = tx1;x<=tx2;x++)
|
|
||||||
{
|
|
||||||
for(uint32_t z = tz1;z<=tz2;z++)
|
|
||||||
{
|
|
||||||
if(Maps->isValidBlock(x,y,z))
|
|
||||||
{
|
|
||||||
if(Maps->ReadBlock40d(x,y,z,&block))
|
|
||||||
{
|
|
||||||
int ystart,yend,xstart,xend;
|
|
||||||
ystart=xstart=0;
|
|
||||||
yend=xend=15;
|
|
||||||
if(y == ty2)
|
|
||||||
{
|
|
||||||
yend = cy2 % 16;
|
|
||||||
}
|
|
||||||
if(y == ty1)
|
|
||||||
{
|
|
||||||
ystart = cy1 % 16;
|
|
||||||
}
|
|
||||||
if(x == tx2)
|
|
||||||
{
|
|
||||||
xend = cx2 % 16;
|
|
||||||
}
|
|
||||||
if(x == tx1)
|
|
||||||
{
|
|
||||||
xstart = cx1 % 16;
|
|
||||||
}
|
|
||||||
int zidx = z-tz1;
|
|
||||||
for(int yy = ystart; yy <= yend;yy++)
|
|
||||||
{
|
|
||||||
int yidx = yy+(16*(y-ty1)-(cy1%16));
|
|
||||||
for(int xx = xstart; xx <= xend;xx++)
|
|
||||||
{
|
|
||||||
int xidx = xx+(16*(x-tx1)-(cx1%16));
|
|
||||||
int16_t tt = block.tiletypes[xx][yy];
|
|
||||||
DFHack::TileShape ts = DFHack::tileShape(tt);
|
|
||||||
if(DFHack::isOpenTerrain(tt) || DFHack::isFloorTerrain(tt))
|
|
||||||
{
|
|
||||||
dig[zidx][yidx][xidx] = "d";
|
|
||||||
}
|
|
||||||
else if(DFHack::STAIR_DOWN == ts)
|
|
||||||
{
|
|
||||||
dig [zidx][yidx][xidx] = "j";
|
|
||||||
build [zidx][yidx][xidx] = "Cd";
|
|
||||||
}
|
|
||||||
else if(DFHack::STAIR_UP == ts)
|
|
||||||
{
|
|
||||||
dig [zidx][yidx][xidx] = "u";
|
|
||||||
build [zidx][yidx][xidx] = "Cu";
|
|
||||||
}
|
|
||||||
else if(DFHack::STAIR_UPDOWN == ts)
|
|
||||||
{
|
|
||||||
dig [zidx][yidx][xidx] = "i";
|
|
||||||
build [zidx][yidx][xidx] = "Cx";
|
|
||||||
}
|
|
||||||
else if(DFHack::isRampTerrain(tt))
|
|
||||||
{
|
|
||||||
dig [zidx][yidx][xidx] = "r";
|
|
||||||
build [zidx][yidx][xidx] = "Cr";
|
|
||||||
}
|
|
||||||
else if(DFHack::isWallTerrain(tt))
|
|
||||||
{
|
|
||||||
build [zidx][yidx][xidx] = "Cw";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yidx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DFHack::Buildings * Bld = DF->getBuildings();
|
|
||||||
std::map <uint32_t, std::string> custom_workshop_types;
|
|
||||||
uint32_t numBuildings;
|
|
||||||
if(Bld->Start(numBuildings))
|
|
||||||
{
|
|
||||||
Bld->ReadCustomWorkshopTypes(custom_workshop_types);
|
|
||||||
for(uint32_t i = 0; i < numBuildings; i++)
|
|
||||||
{
|
|
||||||
DFHack::t_building temp;
|
|
||||||
Bld->Read(i, temp);
|
|
||||||
if(temp.type != 0xFFFFFFFF) // check if type isn't invalid
|
|
||||||
{
|
|
||||||
std::string typestr;
|
|
||||||
mem->resolveClassIDToClassname(temp.type, typestr);
|
|
||||||
if(temp.z == cz1 && cx1 <= temp.x1 && cx2 >= temp.x2 && cy1 <= temp.y1 && cy2 >= temp.y2)
|
|
||||||
{
|
|
||||||
string currStr = build[temp.z-cz1][temp.y1-cy1][temp.x1-cx1];
|
|
||||||
stringstream stream;
|
|
||||||
string newStr = buildCommands[typestr];
|
|
||||||
if(temp.x1 != temp.x2)
|
|
||||||
{
|
|
||||||
stream << "(" << temp.x2-temp.x1+1 << "x" << temp.y2-temp.y1+1 << ")";
|
|
||||||
newStr += stream.str();
|
|
||||||
}
|
|
||||||
build[temp.z-cz1][temp.y1-cy1][temp.x1-cx1] = newStr + currStr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// for testing purposes
|
|
||||||
//ofstream outfile("test.txt");
|
|
||||||
// printVecOfVec(outfile, dig,'\t');
|
|
||||||
// outfile << endl;
|
|
||||||
// printVecOfVec(outfile, build,'\t');
|
|
||||||
// outfile << endl;
|
|
||||||
// outfile.close();
|
|
||||||
|
|
||||||
int32_t cx3,cy3,cz3,cx4,cy4,cz4;
|
|
||||||
uint32_t tx3,ty3,tz3,tx4,ty4,tz4;
|
|
||||||
char result;
|
|
||||||
while(1){
|
|
||||||
cx3 = -30000;
|
|
||||||
while(cx3 == -30000){
|
|
||||||
DF->Resume();
|
|
||||||
cout << "Set cursor at new position, then press any key:";
|
|
||||||
result = cin.get();
|
|
||||||
DF->Suspend();
|
|
||||||
Gui->getCursorCoords(cx3,cy3,cz3);
|
|
||||||
}
|
|
||||||
if(result == 'q'){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cx4 = cx3+cx2-cx1;
|
|
||||||
cy4 = cy3+cy2-cy1;
|
|
||||||
cz4 = cz3+cz2-cz1;
|
|
||||||
tx3=cx3/16;
|
|
||||||
ty3=cy3/16;
|
|
||||||
tz3=cz3;
|
|
||||||
tx4=cx4/16;
|
|
||||||
ty4=cy4/16;
|
|
||||||
tz4=cz4;
|
|
||||||
DFHack::WindowIO * Win = DF->getWindowIO();
|
|
||||||
designations40d designationBlock;
|
|
||||||
for(uint32_t y = ty3;y<=ty4;y++)
|
|
||||||
{
|
|
||||||
for(uint32_t x = tx3;x<=tx4;x++)
|
|
||||||
{
|
|
||||||
for(uint32_t z = tz3;z<=tz4;z++)
|
|
||||||
{
|
|
||||||
Maps->Start();
|
|
||||||
Maps->ReadBlock40d(x,y,z,&block);
|
|
||||||
Maps->ReadDesignations(x,y,z,&designationBlock);
|
|
||||||
int ystart,yend,xstart,xend;
|
|
||||||
ystart=xstart=0;
|
|
||||||
yend=xend=15;
|
|
||||||
if(y == ty4){
|
|
||||||
yend = cy4 % 16;
|
|
||||||
}
|
|
||||||
if(y == ty3){
|
|
||||||
ystart = cy3 % 16;
|
|
||||||
}
|
|
||||||
if(x == tx4){
|
|
||||||
xend = cx4 % 16;
|
|
||||||
}
|
|
||||||
if(x == tx3){
|
|
||||||
xstart = cx3 % 16;
|
|
||||||
}
|
|
||||||
int zidx = z-tz3;
|
|
||||||
for(int yy = ystart; yy <= yend;yy++){
|
|
||||||
int yidx = yy+(16*(y-ty3)-(cy3%16));
|
|
||||||
for(int xx = xstart; xx <= xend;xx++){
|
|
||||||
int xidx = xx+(16*(x-tx3)-(cx3%16));
|
|
||||||
if(dig[zidx][yidx][xidx] != ""){
|
|
||||||
char test = dig[zidx][yidx][xidx].c_str()[0];
|
|
||||||
switch (test){
|
|
||||||
case 'd':
|
|
||||||
designationBlock[xx][yy].bits.dig = DFHack::designation_default;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
designationBlock[xx][yy].bits.dig = DFHack::designation_ud_stair;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
designationBlock[xx][yy].bits.dig = DFHack::designation_u_stair;
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
designationBlock[xx][yy].bits.dig = DFHack::designation_d_stair;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
designationBlock[xx][yy].bits.dig = DFHack::designation_ramp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yidx++;
|
|
||||||
}
|
|
||||||
Maps->Start();
|
|
||||||
Maps->WriteDesignations(x,y,z,&designationBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DF->Detach();
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
std::cout << "Done. Press any key to continue" << std::endl;
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
bool waitTillCursorState(DFHack::Context *DF, bool On)
|
|
||||||
{
|
|
||||||
DFHack::WindowIO * w = DF->getWindowIO();
|
|
||||||
DFHack::Position * p = DF->getPosition();
|
|
||||||
int32_t x,y,z;
|
|
||||||
int tryCount = 0;
|
|
||||||
DF->Suspend();
|
|
||||||
bool cursorResult = p->getCursorCoords(x,y,z);
|
|
||||||
while(tryCount < 50 && On && !cursorResult || !On && cursorResult)
|
|
||||||
{
|
|
||||||
DF->Resume();
|
|
||||||
w->TypeSpecial(DFHack::WAIT,1,WAIT_AMT);
|
|
||||||
tryCount++;
|
|
||||||
DF->Suspend();
|
|
||||||
cursorResult = p->getCursorCoords(x,y,z);
|
|
||||||
}
|
|
||||||
if(tryCount >= 50)
|
|
||||||
{
|
|
||||||
cerr << "Something went wrong, cursor at x: " << x << " y: " << y << " z: " << z << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DF->Resume();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool waitTillCursorPositionState(DFHack::Context *DF, int32_t x,int32_t y, int32_t z)
|
|
||||||
{
|
|
||||||
DFHack::WindowIO * w = DF->getWindowIO();
|
|
||||||
DFHack::Position * p = DF->getPosition();
|
|
||||||
int32_t x2,y2,z2;
|
|
||||||
int tryCount = 0;
|
|
||||||
DF->Suspend();
|
|
||||||
bool cursorResult = p->getCursorCoords(x2,y2,z2);
|
|
||||||
while(tryCount < 50 && (x != x2 || y != y2 || z != z2))
|
|
||||||
{
|
|
||||||
DF->Resume();
|
|
||||||
w->TypeSpecial(DFHack::WAIT,1,WAIT_AMT);
|
|
||||||
tryCount++;
|
|
||||||
DF->Suspend();
|
|
||||||
cursorResult = p->getCursorCoords(x2,y2,z2);
|
|
||||||
}
|
|
||||||
if(tryCount >= 50)
|
|
||||||
{
|
|
||||||
cerr << "Something went wrong, cursor at x: " << x2 << " y: " << y2 << " z: " << z2 << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DF->Resume();
|
|
||||||
return true;
|
|
||||||
}*/
|
|
File diff suppressed because it is too large
Load Diff
@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
DFBauxite - Converts all your mechanisms to bauxite (for use in magma).
|
|
||||||
Author: Alex Legg
|
|
||||||
|
|
||||||
Based on code from and uses DFHack - www.sourceforge.net/projects/dfhack
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
|
|
||||||
#include <DFIntegers.h>
|
|
||||||
#include <DFExport.h>
|
|
||||||
#include <DFError.h>
|
|
||||||
#include <DFVector.h>
|
|
||||||
#include <DFMemInfo.h>
|
|
||||||
#include <DFProcess.h>
|
|
||||||
#include <DFTypes.h>
|
|
||||||
using namespace DFHack;
|
|
||||||
|
|
||||||
|
|
||||||
int main ()
|
|
||||||
{
|
|
||||||
DFHack::Process *proc;
|
|
||||||
DFHack::memory_info *meminfo;
|
|
||||||
DFHack::DfVector <uint32_t> *items_vector;
|
|
||||||
DFHack::t_item_df40d item_40d;
|
|
||||||
DFHack::t_matglossPair item_40d_material;
|
|
||||||
vector<DFHack::t_matgloss> stoneMat;
|
|
||||||
uint32_t item_material_offset;
|
|
||||||
uint32_t temp;
|
|
||||||
int32_t type;
|
|
||||||
int items;
|
|
||||||
int found = 0, converted = 0;
|
|
||||||
|
|
||||||
DFHack::ContextManager DF("Memory.xml");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DF.Attach();
|
|
||||||
}
|
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
cerr << e.what() << endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find out which material is bauxite
|
|
||||||
if(!DF.ReadStoneMatgloss(stoneMat))
|
|
||||||
{
|
|
||||||
cout << "Materials not supported for this version of DF, exiting." << endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
DF.Detach();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
int bauxiteIndex = -1;
|
|
||||||
for (int i = 0; i < stoneMat.size();i++)
|
|
||||||
{
|
|
||||||
if(strcmp(stoneMat[i].id, "BAUXITE") == 0)
|
|
||||||
{
|
|
||||||
bauxiteIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(bauxiteIndex == -1)
|
|
||||||
{
|
|
||||||
cout << "Cannot locate bauxite in the DF raws, exiting" << endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
DF.Detach();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get some basics needed for full access
|
|
||||||
proc = DF.getProcess();
|
|
||||||
meminfo = proc->getDescriptor();
|
|
||||||
|
|
||||||
// Get the object name/ID mapping
|
|
||||||
//FIXME: work on the 'supported features' system required
|
|
||||||
|
|
||||||
// Check availability of required addresses and offsets (doing custom stuff here)
|
|
||||||
|
|
||||||
items = meminfo->getAddress("items");
|
|
||||||
item_material_offset = meminfo->getOffset("item_materials");
|
|
||||||
if( !items || ! item_material_offset)
|
|
||||||
{
|
|
||||||
cout << "Items not supported for this DF version, exiting" << endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
DF.Detach();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
items_vector = new DFHack::DfVector <uint32_t> (proc, items);
|
|
||||||
for(uint32_t i = 0; i < items_vector->size(); i++)
|
|
||||||
{
|
|
||||||
// get pointer to object
|
|
||||||
temp = items_vector->at (i);
|
|
||||||
// read object
|
|
||||||
proc->read (temp, sizeof (DFHack::t_item_df40d), (uint8_t *) &item_40d);
|
|
||||||
|
|
||||||
// resolve object type
|
|
||||||
type = -1;
|
|
||||||
|
|
||||||
// skip things we can't identify
|
|
||||||
if(!meminfo->resolveObjectToClassID (temp, type))
|
|
||||||
continue;
|
|
||||||
string classname;
|
|
||||||
if(!meminfo->resolveClassIDToClassname (type, classname))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(classname == "item_trapparts")
|
|
||||||
{
|
|
||||||
proc->read (temp + item_material_offset, sizeof (DFHack::t_matglossPair), (uint8_t *) &item_40d_material);
|
|
||||||
|
|
||||||
cout << dec << "Mechanism at x:" << item_40d.x << " y:" << item_40d.y << " z:" << item_40d.z << " ID:" << item_40d.ID << endl;
|
|
||||||
|
|
||||||
if (item_40d_material.index != bauxiteIndex)
|
|
||||||
{
|
|
||||||
item_40d_material.index = bauxiteIndex;
|
|
||||||
proc->write (temp + item_material_offset, sizeof (DFHack::t_matglossPair), (uint8_t *) &item_40d_material);
|
|
||||||
converted++;
|
|
||||||
}
|
|
||||||
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (found == 0)
|
|
||||||
{
|
|
||||||
cout << "No mechanisms to convert" << endl;
|
|
||||||
} else {
|
|
||||||
cout << found << " mechanisms found" << endl;
|
|
||||||
cout << converted << " mechanisms converted" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
DF.Resume();
|
|
||||||
DF.Detach();
|
|
||||||
|
|
||||||
delete items_vector;
|
|
||||||
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cout << "Done. Press any key to continue" << endl;
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,259 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <string.h> // for memset
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <stack>
|
|
||||||
#include <map>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <DFHack.h>
|
|
||||||
#include <extra/MapExtras.h>
|
|
||||||
using namespace MapExtras;
|
|
||||||
//#include <argstream.h>
|
|
||||||
|
|
||||||
void usage(int argc, const char * argv[])
|
|
||||||
{
|
|
||||||
cout
|
|
||||||
<< "Usage:" << endl
|
|
||||||
<< argv[0] << " [option 1] [option 2] [...]" << endl
|
|
||||||
<< "-q : Suppress \"Press any key to continue\" at program termination" << endl
|
|
||||||
<< "-u <n> : Dig upwards <n> times (default 5)" << endl
|
|
||||||
<< "-d <n> : Dig downwards <n> times (default 5)" << endl
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void digat(MapCache * MCache, DFHack::DFCoord xy)
|
|
||||||
{
|
|
||||||
int16_t tt;
|
|
||||||
tt = MCache->tiletypeAt(xy);
|
|
||||||
if(!DFHack::isWallTerrain(tt))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// found a good tile, dig+unset material
|
|
||||||
DFHack::t_designation des = MCache->designationAt(xy);
|
|
||||||
|
|
||||||
if(MCache->testCoord(xy))
|
|
||||||
{
|
|
||||||
MCache->clearMaterialAt(xy);
|
|
||||||
|
|
||||||
if(des.bits.dig == DFHack::designation_no)
|
|
||||||
des.bits.dig = DFHack::designation_default;
|
|
||||||
MCache->setDesignationAt(xy,des);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int strtoint(const string &str)
|
|
||||||
{
|
|
||||||
stringstream ss(str);
|
|
||||||
int result;
|
|
||||||
return ss >> result ? result : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int16_t x;
|
|
||||||
int16_t y;
|
|
||||||
} pos;
|
|
||||||
|
|
||||||
int main (int argc, const char* argv[])
|
|
||||||
{
|
|
||||||
// Command line options
|
|
||||||
bool updown = false;
|
|
||||||
bool quiet = true;
|
|
||||||
// let's be more useful when double-clicked on windows
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
quiet = false;
|
|
||||||
#endif
|
|
||||||
int dig_up_n = 5;
|
|
||||||
int dig_down_n = 5;
|
|
||||||
|
|
||||||
for(int i = 1; i < argc; i++)
|
|
||||||
{
|
|
||||||
string arg_cur = argv[i];
|
|
||||||
string arg_next = "";
|
|
||||||
int arg_next_int = -99999;
|
|
||||||
/* Check if argv[i+1] is a number >= 0 */
|
|
||||||
if (i < argc-1) {
|
|
||||||
arg_next = argv[i+1];
|
|
||||||
arg_next_int = strtoint(arg_next);
|
|
||||||
if (arg_next != "0" && arg_next_int == 0) {
|
|
||||||
arg_next_int = -99999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (arg_cur == "-x")
|
|
||||||
{
|
|
||||||
updown = true;
|
|
||||||
}
|
|
||||||
else if (arg_cur == "-q")
|
|
||||||
{
|
|
||||||
quiet = true;
|
|
||||||
}
|
|
||||||
else if(arg_cur == "-u" && i < argc-1)
|
|
||||||
{
|
|
||||||
if (arg_next_int < 0 || arg_next_int >= 99999) {
|
|
||||||
usage(argc, argv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
dig_up_n = arg_next_int;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else if(arg_cur == "-d" && i < argc-1)
|
|
||||||
{
|
|
||||||
if (arg_next_int < 0 || arg_next_int >= 99999) {
|
|
||||||
usage(argc, argv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
dig_down_n = arg_next_int;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage(argc, argv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DFHack::ContextManager DFMgr("Memory.xml");
|
|
||||||
DFHack::Context * DF;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DF = DFMgr.getSingleContext();
|
|
||||||
DF->Attach();
|
|
||||||
}
|
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
cerr << "Error getting context: " << e.what() << endl;
|
|
||||||
if (!quiet)
|
|
||||||
cin.ignore();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t x_max,y_max,z_max;
|
|
||||||
DFHack::Maps * Maps = DF->getMaps();
|
|
||||||
DFHack::Gui * Gui = DF->getGui();
|
|
||||||
|
|
||||||
// init the map
|
|
||||||
if(!Maps->Start())
|
|
||||||
{
|
|
||||||
cerr << "Can't init map. Make sure you have a map loaded in DF." << endl;
|
|
||||||
DF->Detach();
|
|
||||||
if (!quiet)
|
|
||||||
cin.ignore();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t cx, cy, cz;
|
|
||||||
Maps->getSize(x_max,y_max,z_max);
|
|
||||||
uint32_t tx_max = x_max * 16;
|
|
||||||
uint32_t ty_max = y_max * 16;
|
|
||||||
|
|
||||||
Gui->getCursorCoords(cx,cy,cz);
|
|
||||||
if (cx == -30000)
|
|
||||||
{
|
|
||||||
cerr << "Cursor is not active. Point the cursor at the position to dig at." << endl;
|
|
||||||
DF->Detach();
|
|
||||||
if (!quiet)
|
|
||||||
{
|
|
||||||
cin.ignore();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz);
|
|
||||||
if(xy.x == 0 || xy.x == tx_max - 1 || xy.y == 0 || xy.y == ty_max - 1)
|
|
||||||
{
|
|
||||||
cerr << "I won't dig the borders. That would be cheating!" << endl;
|
|
||||||
DF->Detach();
|
|
||||||
if (!quiet)
|
|
||||||
{
|
|
||||||
cin.ignore();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
MapCache * MCache = new MapCache(Maps);
|
|
||||||
|
|
||||||
DFHack::t_designation des = MCache->designationAt(xy);
|
|
||||||
int16_t tt = MCache->tiletypeAt(xy);
|
|
||||||
int16_t veinmat = MCache->veinMaterialAt(xy);
|
|
||||||
|
|
||||||
/*
|
|
||||||
if( veinmat == -1 )
|
|
||||||
{
|
|
||||||
cerr << "This tile is non-vein. Bye :)" << endl;
|
|
||||||
delete MCache;
|
|
||||||
DF->Detach();
|
|
||||||
if (!quiet) {
|
|
||||||
cin.ignore();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
printf("Digging at (%d/%d/%d), tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, des.whole);
|
|
||||||
|
|
||||||
// 1 < xy.x < tx_max - 1
|
|
||||||
// 1 < xy.y < ty_max - 1
|
|
||||||
// xy.z
|
|
||||||
|
|
||||||
// X____
|
|
||||||
// X_XXX
|
|
||||||
// XXXXX
|
|
||||||
// __XXX
|
|
||||||
// __XXX
|
|
||||||
// _____
|
|
||||||
pos map[] =
|
|
||||||
{
|
|
||||||
{ 0,0 }
|
|
||||||
, { 0,1 }
|
|
||||||
, { 0,2 } , { 2,2 }, { 3,2 }, { 4,2 }
|
|
||||||
, { 0,3 }, { 1,3 }, { 2,3 }, { 3,3 }, { 4,3 }
|
|
||||||
, { 2,4 }, { 3,4 }, { 4,4 }
|
|
||||||
// this is mirrored, goes left instead of right
|
|
||||||
, {-2,2 }, {-3,2 }, {-4,2 }
|
|
||||||
, {-1,3 }, {-2,3 }, {-3,3 }, {-4,3 }
|
|
||||||
, {-2,4 }, {-3,4 }, {-4,4 }
|
|
||||||
};
|
|
||||||
|
|
||||||
DFHack::DFCoord npos = xy;
|
|
||||||
|
|
||||||
if (dig_up_n > 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < dig_up_n; j++)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < sizeof(map)/sizeof(map[0]); i++)
|
|
||||||
{
|
|
||||||
npos=xy;
|
|
||||||
npos.x += map[i].x;
|
|
||||||
npos.y -= 4*j + map[i].y;
|
|
||||||
printf("Digging at (%d/%d/%d)\n", npos.x, npos.y, npos.z);
|
|
||||||
digat(MCache, npos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dig_down_n > 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < dig_down_n; j++)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < sizeof(map)/sizeof(map[0]); i++)
|
|
||||||
{
|
|
||||||
npos=xy;
|
|
||||||
npos.x += map[i].x;
|
|
||||||
npos.y += 4*j + map[i].y;
|
|
||||||
printf("Digging at (%d/%d/%d)\n", npos.x, npos.y, npos.z);
|
|
||||||
digat(MCache, npos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MCache->WriteAll();
|
|
||||||
delete MCache;
|
|
||||||
DF->Detach();
|
|
||||||
if (!quiet) {
|
|
||||||
cout << "Done. Press any key to continue" << endl;
|
|
||||||
cin.ignore();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,20 +0,0 @@
|
|||||||
from context import Context, ContextManager
|
|
||||||
|
|
||||||
cm = ContextManager("Memory.xml")
|
|
||||||
df = cm.get_single_context()
|
|
||||||
|
|
||||||
df.attach()
|
|
||||||
|
|
||||||
gui = df.gui
|
|
||||||
|
|
||||||
print "Hotkeys"
|
|
||||||
|
|
||||||
hotkeys = gui.read_hotkeys()
|
|
||||||
|
|
||||||
for key in hotkeys:
|
|
||||||
print "x: %d\ny: %d\tz: %d\ttext: %s" % (key.x, key.y, key.z, key.name)
|
|
||||||
|
|
||||||
df.detach()
|
|
||||||
|
|
||||||
print "Done. Press any key to continue"
|
|
||||||
raw_input()
|
|
File diff suppressed because it is too large
Load Diff
@ -1,148 +0,0 @@
|
|||||||
// Item designator
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <climits>
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <DFHack.h>
|
|
||||||
#include <DFVector.h>
|
|
||||||
using namespace DFHack;
|
|
||||||
|
|
||||||
int main ()
|
|
||||||
{
|
|
||||||
|
|
||||||
DFHack::ContextManager CM ("Memory.xml");
|
|
||||||
DFHack::Context * DF;
|
|
||||||
DFHack::VersionInfo *mem;
|
|
||||||
DFHack::Gui * Gui;
|
|
||||||
DFHack::Materials * Mats;
|
|
||||||
DFHack::Items * Items;
|
|
||||||
cout << "This utility lets you mass-designate items by type and material." << endl
|
|
||||||
<< "Like set on fire all MICROCLINE item_stone..." << endl
|
|
||||||
<< "Some unusual combinations might be untested and cause the program to crash..."<< endl
|
|
||||||
<< "so, watch your step and backup your fort" << endl;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DF = CM.getSingleContext();
|
|
||||||
DF->Attach();
|
|
||||||
mem = DF->getMemoryInfo();
|
|
||||||
Gui = DF->getGui();
|
|
||||||
Mats = DF->getMaterials();
|
|
||||||
Mats->ReadAllMaterials();
|
|
||||||
Items = DF->getItems();
|
|
||||||
}
|
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
cerr << e.what() << endl;
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
DFHack::Process * p = DF->getProcess();
|
|
||||||
DFHack::OffsetGroup* itemGroup = mem->getGroup("Items");
|
|
||||||
unsigned vector_addr = itemGroup->getAddress("items_vector");
|
|
||||||
DFHack::DfVector <uint32_t> p_items (p, vector_addr);
|
|
||||||
uint32_t numItems = p_items.size();
|
|
||||||
|
|
||||||
map< string, map<string,vector< dfh_item > > > itemmap;
|
|
||||||
map< string, map< string, vector< dfh_item > > >::iterator it1;
|
|
||||||
int failedItems = 0;
|
|
||||||
map <string, int > bad_mat_items;
|
|
||||||
for(uint32_t i=0; i< numItems; i++)
|
|
||||||
{
|
|
||||||
DFHack::dfh_item temp;
|
|
||||||
Items->readItem(p_items[i],temp);
|
|
||||||
string typestr = Items->getItemClass(temp);
|
|
||||||
string material = Mats->getDescription(temp.matdesc);
|
|
||||||
itemmap[typestr][material].push_back(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i =0;
|
|
||||||
for( it1 = itemmap.begin(); it1!=itemmap.end();it1++)
|
|
||||||
{
|
|
||||||
cout << i << ": " << it1->first << "\n";
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if(i == 0)
|
|
||||||
{
|
|
||||||
cout << "No items found" << endl;
|
|
||||||
DF->Detach();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
cout << endl << "Select an item type from the list:";
|
|
||||||
int number;
|
|
||||||
string in;
|
|
||||||
stringstream ss;
|
|
||||||
getline(cin, in);
|
|
||||||
ss.str(in);
|
|
||||||
ss >> number;
|
|
||||||
int j = 0;
|
|
||||||
it1 = itemmap.begin();
|
|
||||||
while(j < number && it1!=itemmap.end())
|
|
||||||
{
|
|
||||||
it1++;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
cout << it1->first << "\n";
|
|
||||||
map<string,vector<dfh_item> >::iterator it2;
|
|
||||||
i=0;
|
|
||||||
for(it2 = it1->second.begin();it2!=it1->second.end();it2++){
|
|
||||||
cout << i << ":\t" << it2->first << " [" << it2->second.size() << "]" << endl;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
cout << endl << "Select a material type: ";
|
|
||||||
int number2;
|
|
||||||
ss.clear();
|
|
||||||
getline(cin, in);
|
|
||||||
ss.str(in);
|
|
||||||
ss >> number2;
|
|
||||||
|
|
||||||
decideAgain:
|
|
||||||
cout << "Select a designation - (d)ump, (f)orbid, (m)melt, set on fi(r)e :" << flush;
|
|
||||||
string designationType;
|
|
||||||
getline(cin,designationType);
|
|
||||||
DFHack::t_itemflags changeFlag = {0};
|
|
||||||
if(designationType == "d" || designationType == "dump")
|
|
||||||
{
|
|
||||||
changeFlag.dump = 1;
|
|
||||||
}
|
|
||||||
else if(designationType == "f" || designationType == "forbid")
|
|
||||||
{
|
|
||||||
changeFlag.forbid = 1;
|
|
||||||
}
|
|
||||||
else if(designationType == "m" || designationType == "melt")
|
|
||||||
{
|
|
||||||
changeFlag.melt = 1;
|
|
||||||
}
|
|
||||||
else if(designationType == "r" || designationType == "fire")
|
|
||||||
{
|
|
||||||
changeFlag.on_fire = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
goto decideAgain;
|
|
||||||
}
|
|
||||||
j=0;
|
|
||||||
it2= it1->second.begin();
|
|
||||||
while(j < number2 && it2!=it1->second.end())
|
|
||||||
{
|
|
||||||
it2++;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
for(uint32_t k = 0;k< it2->second.size();k++)
|
|
||||||
{
|
|
||||||
DFHack::dfh_item & t = it2->second[k];
|
|
||||||
t.base.flags.whole |= changeFlag.whole;
|
|
||||||
Items->writeItem(t);
|
|
||||||
}
|
|
||||||
DF->Detach();
|
|
||||||
#ifndef LINUX_BUILD
|
|
||||||
cout << "Done. Press any key to continue" << endl;
|
|
||||||
cin.ignore();
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
import sys
|
|
||||||
from pydfhack import ContextManager
|
|
||||||
|
|
||||||
df_cm = ContextManager("Memory.xml")
|
|
||||||
df = df_cm.get_single_context()
|
|
||||||
|
|
||||||
if not df.attach():
|
|
||||||
print "Unable to attach!"
|
|
||||||
print "Press any key to continue"
|
|
||||||
|
|
||||||
raw_input()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
gui = df.gui
|
|
||||||
|
|
||||||
if gui is not None:
|
|
||||||
maps = df.maps
|
|
||||||
world = df.world
|
|
||||||
|
|
||||||
have_maps = maps.start()
|
|
||||||
world.start()
|
|
||||||
|
|
||||||
gm = world.read_game_mode()
|
|
||||||
|
|
||||||
if gm is not None:
|
|
||||||
print gm
|
|
||||||
|
|
||||||
date_tuple = (world.read_current_year(), world.read_current_month(), world.read_current_day(), world.read_current_tick())
|
|
||||||
|
|
||||||
print "Year: %d Month: %d Day: %d Tick: %d" % date_tuple
|
|
||||||
|
|
||||||
v_coords = gui.get_view_coords()
|
|
||||||
c_coords = gui.get_cursor_coords()
|
|
||||||
w_coords = (-1, -1, -1)
|
|
||||||
world_pos_string = ""
|
|
||||||
|
|
||||||
if have_maps is True:
|
|
||||||
w_coords = maps.getPosition()
|
|
||||||
|
|
||||||
x = (v_coords[0] + w_coords[0]) * 48
|
|
||||||
y = (v_coords[1] + w_coords[1]) * 48
|
|
||||||
z = (v_coords[2] + w_coords[2])
|
|
||||||
|
|
||||||
world_pos_string = " world: %d/%d/%d" % (x, y, z)
|
|
||||||
|
|
||||||
print "Map world offset: %d/%d/%d embark squares" % w_coords
|
|
||||||
|
|
||||||
if v_coords != (-1, -1, -1):
|
|
||||||
print "view coords: %d/%d/%d" % v_coords
|
|
||||||
|
|
||||||
if have_maps is True:
|
|
||||||
print world_pos_string
|
|
||||||
|
|
||||||
if c_coords != (-1, -1, -1):
|
|
||||||
print "cursor coords: %d/%d/%d" % c_coords
|
|
||||||
|
|
||||||
if have_maps is True:
|
|
||||||
print world_pos_string
|
|
||||||
|
|
||||||
window_size = gui.get_window_size()
|
|
||||||
|
|
||||||
if window_size != (-1, -1):
|
|
||||||
print "window size : %d %d" % window_size
|
|
||||||
else:
|
|
||||||
print "cursor and window parameters are unsupported on your version of DF"
|
|
||||||
|
|
||||||
if not df.detach():
|
|
||||||
print "Unable to detach!"
|
|
||||||
|
|
||||||
print "Done. Press any key to continue"
|
|
||||||
raw_input()
|
|
@ -0,0 +1 @@
|
|||||||
|
libstdc++.6.dylib
|
Binary file not shown.
@ -0,0 +1,682 @@
|
|||||||
|
//Blueprint
|
||||||
|
//By cdombroski
|
||||||
|
//Translates a region of tiles specified by the cursor and arguments/prompts into a series of blueprint files suitable for digfort/buildingplan/quickfort
|
||||||
|
|
||||||
|
#include <Console.h>
|
||||||
|
#include <PluginManager.h>
|
||||||
|
|
||||||
|
#include "modules/Buildings.h"
|
||||||
|
#include "modules/Gui.h"
|
||||||
|
#include "modules/MapCache.h"
|
||||||
|
|
||||||
|
#include "df/building_axle_horizontalst.h"
|
||||||
|
#include "df/building_bridgest.h"
|
||||||
|
#include "df/building_constructionst.h"
|
||||||
|
#include "df/building_furnacest.h"
|
||||||
|
#include "df/building_rollersst.h"
|
||||||
|
#include "df/building_screw_pumpst.h"
|
||||||
|
#include "df/building_siegeenginest.h"
|
||||||
|
#include "df/building_trapst.h"
|
||||||
|
#include "df/building_water_wheelst.h"
|
||||||
|
#include "df/building_workshopst.h"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::endl;
|
||||||
|
using std::vector;
|
||||||
|
using std::ofstream;
|
||||||
|
using std::swap;
|
||||||
|
using std::find;
|
||||||
|
using std::pair;
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace df::enums;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("blueprint");
|
||||||
|
|
||||||
|
enum phase {DIG=1, BUILD=2, PLACE=4, QUERY=8};
|
||||||
|
|
||||||
|
command_result blueprint(color_ostream &out, vector <string> ¶meters);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init(color_ostream &out, vector<PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
commands.push_back(PluginCommand("blueprint", "Convert map tiles into a blueprint", blueprint, false));
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown(color_ostream &out)
|
||||||
|
{
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result help(color_ostream &out)
|
||||||
|
{
|
||||||
|
out << "blueprint width height depth name [dig] [build] [place] [query]" << endl
|
||||||
|
<< " width, height, depth: area to translate in tiles" << endl
|
||||||
|
<< " name: base name for blueprint files" << endl
|
||||||
|
<< " dig: generate blueprints for digging" << endl
|
||||||
|
<< " build: generate blueprints for building" << endl
|
||||||
|
<< " place: generate blueprints for stockpiles" << endl
|
||||||
|
<< " query: generate blueprints for querying (room designations)" << endl
|
||||||
|
<< " defaults to generating all blueprints" << endl
|
||||||
|
<< endl
|
||||||
|
<< "blueprint translates a portion of your fortress into blueprints suitable for" << endl
|
||||||
|
<< " digfort/fortplan/quickfort. Blueprints are created in the DF folder with names" << endl
|
||||||
|
<< " following a \"name-phase.csv\" pattern. Translation starts at the current" << endl
|
||||||
|
<< " cursor location and includes all tiles in the range specified." << endl;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<uint32_t, uint32_t> get_building_size(df::building* b)
|
||||||
|
{
|
||||||
|
return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char get_tile_dig(MapExtras::MapCache mc, int32_t x, int32_t y, int32_t z)
|
||||||
|
{
|
||||||
|
df::tiletype tt = mc.tiletypeAt(DFCoord(x, y, z));
|
||||||
|
df::tiletype_shape ts = tileShape(tt);
|
||||||
|
switch (ts)
|
||||||
|
{
|
||||||
|
case tiletype_shape::EMPTY:
|
||||||
|
case tiletype_shape::RAMP_TOP:
|
||||||
|
return 'h';
|
||||||
|
case tiletype_shape::FLOOR:
|
||||||
|
case tiletype_shape::BOULDER:
|
||||||
|
case tiletype_shape::PEBBLES:
|
||||||
|
case tiletype_shape::BROOK_TOP:
|
||||||
|
return 'd';
|
||||||
|
case tiletype_shape::FORTIFICATION:
|
||||||
|
return 'F';
|
||||||
|
case tiletype_shape::STAIR_UP:
|
||||||
|
return 'u';
|
||||||
|
case tiletype_shape::STAIR_DOWN:
|
||||||
|
return 'j';
|
||||||
|
case tiletype_shape::STAIR_UPDOWN:
|
||||||
|
return 'i';
|
||||||
|
case tiletype_shape::RAMP:
|
||||||
|
return 'r';
|
||||||
|
default:
|
||||||
|
return ' ';
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_tile_build(uint32_t x, uint32_t y, df::building* b)
|
||||||
|
{
|
||||||
|
if (! b)
|
||||||
|
return " ";
|
||||||
|
bool at_nw_corner = x == b->x1 && y == b->y1;
|
||||||
|
bool at_se_corner = x == b->x2 && y == b->y2;
|
||||||
|
bool at_center = x == b->centerx && y == b->centery;
|
||||||
|
pair<uint32_t, uint32_t> size = get_building_size(b);
|
||||||
|
stringstream out;// = stringstream();
|
||||||
|
switch(b->getType())
|
||||||
|
{
|
||||||
|
case building_type::Armorstand:
|
||||||
|
return "a";
|
||||||
|
case building_type::Bed:
|
||||||
|
return "b";
|
||||||
|
case building_type::Chair:
|
||||||
|
return "c";
|
||||||
|
case building_type::Door:
|
||||||
|
return "d";
|
||||||
|
case building_type::Floodgate:
|
||||||
|
return "x";
|
||||||
|
case building_type::Cabinet:
|
||||||
|
return "f";
|
||||||
|
case building_type::Box:
|
||||||
|
return "h";
|
||||||
|
//case building_type::Kennel is missing
|
||||||
|
case building_type::FarmPlot:
|
||||||
|
if(!at_nw_corner)
|
||||||
|
return "`";
|
||||||
|
out << "p(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::Weaponrack:
|
||||||
|
return "r";
|
||||||
|
case building_type::Statue:
|
||||||
|
return "s";
|
||||||
|
case building_type::Table:
|
||||||
|
return "t";
|
||||||
|
case building_type::RoadPaved:
|
||||||
|
if(! at_nw_corner)
|
||||||
|
return "`";
|
||||||
|
out << "o(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::RoadDirt:
|
||||||
|
if(! at_nw_corner)
|
||||||
|
return "`";
|
||||||
|
out << "O(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::Bridge:
|
||||||
|
if(! at_nw_corner)
|
||||||
|
return "`";
|
||||||
|
switch(((df::building_bridgest*) b)->direction)
|
||||||
|
{
|
||||||
|
case df::building_bridgest::T_direction::Down:
|
||||||
|
out << "gx";
|
||||||
|
break;
|
||||||
|
case df::building_bridgest::T_direction::Left:
|
||||||
|
out << "ga";
|
||||||
|
break;
|
||||||
|
case df::building_bridgest::T_direction::Up:
|
||||||
|
out << "gw";
|
||||||
|
break;
|
||||||
|
case df::building_bridgest::T_direction::Right:
|
||||||
|
out << "gd";
|
||||||
|
break;
|
||||||
|
case df::building_bridgest::T_direction::Retracting:
|
||||||
|
out << "gs";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out << "(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::Well:
|
||||||
|
return "l";
|
||||||
|
case building_type::SiegeEngine:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
return ((df::building_siegeenginest*) b)->type == df::siegeengine_type::Ballista ? "ib" : "ic";
|
||||||
|
case building_type::Workshop:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
switch (((df::building_workshopst*) b)->type)
|
||||||
|
{
|
||||||
|
case workshop_type::Leatherworks:
|
||||||
|
return "we";
|
||||||
|
case workshop_type::Quern:
|
||||||
|
return "wq";
|
||||||
|
case workshop_type::Millstone:
|
||||||
|
return "wM";
|
||||||
|
case workshop_type::Loom:
|
||||||
|
return "wo";
|
||||||
|
case workshop_type::Clothiers:
|
||||||
|
return "wk";
|
||||||
|
case workshop_type::Bowyers:
|
||||||
|
return "wb";
|
||||||
|
case workshop_type::Carpenters:
|
||||||
|
return "wc";
|
||||||
|
case workshop_type::MetalsmithsForge:
|
||||||
|
return "wf";
|
||||||
|
case workshop_type::MagmaForge:
|
||||||
|
return "wv";
|
||||||
|
case workshop_type::Jewelers:
|
||||||
|
return "wj";
|
||||||
|
case workshop_type::Masons:
|
||||||
|
return "wm";
|
||||||
|
case workshop_type::Butchers:
|
||||||
|
return "wu";
|
||||||
|
case workshop_type::Tanners:
|
||||||
|
return "wn";
|
||||||
|
case workshop_type::Craftsdwarfs:
|
||||||
|
return "wr";
|
||||||
|
case workshop_type::Siege:
|
||||||
|
return "ws";
|
||||||
|
case workshop_type::Mechanics:
|
||||||
|
return "wt";
|
||||||
|
case workshop_type::Still:
|
||||||
|
return "wl";
|
||||||
|
case workshop_type::Farmers:
|
||||||
|
return "ww";
|
||||||
|
case workshop_type::Kitchen:
|
||||||
|
return "wz";
|
||||||
|
case workshop_type::Fishery:
|
||||||
|
return "wh";
|
||||||
|
case workshop_type::Ashery:
|
||||||
|
return "wy";
|
||||||
|
case workshop_type::Dyers:
|
||||||
|
return "wd";
|
||||||
|
case workshop_type::Custom:
|
||||||
|
//can't do anything with custom workshop
|
||||||
|
return "`";
|
||||||
|
}
|
||||||
|
case building_type::Furnace:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
switch (((df::building_furnacest*) b)->type)
|
||||||
|
{
|
||||||
|
case furnace_type::WoodFurnace:
|
||||||
|
return "ew";
|
||||||
|
case furnace_type::Smelter:
|
||||||
|
return "es";
|
||||||
|
case furnace_type::GlassFurnace:
|
||||||
|
return "eg";
|
||||||
|
case furnace_type::Kiln:
|
||||||
|
return "ek";
|
||||||
|
case furnace_type::MagmaSmelter:
|
||||||
|
return "el";
|
||||||
|
case furnace_type::MagmaGlassFurnace:
|
||||||
|
return "ea";
|
||||||
|
case furnace_type::MagmaKiln:
|
||||||
|
return "en";
|
||||||
|
case furnace_type::Custom:
|
||||||
|
//can't do anything with custom furnace
|
||||||
|
return "`";
|
||||||
|
}
|
||||||
|
case building_type::WindowGlass:
|
||||||
|
return "y";
|
||||||
|
case building_type::WindowGem:
|
||||||
|
return "Y";
|
||||||
|
case building_type::Construction:
|
||||||
|
switch (((df::building_constructionst*) b)->type)
|
||||||
|
{
|
||||||
|
case construction_type::Fortification:
|
||||||
|
return "CF";
|
||||||
|
case construction_type::Wall:
|
||||||
|
return "CW";
|
||||||
|
case construction_type::Floor:
|
||||||
|
return "Cf";
|
||||||
|
case construction_type::UpStair:
|
||||||
|
return "Cu";
|
||||||
|
case construction_type::DownStair:
|
||||||
|
return "Cj";
|
||||||
|
case construction_type::UpDownStair:
|
||||||
|
return "Cx";
|
||||||
|
case construction_type::Ramp:
|
||||||
|
return "Cr";
|
||||||
|
case construction_type::TrackN:
|
||||||
|
return "trackN";
|
||||||
|
case construction_type::TrackS:
|
||||||
|
return "trackS";
|
||||||
|
case construction_type::TrackE:
|
||||||
|
return "trackE";
|
||||||
|
case construction_type::TrackW:
|
||||||
|
return "trackW";
|
||||||
|
case construction_type::TrackNS:
|
||||||
|
return "trackNS";
|
||||||
|
case construction_type::TrackNE:
|
||||||
|
return "trackNE";
|
||||||
|
case construction_type::TrackNW:
|
||||||
|
return "trackNW";
|
||||||
|
case construction_type::TrackSE:
|
||||||
|
return "trackSE";
|
||||||
|
case construction_type::TrackSW:
|
||||||
|
return "trackSW";
|
||||||
|
case construction_type::TrackEW:
|
||||||
|
return "trackEW";
|
||||||
|
case construction_type::TrackNSE:
|
||||||
|
return "trackNSE";
|
||||||
|
case construction_type::TrackNSW:
|
||||||
|
return "trackNSW";
|
||||||
|
case construction_type::TrackNEW:
|
||||||
|
return "trackNEW";
|
||||||
|
case construction_type::TrackSEW:
|
||||||
|
return "trackSEW";
|
||||||
|
case construction_type::TrackNSEW:
|
||||||
|
return "trackNSEW";
|
||||||
|
case construction_type::TrackRampN:
|
||||||
|
return "trackrampN";
|
||||||
|
case construction_type::TrackRampS:
|
||||||
|
return "trackrampS";
|
||||||
|
case construction_type::TrackRampE:
|
||||||
|
return "trackrampE";
|
||||||
|
case construction_type::TrackRampW:
|
||||||
|
return "trackrampW";
|
||||||
|
case construction_type::TrackRampNS:
|
||||||
|
return "trackrampNS";
|
||||||
|
case construction_type::TrackRampNE:
|
||||||
|
return "trackrampNE";
|
||||||
|
case construction_type::TrackRampNW:
|
||||||
|
return "trackrampNW";
|
||||||
|
case construction_type::TrackRampSE:
|
||||||
|
return "trackrampSE";
|
||||||
|
case construction_type::TrackRampSW:
|
||||||
|
return "trackrampSW";
|
||||||
|
case construction_type::TrackRampEW:
|
||||||
|
return "trackrampEW";
|
||||||
|
case construction_type::TrackRampNSE:
|
||||||
|
return "trackrampNSE";
|
||||||
|
case construction_type::TrackRampNSW:
|
||||||
|
return "trackrampNSW";
|
||||||
|
case construction_type::TrackRampNEW:
|
||||||
|
return "trackrampNEW";
|
||||||
|
case construction_type::TrackRampSEW:
|
||||||
|
return "trackrampSEW";
|
||||||
|
case construction_type::TrackRampNSEW:
|
||||||
|
return "trackrampNSEW";
|
||||||
|
}
|
||||||
|
case building_type::Shop:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
return "z";
|
||||||
|
case building_type::AnimalTrap:
|
||||||
|
return "m";
|
||||||
|
case building_type::Chain:
|
||||||
|
return "v";
|
||||||
|
case building_type::Cage:
|
||||||
|
return "j";
|
||||||
|
case building_type::TradeDepot:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
return "D";
|
||||||
|
case building_type::Trap:
|
||||||
|
switch (((df::building_trapst*) b)->trap_type)
|
||||||
|
{
|
||||||
|
case trap_type::StoneFallTrap:
|
||||||
|
return "Ts";
|
||||||
|
case trap_type::WeaponTrap:
|
||||||
|
return "Tw";
|
||||||
|
case trap_type::Lever:
|
||||||
|
return "Tl";
|
||||||
|
case trap_type::PressurePlate:
|
||||||
|
return "Tp";
|
||||||
|
case trap_type::CageTrap:
|
||||||
|
return "Tc";
|
||||||
|
case trap_type::TrackStop:
|
||||||
|
df::building_trapst* ts = (df::building_trapst*) b;
|
||||||
|
out << "CS";
|
||||||
|
if (ts->use_dump)
|
||||||
|
{
|
||||||
|
if (ts->dump_x_shift == 0)
|
||||||
|
{
|
||||||
|
if (ts->dump_y_shift > 0)
|
||||||
|
out << "dd";
|
||||||
|
else
|
||||||
|
out << "d";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ts->dump_x_shift > 0)
|
||||||
|
out << "ddd";
|
||||||
|
else
|
||||||
|
out << "dddd";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (ts->friction)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
out << "a";
|
||||||
|
case 50:
|
||||||
|
out << "a";
|
||||||
|
case 500:
|
||||||
|
out << "a";
|
||||||
|
case 10000:
|
||||||
|
out << "a";
|
||||||
|
}
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
case building_type::ScrewPump:
|
||||||
|
if (! at_se_corner) //screw pumps anchor at bottom/right
|
||||||
|
return "`";
|
||||||
|
switch (((df::building_screw_pumpst*) b)->direction)
|
||||||
|
{
|
||||||
|
case screw_pump_direction::FromNorth:
|
||||||
|
return "Msu";
|
||||||
|
case screw_pump_direction::FromEast:
|
||||||
|
return "Msk";
|
||||||
|
case screw_pump_direction::FromSouth:
|
||||||
|
return "Msm";
|
||||||
|
case screw_pump_direction::FromWest:
|
||||||
|
return "Msh";
|
||||||
|
}
|
||||||
|
case building_type::WaterWheel:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
//s swaps orientation which defaults to vertical
|
||||||
|
return ((df::building_water_wheelst*) b)->is_vertical ? "Mw" : "Mws";
|
||||||
|
case building_type::Windmill:
|
||||||
|
if (! at_center)
|
||||||
|
return "`";
|
||||||
|
return "Mm";
|
||||||
|
case building_type::GearAssembly:
|
||||||
|
return "Mg";
|
||||||
|
case building_type::AxleHorizontal:
|
||||||
|
if (! at_nw_corner) //a guess based on how constructions work
|
||||||
|
return "`";
|
||||||
|
//same as water wheel but reversed
|
||||||
|
out << "Mh" << (((df::building_axle_horizontalst*) b)->is_vertical ? "s" : "")
|
||||||
|
<< "(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::AxleVertical:
|
||||||
|
return "Mv";
|
||||||
|
case building_type::Rollers:
|
||||||
|
if (! at_nw_corner)
|
||||||
|
return "`";
|
||||||
|
out << "Mr";
|
||||||
|
switch (((df::building_rollersst*) b)->direction)
|
||||||
|
{
|
||||||
|
case screw_pump_direction::FromNorth:
|
||||||
|
break;
|
||||||
|
case screw_pump_direction::FromEast:
|
||||||
|
out << "s";
|
||||||
|
case screw_pump_direction::FromSouth:
|
||||||
|
out << "s";
|
||||||
|
case screw_pump_direction::FromWest:
|
||||||
|
out << "s";
|
||||||
|
}
|
||||||
|
out << "(" << size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
case building_type::Support:
|
||||||
|
return "S";
|
||||||
|
case building_type::ArcheryTarget:
|
||||||
|
return "A";
|
||||||
|
case building_type::TractionBench:
|
||||||
|
return "R";
|
||||||
|
case building_type::Hatch:
|
||||||
|
return "H";
|
||||||
|
case building_type::Slab:
|
||||||
|
//how to mine alt key?!?
|
||||||
|
//alt+s
|
||||||
|
return " ";
|
||||||
|
case building_type::NestBox:
|
||||||
|
return "N";
|
||||||
|
case building_type::Hive:
|
||||||
|
//alt+h
|
||||||
|
return " ";
|
||||||
|
case building_type::GrateWall:
|
||||||
|
return "W";
|
||||||
|
case building_type::GrateFloor:
|
||||||
|
return "G";
|
||||||
|
case building_type::BarsVertical:
|
||||||
|
return "B";
|
||||||
|
case building_type::BarsFloor:
|
||||||
|
//alt+b
|
||||||
|
return " ";
|
||||||
|
default:
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_tile_place(uint32_t x, uint32_t y, df::building* b)
|
||||||
|
{
|
||||||
|
if (! b || b->getType() != building_type::Stockpile)
|
||||||
|
return " ";
|
||||||
|
if (b->x1 != x || b->y1 != y)
|
||||||
|
return "`";
|
||||||
|
pair<uint32_t, uint32_t> size = get_building_size(b);
|
||||||
|
df::building_stockpilest* sp = (df::building_stockpilest*) b;
|
||||||
|
stringstream out;// = stringstream();
|
||||||
|
switch (sp->settings.flags.whole)
|
||||||
|
{
|
||||||
|
case df::stockpile_group_set::mask_animals:
|
||||||
|
out << "a";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_food:
|
||||||
|
out << "f";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_furniture:
|
||||||
|
out << "u";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_corpses:
|
||||||
|
out << "y";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_refuse:
|
||||||
|
out << "r";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_wood:
|
||||||
|
out << "w";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_stone:
|
||||||
|
out << "s";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_gems:
|
||||||
|
out << "e";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_bars_blocks:
|
||||||
|
out << "b";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_cloth:
|
||||||
|
out << "h";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_leather:
|
||||||
|
out << "l";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_ammo:
|
||||||
|
out << "z";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_coins:
|
||||||
|
out << "n";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_finished_goods:
|
||||||
|
out << "g";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_weapons:
|
||||||
|
out << "p";
|
||||||
|
break;
|
||||||
|
case df::stockpile_group_set::mask_armor:
|
||||||
|
out << "d";
|
||||||
|
break;
|
||||||
|
default: //multiple stockpile type
|
||||||
|
return "`";
|
||||||
|
}
|
||||||
|
out << "("<< size.first << "x" << size.second << ")";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_tile_query(df::building* b)
|
||||||
|
{
|
||||||
|
if (b && b->is_room)
|
||||||
|
return "r+";
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result do_transform(DFCoord start, DFCoord end, string name, uint32_t phases)
|
||||||
|
{
|
||||||
|
ofstream dig, build, place, query;
|
||||||
|
if (phases & QUERY)
|
||||||
|
{
|
||||||
|
//query = ofstream((name + "-query.csv").c_str(), ofstream::trunc);
|
||||||
|
query.open(name+"-query.csv", ofstream::trunc);
|
||||||
|
query << "#query" << endl;
|
||||||
|
}
|
||||||
|
if (phases & PLACE)
|
||||||
|
{
|
||||||
|
//place = ofstream(name + "-place.csv", ofstream::trunc);
|
||||||
|
place.open(name+"-place.csv", ofstream::trunc);
|
||||||
|
place << "#place" << endl;
|
||||||
|
}
|
||||||
|
if (phases & BUILD)
|
||||||
|
{
|
||||||
|
//build = ofstream(name + "-build.csv", ofstream::trunc);
|
||||||
|
build.open(name+"-build.csv", ofstream::trunc);
|
||||||
|
build << "#build" << endl;
|
||||||
|
}
|
||||||
|
if (phases & DIG)
|
||||||
|
{
|
||||||
|
//dig = ofstream(name + "-dig.csv", ofstream::trunc);
|
||||||
|
dig.open(name+"-dig.csv", ofstream::trunc);
|
||||||
|
dig << "#dig" << endl;
|
||||||
|
}
|
||||||
|
if (start.x > end.x)
|
||||||
|
{
|
||||||
|
swap(start.x, end.x);
|
||||||
|
start.x++;
|
||||||
|
end.x++;
|
||||||
|
}
|
||||||
|
if (start.y > end.y)
|
||||||
|
{
|
||||||
|
swap(start.y, end.y);
|
||||||
|
start.y++;
|
||||||
|
end.y++;
|
||||||
|
}
|
||||||
|
if (start.z > end.z)
|
||||||
|
{
|
||||||
|
swap(start.z, end.z);
|
||||||
|
start.z++;
|
||||||
|
end.z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapExtras::MapCache mc;
|
||||||
|
for (int32_t z = start.z; z < end.z; z++)
|
||||||
|
{
|
||||||
|
for (int32_t y = start.y; y < end.y; y++)
|
||||||
|
{
|
||||||
|
for (int32_t x = start.x; x < end.x; x++)
|
||||||
|
{
|
||||||
|
df::building* b = DFHack::Buildings::findAtTile(DFCoord(x, y, z));
|
||||||
|
if (phases & QUERY)
|
||||||
|
query << get_tile_query(b) << ',';
|
||||||
|
if (phases & PLACE)
|
||||||
|
place << get_tile_place(x, y, b) << ',';
|
||||||
|
if (phases & BUILD)
|
||||||
|
build << get_tile_build(x, y, b) << ',';
|
||||||
|
if (phases & DIG)
|
||||||
|
dig << get_tile_dig(mc, x, y, z) << ',';
|
||||||
|
}
|
||||||
|
if (phases & QUERY)
|
||||||
|
query << "#" << endl;
|
||||||
|
if (phases & PLACE)
|
||||||
|
place << "#" << endl;
|
||||||
|
if (phases & BUILD)
|
||||||
|
build << "#" << endl;
|
||||||
|
if (phases & DIG)
|
||||||
|
dig << "#" << endl;
|
||||||
|
}
|
||||||
|
if (z < end.z - 1)
|
||||||
|
{
|
||||||
|
if (phases & QUERY)
|
||||||
|
query << "#<" << endl;
|
||||||
|
if (phases & PLACE)
|
||||||
|
place << "#<" << endl;
|
||||||
|
if (phases & BUILD)
|
||||||
|
build << "#<" << endl;
|
||||||
|
if (phases & DIG)
|
||||||
|
dig << "#<" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (phases & QUERY)
|
||||||
|
query.close();
|
||||||
|
if (phases & PLACE)
|
||||||
|
place.close();
|
||||||
|
if (phases & BUILD)
|
||||||
|
build.close();
|
||||||
|
if (phases & DIG)
|
||||||
|
dig.close();
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmd_option_exists(vector<string>& parameters, const string& option)
|
||||||
|
{
|
||||||
|
return find(parameters.begin(), parameters.end(), option) != parameters.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result blueprint(color_ostream &out, vector<string> ¶meters)
|
||||||
|
{
|
||||||
|
if (parameters.size() < 4 || parameters.size() > 8)
|
||||||
|
return help(out);
|
||||||
|
CoreSuspender suspend;
|
||||||
|
if (!Maps::IsValid())
|
||||||
|
{
|
||||||
|
out.printerr("Map is not available!\n");
|
||||||
|
return CR_FAILURE;
|
||||||
|
}
|
||||||
|
int32_t x, y, z;
|
||||||
|
if (!Gui::getCursorCoords(x, y, z))
|
||||||
|
{
|
||||||
|
out.printerr("Can't get cursor coords! Make sure you have an active cursor in DF.\n");
|
||||||
|
return CR_FAILURE;
|
||||||
|
}
|
||||||
|
DFCoord start (x, y, z);
|
||||||
|
DFCoord end (x + stoi(parameters[0]), y + stoi(parameters[1]), z + stoi(parameters[2]));
|
||||||
|
if (parameters.size() == 4)
|
||||||
|
return do_transform(start, end, parameters[3], DIG | BUILD | PLACE | QUERY);
|
||||||
|
uint32_t option = 0;
|
||||||
|
if (cmd_option_exists(parameters, "dig"))
|
||||||
|
option |= DIG;
|
||||||
|
if (cmd_option_exists(parameters, "build"))
|
||||||
|
option |= BUILD;
|
||||||
|
if (cmd_option_exists(parameters, "place"))
|
||||||
|
option |= PLACE;
|
||||||
|
if (cmd_option_exists(parameters, "query"))
|
||||||
|
option |= QUERY;
|
||||||
|
return do_transform(start, end, parameters[3], option);
|
||||||
|
}
|
@ -0,0 +1,660 @@
|
|||||||
|
#include "buildingplan-lib.h"
|
||||||
|
|
||||||
|
#define PLUGIN_VERSION 0.00
|
||||||
|
void debug(const string &msg)
|
||||||
|
{
|
||||||
|
if (!show_debugging)
|
||||||
|
return;
|
||||||
|
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
out << "DEBUG (" << PLUGIN_VERSION << "): " << msg << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_quickfort_fn(pair<const df::building_type, bool>& pair) { pair.second = true; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Material Choice Screen
|
||||||
|
*/
|
||||||
|
|
||||||
|
static std::string material_to_string_fn(DFHack::MaterialInfo m) { return m.toString(); }
|
||||||
|
|
||||||
|
bool ItemFilter::matchesMask(DFHack::MaterialInfo &mat)
|
||||||
|
{
|
||||||
|
return (mat_mask.whole) ? mat.matches(mat_mask) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemFilter::matches(const df::dfhack_material_category mask) const
|
||||||
|
{
|
||||||
|
return mask.whole & mat_mask.whole;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemFilter::matches(DFHack::MaterialInfo &material) const
|
||||||
|
{
|
||||||
|
for (auto it = materials.begin(); it != materials.end(); ++it)
|
||||||
|
if (material.matches(*it))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemFilter::matches(df::item *item)
|
||||||
|
{
|
||||||
|
if (item->getQuality() < min_quality)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (decorated_only && !item->hasImprovements())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto imattype = item->getActualMaterial();
|
||||||
|
auto imatindex = item->getActualMaterialIndex();
|
||||||
|
auto item_mat = DFHack::MaterialInfo(imattype, imatindex);
|
||||||
|
|
||||||
|
return (materials.size() == 0) ? matchesMask(item_mat) : matches(item_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ItemFilter::getMaterialFilterAsVector()
|
||||||
|
{
|
||||||
|
std::vector<std::string> descriptions;
|
||||||
|
|
||||||
|
transform_(materials, descriptions, material_to_string_fn);
|
||||||
|
|
||||||
|
if (descriptions.size() == 0)
|
||||||
|
bitfield_to_string(&descriptions, mat_mask);
|
||||||
|
|
||||||
|
if (descriptions.size() == 0)
|
||||||
|
descriptions.push_back("any");
|
||||||
|
|
||||||
|
return descriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ItemFilter::getMaterialFilterAsSerial()
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
|
||||||
|
str.append(bitfield_to_string(mat_mask, ","));
|
||||||
|
str.append("/");
|
||||||
|
if (materials.size() > 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < materials.size(); i++)
|
||||||
|
str.append(materials[i].getToken() + ",");
|
||||||
|
|
||||||
|
if (str[str.size()-1] == ',')
|
||||||
|
str.resize(str.size () - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemFilter::parseSerializedMaterialTokens(std::string str)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
split_string(&tokens, str, "/");
|
||||||
|
|
||||||
|
if (tokens.size() > 0 && !tokens[0].empty())
|
||||||
|
{
|
||||||
|
if (!parseJobMaterialCategory(&mat_mask, tokens[0]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens.size() > 1 && !tokens[1].empty())
|
||||||
|
{
|
||||||
|
std::vector<std::string> mat_names;
|
||||||
|
split_string(&mat_names, tokens[1], ",");
|
||||||
|
for (auto m = mat_names.begin(); m != mat_names.end(); m++)
|
||||||
|
{
|
||||||
|
DFHack::MaterialInfo material;
|
||||||
|
if (!material.find(*m) || !material.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
materials.push_back(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ItemFilter::getMinQuality()
|
||||||
|
{
|
||||||
|
return ENUM_KEY_STR(item_quality, min_quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemFilter::isValid()
|
||||||
|
{
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemFilter::clear()
|
||||||
|
{
|
||||||
|
mat_mask.whole = 0;
|
||||||
|
materials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DFHack::MaterialInfo &material_info_identity_fn(DFHack::MaterialInfo &m) { return m; }
|
||||||
|
|
||||||
|
ViewscreenChooseMaterial::ViewscreenChooseMaterial(ItemFilter *filter)
|
||||||
|
{
|
||||||
|
selected_column = 0;
|
||||||
|
masks_column.setTitle("Type");
|
||||||
|
masks_column.multiselect = true;
|
||||||
|
masks_column.allow_search = false;
|
||||||
|
masks_column.left_margin = 2;
|
||||||
|
materials_column.left_margin = MAX_MASK + 3;
|
||||||
|
materials_column.setTitle("Material");
|
||||||
|
materials_column.multiselect = true;
|
||||||
|
this->filter = filter;
|
||||||
|
|
||||||
|
masks_column.changeHighlight(0);
|
||||||
|
|
||||||
|
populateMasks();
|
||||||
|
populateMaterials();
|
||||||
|
|
||||||
|
masks_column.selectDefaultEntry();
|
||||||
|
materials_column.selectDefaultEntry();
|
||||||
|
materials_column.changeHighlight(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewscreenChooseMaterial::feed(set<df::interface_key> *input)
|
||||||
|
{
|
||||||
|
bool key_processed = false;
|
||||||
|
switch (selected_column)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
key_processed = masks_column.feed(input);
|
||||||
|
if (input->count(interface_key::SELECT))
|
||||||
|
populateMaterials(); // Redo materials lists based on category selection
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
key_processed = materials_column.feed(input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_processed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (input->count(interface_key::LEAVESCREEN))
|
||||||
|
{
|
||||||
|
input->clear();
|
||||||
|
Screen::dismiss(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (input->count(interface_key::CUSTOM_SHIFT_C))
|
||||||
|
{
|
||||||
|
filter->clear();
|
||||||
|
masks_column.clearSelection();
|
||||||
|
materials_column.clearSelection();
|
||||||
|
populateMaterials();
|
||||||
|
}
|
||||||
|
else if (input->count(interface_key::SEC_SELECT))
|
||||||
|
{
|
||||||
|
// Convert list selections to material filters
|
||||||
|
|
||||||
|
|
||||||
|
filter->mat_mask.whole = 0;
|
||||||
|
filter->materials.clear();
|
||||||
|
|
||||||
|
// Category masks
|
||||||
|
auto masks = masks_column.getSelectedElems();
|
||||||
|
for (auto it = masks.begin(); it != masks.end(); ++it)
|
||||||
|
filter->mat_mask.whole |= it->whole;
|
||||||
|
|
||||||
|
// Specific materials
|
||||||
|
auto materials = materials_column.getSelectedElems();
|
||||||
|
transform_(materials, filter->materials, material_info_identity_fn);
|
||||||
|
|
||||||
|
Screen::dismiss(this);
|
||||||
|
}
|
||||||
|
else if (input->count(interface_key::CURSOR_LEFT))
|
||||||
|
{
|
||||||
|
--selected_column;
|
||||||
|
validateColumn();
|
||||||
|
}
|
||||||
|
else if (input->count(interface_key::CURSOR_RIGHT))
|
||||||
|
{
|
||||||
|
selected_column++;
|
||||||
|
validateColumn();
|
||||||
|
}
|
||||||
|
else if (enabler->tracking_on && enabler->mouse_lbut)
|
||||||
|
{
|
||||||
|
if (masks_column.setHighlightByMouse())
|
||||||
|
selected_column = 0;
|
||||||
|
else if (materials_column.setHighlightByMouse())
|
||||||
|
selected_column = 1;
|
||||||
|
|
||||||
|
enabler->mouse_lbut = enabler->mouse_rbut = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewscreenChooseMaterial::render()
|
||||||
|
{
|
||||||
|
if (Screen::isDismissed(this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dfhack_viewscreen::render();
|
||||||
|
|
||||||
|
Screen::clear();
|
||||||
|
Screen::drawBorder(" Building Material ");
|
||||||
|
|
||||||
|
masks_column.display(selected_column == 0);
|
||||||
|
materials_column.display(selected_column == 1);
|
||||||
|
|
||||||
|
int32_t y = gps->dimy - 3;
|
||||||
|
int32_t x = 2;
|
||||||
|
OutputHotkeyString(x, y, "Toggle", "Enter");
|
||||||
|
x += 3;
|
||||||
|
OutputHotkeyString(x, y, "Save", "Shift-Enter");
|
||||||
|
x += 3;
|
||||||
|
OutputHotkeyString(x, y, "Clear", "C");
|
||||||
|
x += 3;
|
||||||
|
OutputHotkeyString(x, y, "Cancel", "Esc");
|
||||||
|
}
|
||||||
|
|
||||||
|
// START Room Reservation
|
||||||
|
ReservedRoom::ReservedRoom(df::building *building, std::string noble_code)
|
||||||
|
{
|
||||||
|
this->building = building;
|
||||||
|
config = DFHack::World::AddPersistentData("buildingplan/reservedroom");
|
||||||
|
config.val() = noble_code;
|
||||||
|
config.ival(1) = building->id;
|
||||||
|
pos = df::coord(building->centerx, building->centery, building->z);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReservedRoom::ReservedRoom(PersistentDataItem &config, color_ostream &out)
|
||||||
|
{
|
||||||
|
this->config = config;
|
||||||
|
|
||||||
|
building = df::building::find(config.ival(1));
|
||||||
|
if (!building)
|
||||||
|
return;
|
||||||
|
pos = df::coord(building->centerx, building->centery, building->z);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReservedRoom::checkRoomAssignment()
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto np = getOwnersNobleCode();
|
||||||
|
bool correctOwner = false;
|
||||||
|
for (auto iter = np.begin(); iter != np.end(); iter++)
|
||||||
|
{
|
||||||
|
if (iter->position->code == getCode())
|
||||||
|
{
|
||||||
|
correctOwner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correctOwner)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (auto iter = world->units.active.begin(); iter != world->units.active.end(); iter++)
|
||||||
|
{
|
||||||
|
df::unit* unit = *iter;
|
||||||
|
if (!Units::isCitizen(unit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (DFHack::Units::isDead(unit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
np = getUniqueNoblePositions(unit);
|
||||||
|
for (auto iter = np.begin(); iter != np.end(); iter++)
|
||||||
|
{
|
||||||
|
if (iter->position->code == getCode())
|
||||||
|
{
|
||||||
|
Buildings::setOwner(building, unit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RoomMonitor::getReservedNobleCode(int32_t buildingId)
|
||||||
|
{
|
||||||
|
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
|
||||||
|
{
|
||||||
|
if (buildingId == iter->getId())
|
||||||
|
return iter->getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomMonitor::toggleRoomForPosition(int32_t buildingId, std::string noble_code)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
|
||||||
|
{
|
||||||
|
if (buildingId != iter->getId())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (noble_code == iter->getCode())
|
||||||
|
{
|
||||||
|
iter->remove();
|
||||||
|
reservedRooms.erase(iter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->setCode(noble_code);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
ReservedRoom room(df::building::find(buildingId), noble_code);
|
||||||
|
reservedRooms.push_back(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomMonitor::doCycle()
|
||||||
|
{
|
||||||
|
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end();)
|
||||||
|
{
|
||||||
|
if (iter->checkRoomAssignment())
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->remove();
|
||||||
|
iter = reservedRooms.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomMonitor::reset(color_ostream &out)
|
||||||
|
{
|
||||||
|
reservedRooms.clear();
|
||||||
|
std::vector<PersistentDataItem> items;
|
||||||
|
DFHack::World::GetPersistentData(&items, "buildingplan/reservedroom");
|
||||||
|
|
||||||
|
for (auto i = items.begin(); i != items.end(); i++)
|
||||||
|
{
|
||||||
|
ReservedRoom rr(*i, out);
|
||||||
|
if (rr.isValid())
|
||||||
|
addRoom(rr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void delete_item_fn(df::job_item *x) { delete x; }
|
||||||
|
|
||||||
|
// START Planning
|
||||||
|
|
||||||
|
PlannedBuilding::PlannedBuilding(df::building *building, ItemFilter *filter)
|
||||||
|
{
|
||||||
|
this->building = building;
|
||||||
|
this->filter = *filter;
|
||||||
|
pos = df::coord(building->centerx, building->centery, building->z);
|
||||||
|
config = DFHack::World::AddPersistentData("buildingplan/constraints");
|
||||||
|
config.val() = filter->getMaterialFilterAsSerial();
|
||||||
|
config.ival(1) = building->id;
|
||||||
|
config.ival(2) = filter->min_quality + 1;
|
||||||
|
config.ival(3) = static_cast<int>(filter->decorated_only) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlannedBuilding::PlannedBuilding(PersistentDataItem &config, color_ostream &out)
|
||||||
|
{
|
||||||
|
this->config = config;
|
||||||
|
|
||||||
|
if (!filter.parseSerializedMaterialTokens(config.val()))
|
||||||
|
{
|
||||||
|
out.printerr("Buildingplan: Cannot parse filter: %s\nDiscarding.", config.val().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
building = df::building::find(config.ival(1));
|
||||||
|
if (!building)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pos = df::coord(building->centerx, building->centery, building->z);
|
||||||
|
filter.min_quality = static_cast<df::item_quality>(config.ival(2) - 1);
|
||||||
|
filter.decorated_only = config.ival(3) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlannedBuilding::assignClosestItem(std::vector<df::item *> *items_vector)
|
||||||
|
{
|
||||||
|
decltype(items_vector->begin()) closest_item;
|
||||||
|
int32_t closest_distance = -1;
|
||||||
|
for (auto item_iter = items_vector->begin(); item_iter != items_vector->end(); item_iter++)
|
||||||
|
{
|
||||||
|
auto item = *item_iter;
|
||||||
|
if (!filter.matches(item))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto pos = item->pos;
|
||||||
|
auto distance = abs(pos.x - building->centerx) +
|
||||||
|
abs(pos.y - building->centery) +
|
||||||
|
abs(pos.z - building->z) * 50;
|
||||||
|
|
||||||
|
if (closest_distance > -1 && distance >= closest_distance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
closest_distance = distance;
|
||||||
|
closest_item = item_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_distance > -1 && assignItem(*closest_item))
|
||||||
|
{
|
||||||
|
debug("Item assigned");
|
||||||
|
items_vector->erase(closest_item);
|
||||||
|
remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlannedBuilding::assignItem(df::item *item)
|
||||||
|
{
|
||||||
|
auto ref = df::allocate<df::general_ref_building_holderst>();
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
Core::printerr("Could not allocate general_ref_building_holderst\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref->building_id = building->id;
|
||||||
|
|
||||||
|
if (building->jobs.size() != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto job = building->jobs[0];
|
||||||
|
|
||||||
|
for_each_(job->job_items, delete_item_fn);
|
||||||
|
job->job_items.clear();
|
||||||
|
job->flags.bits.suspend = false;
|
||||||
|
|
||||||
|
bool rough = false;
|
||||||
|
Job::attachJobItem(job, item, df::job_item_ref::Hauled);
|
||||||
|
if (item->getType() == item_type::BOULDER)
|
||||||
|
rough = true;
|
||||||
|
building->mat_type = item->getMaterial();
|
||||||
|
building->mat_index = item->getMaterialIndex();
|
||||||
|
|
||||||
|
job->mat_type = building->mat_type;
|
||||||
|
job->mat_index = building->mat_index;
|
||||||
|
|
||||||
|
if (building->needsDesign())
|
||||||
|
{
|
||||||
|
auto act = (df::building_actual *) building;
|
||||||
|
act->design = new df::building_design();
|
||||||
|
act->design->flags.bits.rough = rough;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlannedBuilding::isValid()
|
||||||
|
{
|
||||||
|
bool valid = filter.isValid() &&
|
||||||
|
building && Buildings::findAtTile(pos) == building &&
|
||||||
|
building->getBuildStage() == 0;
|
||||||
|
|
||||||
|
if (!valid)
|
||||||
|
remove();
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Planner::reset(color_ostream &out)
|
||||||
|
{
|
||||||
|
planned_buildings.clear();
|
||||||
|
std::vector<PersistentDataItem> items;
|
||||||
|
DFHack::World::GetPersistentData(&items, "buildingplan/constraints");
|
||||||
|
|
||||||
|
for (auto i = items.begin(); i != items.end(); i++)
|
||||||
|
{
|
||||||
|
PlannedBuilding pb(*i, out);
|
||||||
|
if (pb.isValid())
|
||||||
|
planned_buildings.push_back(pb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Planner::initialize()
|
||||||
|
{
|
||||||
|
std::vector<std::string> item_names;
|
||||||
|
typedef df::enum_traits<df::item_type> item_types;
|
||||||
|
int size = item_types::last_item_value - item_types::first_item_value+1;
|
||||||
|
for (size_t i = 1; i < size; i++)
|
||||||
|
{
|
||||||
|
is_relevant_item_type[(df::item_type) (i-1)] = false;
|
||||||
|
std::string item_name = toLower(item_types::key_table[i]);
|
||||||
|
std::string item_name_clean;
|
||||||
|
for (auto c = item_name.begin(); c != item_name.end(); c++)
|
||||||
|
{
|
||||||
|
if (*c == '_')
|
||||||
|
continue;
|
||||||
|
item_name_clean += *c;
|
||||||
|
}
|
||||||
|
item_names.push_back(item_name_clean);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef df::enum_traits<df::building_type> building_types;
|
||||||
|
size = building_types::last_item_value - building_types::first_item_value+1;
|
||||||
|
for (size_t i = 1; i < size; i++)
|
||||||
|
{
|
||||||
|
auto building_type = (df::building_type) (i-1);
|
||||||
|
if (building_type == building_type::Weapon || building_type == building_type::Floodgate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string building_name = toLower(building_types::key_table[i]);
|
||||||
|
for (size_t j = 0; j < item_names.size(); j++)
|
||||||
|
{
|
||||||
|
if (building_name == item_names[j])
|
||||||
|
{
|
||||||
|
auto btype = (df::building_type) (i-1);
|
||||||
|
auto itype = (df::item_type) j;
|
||||||
|
|
||||||
|
item_for_building_type[btype] = itype;
|
||||||
|
default_item_filters[btype] = ItemFilter();
|
||||||
|
available_item_vectors[itype] = std::vector<df::item *>();
|
||||||
|
is_relevant_item_type[itype] = true;
|
||||||
|
|
||||||
|
if (planmode_enabled.find(btype) == planmode_enabled.end())
|
||||||
|
{
|
||||||
|
planmode_enabled[btype] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Planner::doCycle()
|
||||||
|
{
|
||||||
|
debug("Running Cycle");
|
||||||
|
if (planned_buildings.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
debug("Planned count: " + int_to_string(planned_buildings.size()));
|
||||||
|
|
||||||
|
gather_available_items();
|
||||||
|
for (auto building_iter = planned_buildings.begin(); building_iter != planned_buildings.end();)
|
||||||
|
{
|
||||||
|
if (building_iter->isValid())
|
||||||
|
{
|
||||||
|
if (show_debugging)
|
||||||
|
debug(std::string("Trying to allocate ") + enum_item_key_str(building_iter->getType()));
|
||||||
|
|
||||||
|
auto required_item_type = item_for_building_type[building_iter->getType()];
|
||||||
|
auto items_vector = &available_item_vectors[required_item_type];
|
||||||
|
if (items_vector->size() == 0 || !building_iter->assignClosestItem(items_vector))
|
||||||
|
{
|
||||||
|
debug("Unable to allocate an item");
|
||||||
|
++building_iter;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug("Removing building plan");
|
||||||
|
building_iter = planned_buildings.erase(building_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Planner::allocatePlannedBuilding(df::building_type type)
|
||||||
|
{
|
||||||
|
coord32_t cursor;
|
||||||
|
if (!DFHack::Gui::getCursorCoords(cursor.x, cursor.y, cursor.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto newinst = Buildings::allocInstance(cursor.get_coord16(), type);
|
||||||
|
if (!newinst)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
df::job_item *filter = new df::job_item();
|
||||||
|
filter->item_type = item_type::NONE;
|
||||||
|
filter->mat_index = 0;
|
||||||
|
filter->flags2.bits.building_material = true;
|
||||||
|
std::vector<df::job_item*> filters;
|
||||||
|
filters.push_back(filter);
|
||||||
|
|
||||||
|
if (!Buildings::constructWithFilters(newinst, filters))
|
||||||
|
{
|
||||||
|
delete newinst;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto iter = newinst->jobs.begin(); iter != newinst->jobs.end(); iter++)
|
||||||
|
{
|
||||||
|
(*iter)->flags.bits.suspend = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == building_type::Door)
|
||||||
|
{
|
||||||
|
auto door = virtual_cast<df::building_doorst>(newinst);
|
||||||
|
if (door)
|
||||||
|
door->door_flags.bits.pet_passable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlannedBuilding(newinst);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlannedBuilding *Planner::getSelectedPlannedBuilding()
|
||||||
|
{
|
||||||
|
for (auto building_iter = planned_buildings.begin(); building_iter != planned_buildings.end(); building_iter++)
|
||||||
|
{
|
||||||
|
if (building_iter->isCurrentlySelectedBuilding())
|
||||||
|
{
|
||||||
|
return &(*building_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Planner::cycleDefaultQuality(df::building_type type)
|
||||||
|
{
|
||||||
|
auto quality = &getDefaultItemFilterForType(type)->min_quality;
|
||||||
|
*quality = static_cast<df::item_quality>(*quality + 1);
|
||||||
|
if (*quality == item_quality::Artifact)
|
||||||
|
(*quality) = item_quality::Ordinary;
|
||||||
|
}
|
@ -0,0 +1,495 @@
|
|||||||
|
#ifndef BUILDINGPLAN_H
|
||||||
|
#define BUILDINGPLAN_H
|
||||||
|
|
||||||
|
#include "uicommon.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// DF data structure definition headers
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Types.h"
|
||||||
|
#include "df/build_req_choice_genst.h"
|
||||||
|
#include "df/build_req_choice_specst.h"
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/ui.h"
|
||||||
|
#include "df/ui_build_selector.h"
|
||||||
|
#include "df/viewscreen_dwarfmodest.h"
|
||||||
|
#include "df/items_other_id.h"
|
||||||
|
#include "df/job.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
#include "df/building_constructionst.h"
|
||||||
|
#include "df/building_design.h"
|
||||||
|
#include "df/entity_position.h"
|
||||||
|
|
||||||
|
#include "modules/Buildings.h"
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
#include "modules/Items.h"
|
||||||
|
#include "modules/Units.h"
|
||||||
|
#include "modules/Gui.h"
|
||||||
|
|
||||||
|
#include "TileTypes.h"
|
||||||
|
#include "df/job_item.h"
|
||||||
|
#include "df/dfhack_material_category.h"
|
||||||
|
#include "df/general_ref_building_holderst.h"
|
||||||
|
#include "modules/Job.h"
|
||||||
|
#include "df/building_design.h"
|
||||||
|
#include "df/buildings_other_id.h"
|
||||||
|
#include "modules/World.h"
|
||||||
|
#include "df/building.h"
|
||||||
|
#include "df/building_doorst.h"
|
||||||
|
|
||||||
|
using df::global::ui;
|
||||||
|
using df::global::ui_build_selector;
|
||||||
|
using df::global::world;
|
||||||
|
|
||||||
|
struct MaterialDescriptor
|
||||||
|
{
|
||||||
|
df::item_type item_type;
|
||||||
|
int16_t item_subtype;
|
||||||
|
int16_t type;
|
||||||
|
int32_t index;
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
bool matches(const MaterialDescriptor &a) const
|
||||||
|
{
|
||||||
|
return a.valid && valid &&
|
||||||
|
a.type == type &&
|
||||||
|
a.index == index &&
|
||||||
|
a.item_type == item_type &&
|
||||||
|
a.item_subtype == item_subtype;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_MASK 10
|
||||||
|
#define MAX_MATERIAL 21
|
||||||
|
#define SIDEBAR_WIDTH 30
|
||||||
|
|
||||||
|
static bool canReserveRoom(df::building *building)
|
||||||
|
{
|
||||||
|
if (!building)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (building->jobs.size() > 0 && building->jobs[0]->job_type == job_type::DestroyBuilding)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return building->is_room;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<Units::NoblePosition> getUniqueNoblePositions(df::unit *unit)
|
||||||
|
{
|
||||||
|
std::vector<Units::NoblePosition> np;
|
||||||
|
Units::getNoblePositions(&np, unit);
|
||||||
|
for (auto iter = np.begin(); iter != np.end(); iter++)
|
||||||
|
{
|
||||||
|
if (iter->position->code == "MILITIA_CAPTAIN")
|
||||||
|
{
|
||||||
|
np.erase(iter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delete_item_fn(df::job_item *x);
|
||||||
|
|
||||||
|
static MaterialInfo &material_info_identity_fn(MaterialInfo &m);
|
||||||
|
|
||||||
|
static map<df::building_type, bool> planmode_enabled, saved_planmodes;
|
||||||
|
|
||||||
|
void enable_quickfort_fn(pair<const df::building_type, bool>& pair);
|
||||||
|
|
||||||
|
void debug(const std::string &msg);
|
||||||
|
static std::string material_to_string_fn(MaterialInfo m);
|
||||||
|
|
||||||
|
static bool show_debugging = false;
|
||||||
|
static bool show_help = false;
|
||||||
|
|
||||||
|
struct ItemFilter
|
||||||
|
{
|
||||||
|
df::dfhack_material_category mat_mask;
|
||||||
|
std::vector<DFHack::MaterialInfo> materials;
|
||||||
|
df::item_quality min_quality;
|
||||||
|
bool decorated_only;
|
||||||
|
|
||||||
|
ItemFilter() : min_quality(df::item_quality::Ordinary), decorated_only(false), valid(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool matchesMask(DFHack::MaterialInfo &mat);
|
||||||
|
|
||||||
|
bool matches(const df::dfhack_material_category mask) const;
|
||||||
|
|
||||||
|
bool matches(DFHack::MaterialInfo &material) const;
|
||||||
|
|
||||||
|
bool matches(df::item *item);
|
||||||
|
|
||||||
|
std::vector<std::string> getMaterialFilterAsVector();
|
||||||
|
|
||||||
|
std::string getMaterialFilterAsSerial();
|
||||||
|
|
||||||
|
bool parseSerializedMaterialTokens(std::string str);
|
||||||
|
|
||||||
|
std::string getMinQuality();
|
||||||
|
|
||||||
|
bool isValid();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ViewscreenChooseMaterial : public dfhack_viewscreen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ViewscreenChooseMaterial(ItemFilter *filter);
|
||||||
|
|
||||||
|
void feed(set<df::interface_key> *input);
|
||||||
|
|
||||||
|
void render();
|
||||||
|
|
||||||
|
std::string getFocusString() { return "buildingplan_choosemat"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ListColumn<df::dfhack_material_category> masks_column;
|
||||||
|
ListColumn<MaterialInfo> materials_column;
|
||||||
|
int selected_column;
|
||||||
|
ItemFilter *filter;
|
||||||
|
|
||||||
|
df::building_type btype;
|
||||||
|
|
||||||
|
void addMaskEntry(df::dfhack_material_category &mask, const std::string &text)
|
||||||
|
{
|
||||||
|
auto entry = ListEntry<df::dfhack_material_category>(pad_string(text, MAX_MASK, false), mask);
|
||||||
|
if (filter->matches(mask))
|
||||||
|
entry.selected = true;
|
||||||
|
|
||||||
|
masks_column.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateMasks()
|
||||||
|
{
|
||||||
|
masks_column.clear();
|
||||||
|
df::dfhack_material_category mask;
|
||||||
|
|
||||||
|
mask.whole = 0;
|
||||||
|
mask.bits.stone = true;
|
||||||
|
addMaskEntry(mask, "Stone");
|
||||||
|
|
||||||
|
mask.whole = 0;
|
||||||
|
mask.bits.wood = true;
|
||||||
|
addMaskEntry(mask, "Wood");
|
||||||
|
|
||||||
|
mask.whole = 0;
|
||||||
|
mask.bits.metal = true;
|
||||||
|
addMaskEntry(mask, "Metal");
|
||||||
|
|
||||||
|
mask.whole = 0;
|
||||||
|
mask.bits.soap = true;
|
||||||
|
addMaskEntry(mask, "Soap");
|
||||||
|
|
||||||
|
masks_column.filterDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateMaterials()
|
||||||
|
{
|
||||||
|
materials_column.clear();
|
||||||
|
df::dfhack_material_category selected_category;
|
||||||
|
std::vector<df::dfhack_material_category> selected_masks = masks_column.getSelectedElems();
|
||||||
|
if (selected_masks.size() == 1)
|
||||||
|
selected_category = selected_masks[0];
|
||||||
|
else if (selected_masks.size() > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
df::world_raws &raws = world->raws;
|
||||||
|
for (int i = 1; i < DFHack::MaterialInfo::NUM_BUILTIN; i++)
|
||||||
|
{
|
||||||
|
auto obj = raws.mat_table.builtin[i];
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
MaterialInfo material;
|
||||||
|
material.decode(i, -1);
|
||||||
|
addMaterialEntry(selected_category, material, material.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < raws.inorganics.size(); i++)
|
||||||
|
{
|
||||||
|
df::inorganic_raw *p = raws.inorganics[i];
|
||||||
|
MaterialInfo material;
|
||||||
|
material.decode(0, i);
|
||||||
|
addMaterialEntry(selected_category, material, material.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(selected_category) wood_flag;
|
||||||
|
wood_flag.bits.wood = true;
|
||||||
|
if (!selected_category.whole || selected_category.bits.wood)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < raws.plants.all.size(); i++)
|
||||||
|
{
|
||||||
|
df::plant_raw *p = raws.plants.all[i];
|
||||||
|
for (size_t j = 0; p->material.size() > 1 && j < p->material.size(); j++)
|
||||||
|
{
|
||||||
|
auto t = p->material[j];
|
||||||
|
if (p->material[j]->id != "WOOD")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MaterialInfo material;
|
||||||
|
material.decode(DFHack::MaterialInfo::PLANT_BASE+j, i);
|
||||||
|
auto name = material.toString();
|
||||||
|
ListEntry<MaterialInfo> entry(pad_string(name, MAX_MATERIAL, false), material);
|
||||||
|
if (filter->matches(material))
|
||||||
|
entry.selected = true;
|
||||||
|
|
||||||
|
materials_column.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
materials_column.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMaterialEntry(df::dfhack_material_category &selected_category,
|
||||||
|
MaterialInfo &material, std::string name)
|
||||||
|
{
|
||||||
|
if (!selected_category.whole || material.matches(selected_category))
|
||||||
|
{
|
||||||
|
ListEntry<MaterialInfo> entry(pad_string(name, MAX_MATERIAL, false), material);
|
||||||
|
if (filter->matches(material))
|
||||||
|
entry.selected = true;
|
||||||
|
|
||||||
|
materials_column.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateColumn()
|
||||||
|
{
|
||||||
|
set_to_limit(selected_column, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(int32_t x, int32_t y)
|
||||||
|
{
|
||||||
|
dfhack_viewscreen::resize(x, y);
|
||||||
|
masks_column.resize();
|
||||||
|
materials_column.resize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReservedRoom
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReservedRoom(df::building *building, std::string noble_code);
|
||||||
|
|
||||||
|
ReservedRoom(PersistentDataItem &config, color_ostream &out);
|
||||||
|
|
||||||
|
bool checkRoomAssignment();
|
||||||
|
|
||||||
|
void remove() { DFHack::World::DeletePersistentData(config); }
|
||||||
|
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
if (!building)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Buildings::findAtTile(pos) != building)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return canReserveRoom(building);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getId()
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return building->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getCode() { return config.val(); }
|
||||||
|
|
||||||
|
void setCode(const std::string &noble_code) { config.val() = noble_code; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
df::building *building;
|
||||||
|
PersistentDataItem config;
|
||||||
|
df::coord pos;
|
||||||
|
|
||||||
|
std::vector<Units::NoblePosition> getOwnersNobleCode()
|
||||||
|
{
|
||||||
|
if (!building->owner)
|
||||||
|
return std::vector<Units::NoblePosition> ();
|
||||||
|
|
||||||
|
return getUniqueNoblePositions(building->owner);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RoomMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RoomMonitor() { }
|
||||||
|
|
||||||
|
std::string getReservedNobleCode(int32_t buildingId);
|
||||||
|
|
||||||
|
void toggleRoomForPosition(int32_t buildingId, std::string noble_code);
|
||||||
|
|
||||||
|
void doCycle();
|
||||||
|
|
||||||
|
void reset(color_ostream &out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<ReservedRoom> reservedRooms;
|
||||||
|
|
||||||
|
void addRoom(ReservedRoom &rr)
|
||||||
|
{
|
||||||
|
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
|
||||||
|
{
|
||||||
|
if (iter->getId() == rr.getId())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reservedRooms.push_back(rr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// START Planning
|
||||||
|
class PlannedBuilding
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlannedBuilding(df::building *building, ItemFilter *filter);
|
||||||
|
|
||||||
|
PlannedBuilding(PersistentDataItem &config, color_ostream &out);
|
||||||
|
|
||||||
|
bool assignClosestItem(std::vector<df::item *> *items_vector);
|
||||||
|
|
||||||
|
bool assignItem(df::item *item);
|
||||||
|
|
||||||
|
bool isValid();
|
||||||
|
|
||||||
|
df::building_type getType() { return building->getType(); }
|
||||||
|
|
||||||
|
bool isCurrentlySelectedBuilding() { return isValid() && (building == world->selected_building); }
|
||||||
|
|
||||||
|
ItemFilter *getFilter() { return &filter; }
|
||||||
|
|
||||||
|
void remove() { DFHack::World::DeletePersistentData(config); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
df::building *building;
|
||||||
|
PersistentDataItem config;
|
||||||
|
df::coord pos;
|
||||||
|
ItemFilter filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Planner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool in_dummmy_screen;
|
||||||
|
|
||||||
|
Planner() : quickfort_mode(false), in_dummmy_screen(false) { }
|
||||||
|
|
||||||
|
bool isPlanableBuilding(const df::building_type type) const
|
||||||
|
{
|
||||||
|
return item_for_building_type.find(type) != item_for_building_type.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(color_ostream &out);
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
void addPlannedBuilding(df::building *bld)
|
||||||
|
{
|
||||||
|
PlannedBuilding pb(bld, &default_item_filters[bld->getType()]);
|
||||||
|
planned_buildings.push_back(pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doCycle();
|
||||||
|
|
||||||
|
bool allocatePlannedBuilding(df::building_type type);
|
||||||
|
|
||||||
|
PlannedBuilding *getSelectedPlannedBuilding();
|
||||||
|
|
||||||
|
void removeSelectedPlannedBuilding() { getSelectedPlannedBuilding()->remove(); }
|
||||||
|
|
||||||
|
ItemFilter *getDefaultItemFilterForType(df::building_type type) { return &default_item_filters[type]; }
|
||||||
|
|
||||||
|
void cycleDefaultQuality(df::building_type type);
|
||||||
|
|
||||||
|
void enableQuickfortMode()
|
||||||
|
{
|
||||||
|
saved_planmodes = planmode_enabled;
|
||||||
|
for_each_(planmode_enabled, enable_quickfort_fn);
|
||||||
|
|
||||||
|
quickfort_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableQuickfortMode()
|
||||||
|
{
|
||||||
|
planmode_enabled = saved_planmodes;
|
||||||
|
quickfort_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inQuickFortMode() { return quickfort_mode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
map<df::building_type, df::item_type> item_for_building_type;
|
||||||
|
map<df::building_type, ItemFilter> default_item_filters;
|
||||||
|
map<df::item_type, std::vector<df::item *>> available_item_vectors;
|
||||||
|
map<df::item_type, bool> is_relevant_item_type; //Needed for fast check when looping over all items
|
||||||
|
bool quickfort_mode;
|
||||||
|
|
||||||
|
std::vector<PlannedBuilding> planned_buildings;
|
||||||
|
|
||||||
|
void gather_available_items()
|
||||||
|
{
|
||||||
|
debug("Gather available items");
|
||||||
|
for (auto iter = available_item_vectors.begin(); iter != available_item_vectors.end(); iter++)
|
||||||
|
{
|
||||||
|
iter->second.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precompute a bitmask with the bad flags
|
||||||
|
df::item_flags bad_flags;
|
||||||
|
bad_flags.whole = 0;
|
||||||
|
|
||||||
|
#define F(x) bad_flags.bits.x = true;
|
||||||
|
F(dump); F(forbid); F(garbage_collect);
|
||||||
|
F(hostile); F(on_fire); F(rotten); F(trader);
|
||||||
|
F(in_building); F(construction); F(artifact);
|
||||||
|
#undef F
|
||||||
|
|
||||||
|
std::vector<df::item*> &items = world->items.other[items_other_id::IN_PLAY];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < items.size(); i++)
|
||||||
|
{
|
||||||
|
df::item *item = items[i];
|
||||||
|
|
||||||
|
if (item->flags.whole & bad_flags.whole)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
df::item_type itype = item->getType();
|
||||||
|
if (!is_relevant_item_type[itype])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (itype == item_type::BOX && item->isBag())
|
||||||
|
continue; //Skip bags
|
||||||
|
|
||||||
|
if (item->flags.bits.artifact)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (item->flags.bits.in_job ||
|
||||||
|
item->isAssignedToStockpile() ||
|
||||||
|
item->flags.bits.owned ||
|
||||||
|
item->flags.bits.in_chest)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
available_item_vectors[itype].push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static Planner planner;
|
||||||
|
|
||||||
|
static RoomMonitor roomMonitor;
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,375 @@
|
|||||||
|
#include "buildingplan-lib.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include "modules/Filesystem.h"
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("fortplan");
|
||||||
|
#define PLUGIN_VERSION 0.15
|
||||||
|
|
||||||
|
command_result fortplan(color_ostream &out, vector<string> & params);
|
||||||
|
|
||||||
|
struct BuildingInfo {
|
||||||
|
std::string code;
|
||||||
|
df::building_type type;
|
||||||
|
df::furnace_type furnaceType;
|
||||||
|
df::workshop_type workshopType;
|
||||||
|
df::trap_type trapType;
|
||||||
|
std::string name;
|
||||||
|
bool variableSize;
|
||||||
|
int defaultHeight;
|
||||||
|
int defaultWidth;
|
||||||
|
bool hasCustomOptions;
|
||||||
|
|
||||||
|
BuildingInfo(std::string theCode, df::building_type theType, std::string theName, int height, int width) {
|
||||||
|
code = theCode;
|
||||||
|
type = theType;
|
||||||
|
name = theName;
|
||||||
|
variableSize = false;
|
||||||
|
defaultHeight = height;
|
||||||
|
defaultWidth = width;
|
||||||
|
hasCustomOptions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocate() {
|
||||||
|
return planner.allocatePlannedBuilding(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MatchesCode
|
||||||
|
{
|
||||||
|
std::string _code;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MatchesCode(const std::string &code) : _code(code) {}
|
||||||
|
|
||||||
|
bool operator()(const BuildingInfo &check) const
|
||||||
|
{
|
||||||
|
return check.code == _code;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<BuildingInfo> buildings;
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands) {
|
||||||
|
commands.push_back(PluginCommand("fortplan","Lay out buildings in your fortress based on a Quickfort-style CSV input file.",fortplan,false,
|
||||||
|
"Lay out buildings in your fortress based on a Quickfort-style CSV input file.\n"
|
||||||
|
"Usage: fortplan [filename]\n"));
|
||||||
|
|
||||||
|
buildings.push_back(BuildingInfo("c",df::building_type::Chair,"Chair",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("b",df::building_type::Bed,"Bed",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("t",df::building_type::Table,"Table",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("n",df::building_type::Coffin,"Coffin",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("d",df::building_type::Door,"Door",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("x",df::building_type::Floodgate,"Floodgate",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("h",df::building_type::Box,"Box",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("r",df::building_type::Weaponrack,"Weapon Rack",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("a",df::building_type::Armorstand,"Armor Stand",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("f",df::building_type::Cabinet,"Cabinet",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("s",df::building_type::Statue,"Statue",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("y",df::building_type::WindowGlass,"Glass Window",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("m",df::building_type::AnimalTrap,"Animal Trap",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("v",df::building_type::Chain,"Chain",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("j",df::building_type::Cage,"Cage",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("H",df::building_type::Hatch,"Floor Hatch",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("W",df::building_type::GrateWall,"Wall Grate",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("G",df::building_type::GrateFloor,"Floor Grate",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("B",df::building_type::BarsVertical,"Vertical Bars",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("~b",df::building_type::BarsFloor,"Floor Bars",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("R",df::building_type::TractionBench,"Traction Bench",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("~s",df::building_type::Slab,"Slab",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("N",df::building_type::NestBox,"Nest Box",1,1));
|
||||||
|
buildings.push_back(BuildingInfo("~h",df::building_type::Hive,"Hive",1,1));
|
||||||
|
|
||||||
|
planner.initialize();
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DAY_TICKS 1200
|
||||||
|
DFhackCExport command_result plugin_onupdate(color_ostream &out)
|
||||||
|
{
|
||||||
|
static decltype(world->frame_counter) last_frame_count = 0;
|
||||||
|
if ((world->frame_counter - last_frame_count) >= DAY_TICKS/2)
|
||||||
|
{
|
||||||
|
last_frame_count = world->frame_counter;
|
||||||
|
planner.doCycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
|
||||||
|
{
|
||||||
|
if (!gps)
|
||||||
|
return CR_FAILURE;
|
||||||
|
|
||||||
|
if (enable != is_enabled)
|
||||||
|
{
|
||||||
|
planner.reset(out);
|
||||||
|
|
||||||
|
is_enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<std::string>> tokenizeFile(std::string filename) {
|
||||||
|
std::ifstream infile(filename.c_str());
|
||||||
|
std::vector<std::vector<std::string>> fileTokens(128, std::vector<std::string>(128));
|
||||||
|
std::vector<std::vector<std::string>>::size_type x, y;
|
||||||
|
if (!infile.good()) {
|
||||||
|
throw -1;
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
y = 0;
|
||||||
|
while (std::getline(infile, line)) {
|
||||||
|
x = 0;
|
||||||
|
if (strcmp(line.substr(0,1).c_str(),"#")==0) {
|
||||||
|
fileTokens[y++][0] = line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = 0;
|
||||||
|
auto nextInd = line.find(',');
|
||||||
|
std::string curCell = line.substr(start,nextInd-start);
|
||||||
|
do {
|
||||||
|
fileTokens[y][x] = curCell;
|
||||||
|
start = nextInd+1;
|
||||||
|
nextInd = line.find(',',start);
|
||||||
|
curCell = line.substr(start,nextInd-start);
|
||||||
|
x++;
|
||||||
|
} while (nextInd != line.npos);
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
return fileTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result fortplan(color_ostream &out, vector<string> & params) {
|
||||||
|
|
||||||
|
auto & con = out;
|
||||||
|
std::vector<std::vector<std::string>> layout(128, std::vector<std::string>(128));
|
||||||
|
if (params.size()) {
|
||||||
|
coord32_t cursor;
|
||||||
|
coord32_t userCursor;
|
||||||
|
coord32_t startCursor;
|
||||||
|
if (!DFHack::Gui::getCursorCoords(cursor.x, cursor.y, cursor.z)) {
|
||||||
|
con.print("You must have an active in-game cursor.\n");
|
||||||
|
return CR_FAILURE;
|
||||||
|
}
|
||||||
|
DFHack::Gui::getCursorCoords(startCursor.x, startCursor.y, startCursor.z);
|
||||||
|
userCursor = startCursor;
|
||||||
|
|
||||||
|
std::string cwd = Filesystem::getcwd();
|
||||||
|
std::string filename = cwd+"/"+params[0];
|
||||||
|
con.print("Loading file '%s'...\n",filename.c_str());
|
||||||
|
try {
|
||||||
|
layout = tokenizeFile(filename);
|
||||||
|
} catch (int e) {
|
||||||
|
con.print("Could not open the file.\n");
|
||||||
|
return CR_FAILURE;
|
||||||
|
}
|
||||||
|
if (!is_enabled) {
|
||||||
|
plugin_enable(out, true);
|
||||||
|
}
|
||||||
|
con.print("Loaded.\n");
|
||||||
|
std::vector<std::vector<std::string>>::size_type x, y;
|
||||||
|
bool started = false;
|
||||||
|
for (y = 0; y < layout.size(); y++) {
|
||||||
|
x = 0;
|
||||||
|
auto hashBuild = layout[y][x].find("#build");
|
||||||
|
if (hashBuild != layout[y][x].npos) {
|
||||||
|
auto startLoc = layout[y][x].find("start(");
|
||||||
|
if (startLoc != layout[y][x].npos) {
|
||||||
|
startLoc += 6;
|
||||||
|
auto nextDelimiter = layout[y][x].find(";",startLoc);
|
||||||
|
std::string startXStr = layout[y][x].substr(startLoc,nextDelimiter-startLoc);
|
||||||
|
int startXOffset = std::stoi(startXStr);
|
||||||
|
startLoc = nextDelimiter+1;
|
||||||
|
nextDelimiter = layout[y][x].find(";",startLoc);
|
||||||
|
std::string startYStr = layout[y][x].substr(startLoc,nextDelimiter-startLoc);
|
||||||
|
int startYOffset = std::stoi(startYStr);
|
||||||
|
startCursor.x -= startXOffset;
|
||||||
|
startCursor.y -= startYOffset;
|
||||||
|
DFHack::Gui::setCursorCoords(startCursor.x,startCursor.y,startCursor.z);
|
||||||
|
started = true;
|
||||||
|
|
||||||
|
auto startEnd = layout[y][x].find(")",nextDelimiter);
|
||||||
|
|
||||||
|
con.print("Starting at (%d,%d,%d) which is described as: %s\n",startCursor.x,startCursor.y,startCursor.z,layout[y][x].substr(nextDelimiter+1,startEnd-nextDelimiter).c_str());
|
||||||
|
std::string desc = layout[y][x].substr(startEnd+1);
|
||||||
|
if (desc.size()>0) {
|
||||||
|
con.print("Description of this plan: %s\n",desc.c_str());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
con.print("No start location found for this block\n");
|
||||||
|
}
|
||||||
|
} else if (!started) {
|
||||||
|
con.print("Not a build file: %s\n",layout[y][x].c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < layout[y].size(); x++) {
|
||||||
|
if (strcmp(layout[y][x].substr(0,1).c_str(),"#")==0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(layout[y][x].c_str(),"`")!=0) {
|
||||||
|
auto dataIndex = layout[y][x].find("(");
|
||||||
|
std::string curCode;
|
||||||
|
std::vector<std::string> curData;
|
||||||
|
if (dataIndex != layout[y][x].npos) {
|
||||||
|
curCode = layout[y][x].substr(0,dataIndex);
|
||||||
|
int dataStart = dataIndex+1;
|
||||||
|
auto nextDataStart = layout[y][x].find(",",dataStart);
|
||||||
|
while (nextDataStart!=layout[y][x].npos) {
|
||||||
|
std::string nextData = layout[y][x].substr(dataStart,nextDataStart);
|
||||||
|
if (strcmp(nextData.substr(nextData.size()-1,1).c_str(),")")==0) {
|
||||||
|
nextData = nextData.substr(0,nextData.size()-1);
|
||||||
|
}
|
||||||
|
curData.push_back(nextData);
|
||||||
|
dataStart = nextDataStart+1;
|
||||||
|
nextDataStart = layout[y][x].find(",",dataStart);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
curCode = layout[y][x];
|
||||||
|
}
|
||||||
|
//con.print("Found a cell with '%s' in it (layout[y][x] %d:%d-%d)\n",layout[y][x].c_str(),lineNum,start,nextInd);
|
||||||
|
auto buildingIndex = std::find_if(buildings.begin(), buildings.end(), MatchesCode(curCode.c_str()));
|
||||||
|
|
||||||
|
// = std::find(validInstructions.begin(), validInstructions.end(), layout[y][x]);
|
||||||
|
if(buildingIndex == buildings.end()) {
|
||||||
|
//con.print("That is not a valid code.\n");
|
||||||
|
} else {
|
||||||
|
//con.print("I can build that!\n");
|
||||||
|
BuildingInfo buildingInfo = *buildingIndex;
|
||||||
|
if (buildingInfo.variableSize || buildingInfo.defaultHeight > 1 || buildingInfo.defaultWidth > 1) {
|
||||||
|
//con.print("Found a building at (%d,%d) called %s, which has a size %dx%d or variable size\n",x,y,buildingInfo.name.c_str(),buildingInfo.defaultWidth, buildingInfo.defaultHeight);
|
||||||
|
// TODO: Make this function smarter, able to determine the exact shape
|
||||||
|
// and location of the building
|
||||||
|
// For now, we just assume that we are always looking at the top left
|
||||||
|
// corner of where it is located in the input file
|
||||||
|
coord32_t buildingSize;
|
||||||
|
if (!buildingInfo.variableSize) {
|
||||||
|
bool single = true;
|
||||||
|
bool block = true;
|
||||||
|
std::vector<std::vector<std::string>>::size_type checkX, checkY;
|
||||||
|
int yOffset = ((buildingInfo.defaultHeight-1)/2);
|
||||||
|
int xOffset = ((buildingInfo.defaultWidth-1)/2);
|
||||||
|
//con.print(" - Checking to see if it's a single, with %d<=x<%d and %d<=y<%d...\n",x-xOffset,x+xOffset,y-yOffset,y+yOffset);
|
||||||
|
// First, check to see if this is in the center of an empty square of its default size
|
||||||
|
for (checkY = y-yOffset; checkY <= (y+yOffset); checkY++) {
|
||||||
|
for (checkX = x-xOffset; checkX <= (x+xOffset); checkX++) {
|
||||||
|
if (checkX==x && checkY==y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto checkDataIndex = layout[checkY][checkX].find("(");
|
||||||
|
std::string checkCode;
|
||||||
|
if (checkDataIndex != layout[checkY][checkX].npos) {
|
||||||
|
checkCode = layout[checkY][checkX].substr(0,checkDataIndex);
|
||||||
|
} else {
|
||||||
|
checkCode = layout[checkY][checkX];
|
||||||
|
}
|
||||||
|
|
||||||
|
con.print(" - Code at (%d,%d) is '%s': ",checkX,checkY,checkCode.c_str());
|
||||||
|
auto checkIndex = std::find_if(buildings.begin(), buildings.end(), MatchesCode(checkCode.c_str()));
|
||||||
|
//if (checkIndex == buildings.end()) {
|
||||||
|
// con.print("this is not a valid code, so we keep going.\n");
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
//if (curCode.compare(layout[checkY][checkX]) != 0) {
|
||||||
|
if (checkIndex != buildings.end()) {
|
||||||
|
//con.print("this is a building, so we break.\n");
|
||||||
|
single = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
//con.print("this is not a building, so we keep going.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!single) {
|
||||||
|
//con.print("Not a single. Moving forward.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!single) {
|
||||||
|
// If that's not the case, check to see if this is the top-left corner of
|
||||||
|
// a square of its default size
|
||||||
|
//con.print(" - It's not single; checking to see if it's a block...\n");
|
||||||
|
for (checkY = y; checkY < (y+buildingInfo.defaultHeight); checkY++) {
|
||||||
|
for (checkX = x; checkX < (x+buildingInfo.defaultWidth); checkX++) {
|
||||||
|
if (checkX==x && checkY==y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto checkDataIndex = layout[checkY][checkX].find("(");
|
||||||
|
std::string checkCode;
|
||||||
|
if (checkDataIndex != layout[checkY][checkX].npos) {
|
||||||
|
checkCode = layout[checkY][checkX].substr(0,checkDataIndex);
|
||||||
|
} else {
|
||||||
|
checkCode = layout[checkY][checkX];
|
||||||
|
}
|
||||||
|
|
||||||
|
//con.print(" - Code at (%d,%d) is '%s': ",checkX,checkY,checkCode.c_str());
|
||||||
|
if (curCode.compare(checkCode) != 0) {
|
||||||
|
//con.print("this is not the same as '%s', so we break.\n",curCode.c_str());
|
||||||
|
block = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
//con.print("this is the same as '%s', so we erase it and move on.\n",curCode.c_str());
|
||||||
|
layout[checkY][checkX] = "``";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!block) {
|
||||||
|
//con.print("Not a block. Moving forward.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (single) {
|
||||||
|
//con.print("Placing a building with code '%s' centered at (%d,%d) and default size %dx%d.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
|
||||||
|
coord32_t offsetCursor = cursor;
|
||||||
|
offsetCursor.x -= xOffset;
|
||||||
|
offsetCursor.y -= yOffset;
|
||||||
|
DFHack::Gui::setCursorCoords(offsetCursor.x, offsetCursor.y, offsetCursor.z);
|
||||||
|
if (!buildingInfo.allocate()) {
|
||||||
|
con.print("*** There was an error placing building with code '%s' centered at (%d,%d).\n",curCode.c_str(),x,y);
|
||||||
|
}
|
||||||
|
DFHack::Gui::setCursorCoords(cursor.x, cursor.y, cursor.z);
|
||||||
|
} else if (block) {
|
||||||
|
//con.print("Placing a building with code '%s' with corner at (%d,%d) and default size %dx%d.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
|
||||||
|
if (!buildingInfo.allocate()) {
|
||||||
|
con.print("*** There was an error placing building with code '%s' with corner at (%d,%d).\n",curCode.c_str(),x,y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.print("*** Found a code '%s' at (%d,%d) for a building with default size %dx%d with an invalid size designation.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//buildingSize = findBuildingExtent(layout, x, y, -1, -1, out);
|
||||||
|
//con.print(" - The building has variable size %dx%d\n",buildingSize.x, buildingSize.y);
|
||||||
|
//con.print(" - The building has a variable size, which is not yet handled.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//con.print("Building a(n) %s.\n",buildingInfo.name.c_str());
|
||||||
|
if (!buildingInfo.allocate()) {
|
||||||
|
con.print("*** There was an error placing the %s at (%d,%d).\n",buildingInfo.name.c_str(),x,y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.x++;
|
||||||
|
DFHack::Gui::setCursorCoords(cursor.x, cursor.y, cursor.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.y++;
|
||||||
|
cursor.x = startCursor.x;
|
||||||
|
|
||||||
|
}
|
||||||
|
DFHack::Gui::setCursorCoords(userCursor.x, userCursor.y, userCursor.z);
|
||||||
|
con.print("Done with file.\n");
|
||||||
|
} else {
|
||||||
|
con.print("You must supply a filename to read.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
@ -0,0 +1,199 @@
|
|||||||
|
local _ENV = mkmodule('plugins.stockpiles')
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
Native functions:
|
||||||
|
|
||||||
|
* stockpiles_list_settings(dir_path), list files in directory
|
||||||
|
* stockpiles_load(file), with full path
|
||||||
|
* stockpiles_save(file), with full path
|
||||||
|
* isEnabled()
|
||||||
|
|
||||||
|
--]]
|
||||||
|
--
|
||||||
|
function safe_require(module)
|
||||||
|
local status, module = pcall(require, module)
|
||||||
|
return status and module or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local gui = require 'gui'
|
||||||
|
local widgets = require('gui.widgets')
|
||||||
|
local dlg = require('gui.dialogs')
|
||||||
|
local script = require 'gui.script'
|
||||||
|
local persist = safe_require('persist-table')
|
||||||
|
|
||||||
|
|
||||||
|
function ListFilterDialog(args)
|
||||||
|
args.text = args.prompt or 'Type or select an option'
|
||||||
|
args.text_pen = COLOR_WHITE
|
||||||
|
args.with_filter = true
|
||||||
|
args.icon_width = 2
|
||||||
|
|
||||||
|
local choices = {}
|
||||||
|
|
||||||
|
if not args.hide_none then
|
||||||
|
table.insert(choices, {
|
||||||
|
icon = '?', text = args.none_caption or 'none',
|
||||||
|
index = -1, name = -1
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local filter = args.item_filter
|
||||||
|
|
||||||
|
for i,v in ipairs(args.items) do
|
||||||
|
if not filter or filter(v,-1) then
|
||||||
|
local name = v
|
||||||
|
local icon
|
||||||
|
table.insert(choices, {
|
||||||
|
icon = icon, search_key = string.lower(name), text = name, index = i
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
args.choices = choices
|
||||||
|
|
||||||
|
if args.on_select then
|
||||||
|
local cb = args.on_select
|
||||||
|
args.on_select = function(idx, obj)
|
||||||
|
return cb(obj.index, args.items[obj.index])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return dlg.ListBox(args)
|
||||||
|
end
|
||||||
|
|
||||||
|
function showFilterPrompt(title, list, text,item_filter,hide_none)
|
||||||
|
ListFilterDialog{
|
||||||
|
frame_title=title,
|
||||||
|
items=list,
|
||||||
|
prompt=text,
|
||||||
|
item_filter=item_filter,
|
||||||
|
hide_none=hide_none,
|
||||||
|
on_select=script.mkresume(true),
|
||||||
|
on_cancel=script.mkresume(false),
|
||||||
|
on_close=script.qresume(nil)
|
||||||
|
}:show()
|
||||||
|
|
||||||
|
return script.wait()
|
||||||
|
end
|
||||||
|
|
||||||
|
function init()
|
||||||
|
if persist == nil then return end
|
||||||
|
if dfhack.isMapLoaded() then
|
||||||
|
if persist.GlobalTable.stockpiles == nil then
|
||||||
|
persist.GlobalTable.stockpiles = {}
|
||||||
|
persist.GlobalTable.stockpiles['settings_path'] = './stocksettings'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tablify(iterableObject)
|
||||||
|
t={}
|
||||||
|
for k,v in ipairs(iterableObject) do
|
||||||
|
t[k] = v~=nil and v or 'nil'
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function load_settings()
|
||||||
|
init()
|
||||||
|
local path = get_path()
|
||||||
|
local ok, list = pcall(stockpiles_list_settings, path)
|
||||||
|
if not ok then
|
||||||
|
show_message_box("Stockpile Settings", "The stockpile settings folder doesn't exist.", true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if #list == 0 then
|
||||||
|
show_message_box("Stockpile Settings", "There are no saved stockpile settings.", true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local choice_list = {}
|
||||||
|
for i,v in ipairs(list) do
|
||||||
|
choice_list[i] = string.gsub(v, "/", "/ ")
|
||||||
|
choice_list[i] = string.gsub(choice_list[i], "-", " - ")
|
||||||
|
choice_list[i] = string.gsub(choice_list[i], "_", " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
script.start(function()
|
||||||
|
local ok2,index,name=showFilterPrompt('Stockpile Settings', choice_list, 'Choose a stockpile', function(item) return true end, true)
|
||||||
|
if ok2 then
|
||||||
|
local filename = list[index];
|
||||||
|
stockpiles_load(path..'/'..filename)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function save_settings(stockpile)
|
||||||
|
init()
|
||||||
|
script.start(function()
|
||||||
|
local suggested = stockpile.name
|
||||||
|
if #suggested == 0 then
|
||||||
|
suggested = 'Stock1'
|
||||||
|
end
|
||||||
|
local path = get_path()
|
||||||
|
local sok,filename = script.showInputPrompt('Stockpile Settings', 'Enter stockpile name', COLOR_WHITE, suggested)
|
||||||
|
if sok then
|
||||||
|
if filename == nil or filename == '' then
|
||||||
|
script.showMessage('Stockpile Settings', 'Invalid File Name', COLOR_RED)
|
||||||
|
else
|
||||||
|
print("saving...", path..'/'..filename)
|
||||||
|
stockpiles_save(path..'/'..filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function manage_settings(sp)
|
||||||
|
init()
|
||||||
|
if not guard() then return false end
|
||||||
|
script.start(function()
|
||||||
|
local list = {'Load', 'Save'}
|
||||||
|
local tok,i = script.showListPrompt('Stockpile Settings','Load or Save Settings?',COLOR_WHITE,tablify(list))
|
||||||
|
if tok then
|
||||||
|
if i == 1 then
|
||||||
|
load_settings()
|
||||||
|
else
|
||||||
|
save_settings(sp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function show_message_box(title, msg, iserror)
|
||||||
|
local color = COLOR_WHITE
|
||||||
|
if iserror then
|
||||||
|
color = COLOR_RED
|
||||||
|
end
|
||||||
|
script.start(function()
|
||||||
|
script.showMessage(title, msg, color)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function guard()
|
||||||
|
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Stockpile') then
|
||||||
|
qerror("This script requires a stockpile selected in the 'q' mode")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_path(path)
|
||||||
|
init()
|
||||||
|
if persist == nil then
|
||||||
|
qerror("This version of DFHack doesn't support setting the stockpile settings path. Sorry.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
persist.GlobalTable.stockpiles['settings_path'] = path
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_path()
|
||||||
|
init()
|
||||||
|
if persist == nil then
|
||||||
|
return "stocksettings"
|
||||||
|
end
|
||||||
|
return persist.GlobalTable.stockpiles['settings_path']
|
||||||
|
end
|
||||||
|
|
||||||
|
return _ENV
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue