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

Conflicts:
	NEWS
develop
expwnent 2013-10-20 11:09:40 -04:00
commit ee056f4422
26 changed files with 3503 additions and 789 deletions

@ -10,6 +10,7 @@ DFHack v0.34.11-r3
- restrictice - Restrict traffic on squares above visible ice.
- treefarm - automatically chop trees and dig obsidian
- diggingInvaders - allows invaders to dig and/or deconstruct walls and buildings in order to get at your dwarves.
- enable, disable - Built-in commands that can be used to enable/disable many plugins.
New scripts:
- masspit: designate caged creatures in a zone for pitting
- locate_ore: scan the map for unmined ore veins
@ -30,9 +31,11 @@ DFHack v0.34.11-r3
- syndromeTrigger: replaces and extends trueTransformation. Can trigger things when syndromes are added for any reason.
- fastdwarf: fixed bug involving fastdwarf and teledwarf being on at the same time
- workNow: can optionally look for jobs when jobs are completed
- tiletypes: support changing tile material to arbitrary stone.
New tweaks:
- hive-crash: Prevent crash if bees die in a hive with ungathered products (bug 6368).
New plugins:
- 3dveins: Reshapes all veins on the map in a way that flows between Z levels.
- buildingplan: Place furniture before it's built
- outsideOnly: make raw-specified buildings impossible to build inside
- resume: A plugin to help display and resume suspended constructions conveniently
@ -44,7 +47,8 @@ DFHack v0.34.11-r3
- Core: there is now a per-save dfhack.init file for when the save is loaded, and another for when it is unloaded
- EventManager: fixed job completion detection
- Once: easy way to make sure something happens once per run of DF, such as an error message
- Lua API for a better random number generator and perlin noise functions.
DFHack v0.34.11-r3
Internals:

@ -361,6 +361,24 @@ Usage:
Map modification
================
3dveins
-------
Removes all existing veins from the map and generates new ones using
3D Perlin noise, in order to produce a layout that smoothly flows between
Z levels. The vein distribution is based on the world seed, so running
the command for the second time should produce no change. It is best to
run it just once immediately after embark.
This command is intended as only a cosmetic change, so it takes
care to exactly preserve the mineral counts reported by ``prospect all``.
The amounts of different layer stone may slightly change in some cases
if vein mass shifts between Z layers.
This command is very unlikely to work on maps generated before version 0.34.08.
Note that there is no undo option other than restoring from backup.
changelayer
-----------
Changes material of the geology layer under cursor to the specified inorganic
@ -586,6 +604,33 @@ Or this:
This will hide previously revealed tiles (or show hidden with the 0 option).
More recently, the tool supports changing the base material of the tile to
an arbitrary stone from the raws, by creating new veins as required. Note
that this mode paints under ice and constructions, instead of overwriting
them. To enable, use:
::
paint stone MICROCLINE
This mode is incompatible with the regular ``material`` setting, so changing
it cancels the specific stone selection:
::
paint material ANY
Since different vein types have different drop rates, it is possible to choose
which one to use in painting:
::
paint veintype CLUSTER_SMALL
When the chosen type is ``CLUSTER`` (the default), the tool may automatically
choose to use layer stone or lava stone instead of veins if its material matches
the desired one.
Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:
::

@ -140,6 +140,7 @@ modules/Gui.cpp
modules/Items.cpp
modules/Job.cpp
modules/kitchen.cpp
modules/MapCache.cpp
modules/Maps.cpp
modules/Materials.cpp
modules/Notes.cpp

@ -26,8 +26,250 @@ distribution.
#include "TileTypes.h"
#include "Export.h"
#include <map>
using namespace DFHack;
const int NUM_TILETYPES = 1+(int)ENUM_LAST_ITEM(tiletype);
const int NUM_MATERIALS = 1+(int)ENUM_LAST_ITEM(tiletype_material);
const int NUM_CVTABLES = 1+(int)tiletype_material::CONSTRUCTION;
typedef std::map<df::tiletype_variant, df::tiletype> T_VariantMap;
typedef std::map<std::string, T_VariantMap> T_DirectionMap;
typedef std::map<df::tiletype_special, T_DirectionMap> T_SpecialMap;
typedef std::map<df::tiletype_shape, T_SpecialMap> T_ShapeMap;
typedef T_ShapeMap T_MaterialMap[NUM_MATERIALS];
static bool tables_ready = false;
static T_MaterialMap tile_table;
static df::tiletype tile_to_mat[NUM_CVTABLES][NUM_TILETYPES];
static df::tiletype find_match(
df::tiletype_material mat, df::tiletype_shape shape, df::tiletype_special special,
std::string dir, df::tiletype_variant variant, bool warn
) {
using namespace df::enums::tiletype_shape;
using namespace df::enums::tiletype_special;
if (mat < 0 || mat >= NUM_MATERIALS)
return tiletype::Void;
auto &sh_map = tile_table[mat];
if (!sh_map.count(shape))
{
if (warn)
{
fprintf(
stderr, "NOTE: No shape %s in %s.\n",
enum_item_key(shape).c_str(), enum_item_key(mat).c_str()
);
}
switch (shape)
{
case BROOK_BED:
if (sh_map.count(FORTIFICATION)) { shape = FORTIFICATION; break; }
case FORTIFICATION:
if (sh_map.count(WALL)) { shape = WALL; break; }
return tiletype::Void;
case BROOK_TOP:
case BOULDER:
case PEBBLES:
if (sh_map.count(FLOOR)) { shape = FLOOR; break; }
return tiletype::Void;
default:
return tiletype::Void;
};
}
auto &sp_map = sh_map[shape];
if (!sp_map.count(special))
{
if (warn)
{
fprintf(
stderr, "NOTE: No special %s in %s:%s.\n",
enum_item_key(special).c_str(), enum_item_key(mat).c_str(),
enum_item_key(shape).c_str()
);
}
switch (special)
{
case TRACK:
if (sp_map.count(SMOOTH)) {
special = SMOOTH; break;
}
case df::enums::tiletype_special::NONE:
case NORMAL:
case SMOOTH:
case WET:
case FURROWED:
case RIVER_SOURCE:
case WATERFALL:
case WORN_1:
case WORN_2:
case WORN_3:
if (sp_map.count(NORMAL)) {
special = NORMAL; break;
}
if (sp_map.count(df::enums::tiletype_special::NONE)) {
special = df::enums::tiletype_special::NONE; break;
}
// For targeting construction
if (sp_map.count(SMOOTH)) {
special = SMOOTH; break;
}
default:
return tiletype::Void;
}
}
auto &dir_map = sp_map[special];
if (!dir_map.count(dir))
{
if (warn)
{
fprintf(
stderr, "NOTE: No direction '%s' in %s:%s:%s.\n",
dir.c_str(), enum_item_key(mat).c_str(),
enum_item_key(shape).c_str(), enum_item_key(special).c_str()
);
}
if (dir_map.count("--------"))
dir = "--------";
else if (dir_map.count("NSEW"))
dir = "NSEW";
else if (dir_map.count("N-S-W-E-"))
dir = "N-S-W-E-";
else
dir = dir_map.begin()->first;
}
auto &var_map = dir_map[dir];
if (!var_map.count(variant))
{
if (warn)
{
fprintf(
stderr, "NOTE: No variant '%s' in %s:%s:%s:%s.\n",
enum_item_key(variant).c_str(), enum_item_key(mat).c_str(),
enum_item_key(shape).c_str(), enum_item_key(special).c_str(), dir.c_str()
);
}
variant = var_map.begin()->first;
}
return var_map[variant];
}
static void init_tables()
{
tables_ready = true;
memset(tile_to_mat, 0, sizeof(tile_to_mat));
// Index tile types
FOR_ENUM_ITEMS(tiletype, tt)
{
auto &attrs = df::enum_traits<df::tiletype>::attrs(tt);
if (attrs.material < 0)
continue;
tile_table[attrs.material][attrs.shape][attrs.special][attrs.direction][attrs.variant] = tt;
if (isCoreMaterial(attrs.material))
{
assert(attrs.material < NUM_CVTABLES);
tile_to_mat[attrs.material][tt] = tt;
}
}
// Build mapping of everything to STONE and back
FOR_ENUM_ITEMS(tiletype, tt)
{
auto &attrs = df::enum_traits<df::tiletype>::attrs(tt);
if (!isCoreMaterial(attrs.material))
continue;
if (attrs.material != tiletype_material::STONE)
{
df::tiletype ttm = find_match(
tiletype_material::STONE,
attrs.shape, attrs.special, attrs.direction, attrs.variant,
isStoneMaterial(attrs.material)
);
tile_to_mat[tiletype_material::STONE][tt] = ttm;
if (ttm == tiletype::Void)
fprintf(stderr, "No match for tile %s in STONE.\n",
enum_item_key(tt).c_str());
}
else
{
FOR_ENUM_ITEMS(tiletype_material, mat)
{
if (!isCoreMaterial(mat) || mat == attrs.material)
continue;
df::tiletype ttm = find_match(
mat,
attrs.shape, attrs.special, attrs.direction, attrs.variant,
isStoneMaterial(mat)
);
tile_to_mat[mat][tt] = ttm;
if (ttm == tiletype::Void)
fprintf(stderr, "No match for tile %s in %s.\n",
enum_item_key(tt).c_str(), enum_item_key(mat).c_str());
}
}
}
// Transitive closure via STONE
FOR_ENUM_ITEMS(tiletype_material, mat)
{
if (!isCoreMaterial(mat) || mat == tiletype_material::STONE)
continue;
FOR_ENUM_ITEMS(tiletype, tt)
{
if (!tt || tile_to_mat[mat][tt])
continue;
auto stone = tile_to_mat[tiletype_material::STONE][tt];
if (stone)
tile_to_mat[mat][tt] = tile_to_mat[mat][stone];
}
}
}
df::tiletype DFHack::matchTileMaterial(df::tiletype source, df::tiletype_material tmat)
{
if (!isCoreMaterial(tmat) || !source || source >= NUM_TILETYPES)
return tiletype::Void;
if (!tables_ready)
init_tables();
return tile_to_mat[tmat][source];
}
namespace DFHack
{
df::tiletype findSimilarTileType (const df::tiletype sourceTileType, const df::tiletype_shape tshape)
{
df::tiletype match = tiletype::Void;

@ -32,7 +32,9 @@ distribution.
#include <sstream>
#include <cstdio>
using namespace std;
using std::ostream;
using std::stringstream;
using std::endl;
template <typename T>
void print_bits ( T val, ostream& out )

@ -119,10 +119,10 @@ namespace DFHack
inline char * getStr() const
{
static char str[16];
//type punning trick
*( (uint64_t *)str ) = *( (uint64_t *)"--------" );
str[8]=0;
#define DIRECTION(x,i,c) \
str[i] = str[i+1] = '-'; \
if(x){ \
str[i]=c; \
if(1==x) ; \
@ -183,6 +183,56 @@ namespace DFHack
return TileDirection(ENUM_ATTR(tiletype, direction, tiletype));
}
// Air
inline bool isAirMaterial(df::tiletype_material mat) { return mat == tiletype_material::AIR; }
inline bool isAirMaterial(df::tiletype tt) { return isAirMaterial(tileMaterial(tt)); }
// Soil
inline bool isSoilMaterial(df::tiletype_material mat) { return mat == tiletype_material::SOIL; }
inline bool isSoilMaterial(df::tiletype tt) { return isSoilMaterial(tileMaterial(tt)); }
// Stone materials - their tiles are completely interchangable
inline bool isStoneMaterial(df::tiletype_material mat)
{
using namespace df::enums::tiletype_material;
switch (mat) {
case STONE: case LAVA_STONE: case MINERAL: case FEATURE:
return true;
default:
return false;
}
}
inline bool isStoneMaterial(df::tiletype tt) { return isStoneMaterial(tileMaterial(tt)); }
// Regular ground materials = stone + soil
inline bool isGroundMaterial(df::tiletype_material mat)
{
using namespace df::enums::tiletype_material;
switch (mat) {
case SOIL:
case STONE: case LAVA_STONE: case MINERAL: case FEATURE:
return true;
default:
return false;
}
}
inline bool isGroundMaterial(df::tiletype tt) { return isGroundMaterial(tileMaterial(tt)); }
// Core materials - their tile sets are sufficiently close to stone
inline bool isCoreMaterial(df::tiletype_material mat)
{
using namespace df::enums::tiletype_material;
switch (mat) {
case SOIL:
case STONE: case LAVA_STONE: case MINERAL: case FEATURE:
case FROZEN_LIQUID: case CONSTRUCTION:
return true;
default:
return false;
}
}
inline bool isCoreMaterial(df::tiletype tt) { return isCoreMaterial(tileMaterial(tt)); }
// tile is missing a floor
inline
bool LowPassable(df::tiletype tiletype)
@ -295,5 +345,11 @@ namespace DFHack
* If there are no variants, returns the same tile
*/
DFHACK_EXPORT df::tiletype findRandomVariant(const df::tiletype tile);
/**
* Map a tile type to a different core material (see above for the list).
* Returns Void (0) in case of failure.
*/
DFHACK_EXPORT df::tiletype matchTileMaterial(df::tiletype source, df::tiletype_material tmat);
}

@ -1,3 +1,5 @@
static const int SIZE = 16;
inline uint16_t &operator[] (int y)
{
return bits[y];
@ -36,3 +38,18 @@ bool has_assignments()
return true;
return false;
}
df::tile_bitmask &operator |= (const df::tile_bitmask &b) {
for (int i = 0; i < 16; i++)
bits[i] |= b.bits[i];
return *this;
}
df::tile_bitmask &operator &= (const df::tile_bitmask &b) {
for (int i = 0; i < 16; i++)
bits[i] &= b.bits[i];
return *this;
}
df::tile_bitmask &operator -= (const df::tile_bitmask &b) {
for (int i = 0; i < 16; i++)
bits[i] &= ~b.bits[i];
return *this;
}

@ -35,6 +35,7 @@ distribution.
#include "df/block_square_event_mineralst.h"
#include "df/construction.h"
#include "df/item.h"
#include "df/inclusion_type.h"
using namespace DFHack;
@ -49,6 +50,22 @@ class DFHACK_EXPORT MapCache;
class Block;
struct BiomeInfo {
// Determined by the 4-bit index in the designation bitfield
static const unsigned MAX_LAYERS = 16;
df::coord2d pos;
int default_soil, default_stone, lava_stone;
int geo_index;
df::region_map_entry *biome;
df::world_geo_biome *geobiome;
df::world_region_details *details;
int16_t layer_stone[MAX_LAYERS];
};
typedef uint8_t t_veintype[16][16];
typedef df::tiletype t_tilearr[16][16];
class BlockInfo
{
Block *mblock;
@ -56,8 +73,15 @@ class BlockInfo
df::map_block *block;
public:
enum GroundType {
G_UNKNOWN = 0, G_STONE, G_SOIL
};
static GroundType getGroundType(int material);
typedef df::block_square_event_mineralst::T_flags DFVeinFlags;
t_veintype veintype;
t_blockmaterials veinmats;
t_blockmaterials basemats;
t_blockmaterials grass;
std::map<df::coord,df::plant*> plants;
@ -72,7 +96,10 @@ public:
t_matpair getBaseMaterial(df::tiletype tt, df::coord2d pos);
static void SquashVeins(df::map_block *mb, t_blockmaterials & materials);
static df::inclusion_type getVeinType(DFVeinFlags &flags);
static void setVeinType(DFVeinFlags &flags, df::inclusion_type type);
static void SquashVeins(df::map_block *mb, t_blockmaterials & materials, t_veintype &veintype);
static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen);
static void SquashRocks (df::map_block *mb, t_blockmaterials & materials,
std::vector< std::vector <int16_t> > * layerassign);
@ -95,18 +122,19 @@ public:
* All coordinates are taken mod 16.
*/
//Arbitrary tag field for flood fills etc.
/// Arbitrary tag field for flood fills etc.
int16_t &tag(df::coord2d p) {
if (!tags) init_tags();
return index_tile<int16_t&>(tags, p);
}
// Base layer
/// Base layer tile type (i.e. layer stone, veins, feature stone)
df::tiletype baseTiletypeAt(df::coord2d p)
{
if (!tiles) init_tiles();
return index_tile<df::tiletype>(tiles->base_tiles,p);
}
/// Base layer material (i.e. layer stone, veins, feature stone)
t_matpair baseMaterialAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
@ -115,12 +143,14 @@ public:
index_tile<int16_t>(basemats->mat_index,p)
);
}
/// Check if the base layer tile is a vein
bool isVeinAt(df::coord2d p)
{
using namespace df::enums::tiletype_material;
auto tm = tileMaterial(baseTiletypeAt(p));
return tm == MINERAL;
}
/// Check if the base layer tile is layer stone or soil
bool isLayerAt(df::coord2d p)
{
using namespace df::enums::tiletype_material;
@ -128,17 +158,49 @@ public:
return tm == STONE || tm == SOIL;
}
/// Vein material at pos (even if there is no vein tile), or -1 if none
int16_t veinMaterialAt(df::coord2d p)
{
return isVeinAt(p) ? baseMaterialAt(p).mat_index : -1;
if (!basemats) init_tiles(true);
return index_tile<int16_t>(basemats->veinmat,p);
}
int16_t layerMaterialAt(df::coord2d p)
/// Vein type at pos (even if there is no vein tile)
df::inclusion_type veinTypeAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
return index_tile<int16_t>(basemats->layermat,p);
return (df::inclusion_type)index_tile<uint8_t>(basemats->veintype,p);
}
// Static layer (base + constructions)
/** Sets the vein material at the specified tile position.
* Use -1 to clear the tile from all veins. Does not update tile types.
* Returns false in case of some error, e.g. non-stone mat.
*/
bool setVeinMaterialAt(df::coord2d p, int16_t mat, df::inclusion_type type = df::enums::inclusion_type::CLUSTER);
/// Geological layer soil or stone material at pos
int16_t layerMaterialAt(df::coord2d p) {
return biomeInfoAt(p).layer_stone[layerIndexAt(p)];
}
/// Biome-specific lava stone at pos
int16_t lavaStoneAt(df::coord2d p) { return biomeInfoAt(p).lava_stone; }
/**
* Sets the stone tile and material at specified position, automatically
* choosing between layer, lava or vein stone.
* The force_type flags ensures the correct inclusion type, even forcing
* a vein format if necessary. If kill_veins is true and the chosen mode
* isn't vein, it will clear any old veins from the tile.
*/
bool setStoneAt(df::coord2d p, df::tiletype tile, int16_t mat, df::inclusion_type type = df::enums::inclusion_type::CLUSTER, bool force_type = false, bool kill_veins = false);
/**
* Sets the tile at the position to SOIL material. The actual material
* is completely determined by geological layers and cannot be set.
*/
bool setSoilAt(df::coord2d p, df::tiletype tile, bool kill_veins = false);
/// Static layer tile (i.e. base + constructions)
df::tiletype staticTiletypeAt(df::coord2d p)
{
if (!tiles) init_tiles();
@ -146,6 +208,7 @@ public:
return index_tile<df::tiletype>(tiles->con_info->tiles,p);
return baseTiletypeAt(p);
}
/// Static layer material (i.e. base + constructions)
t_matpair staticMaterialAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
@ -223,6 +286,15 @@ public:
return true;
}
bool getFlagAt(df::coord2d p, df::tile_designation::Mask mask) {
return (index_tile<df::tile_designation&>(designation,p).whole & mask) != 0;
}
bool getFlagAt(df::coord2d p, df::tile_occupancy::Mask mask) {
return (index_tile<df::tile_occupancy&>(occupancy,p).whole & mask) != 0;
}
bool setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool set);
bool setFlagAt(df::coord2d p, df::tile_occupancy::Mask mask, bool set);
int itemCountAt(df::coord2d p)
{
if (!item_counts) init_item_counts();
@ -235,9 +307,16 @@ public:
}
bool Write();
bool isDirty();
int biomeIndexAt(df::coord2d p);
int layerIndexAt(df::coord2d p) {
return index_tile<df::tile_designation&>(designation,p).bits.geolayer_index;
}
df::coord2d biomeRegionAt(df::coord2d p);
int16_t GeoIndexAt(df::coord2d p);
const BiomeInfo &biomeInfoAt(df::coord2d p);
int16_t GeoIndexAt(df::coord2d p) { return biomeInfoAt(p).geo_index; }
bool GetGlobalFeature(t_feature *out);
bool GetLocalFeature(t_feature *out);
@ -258,11 +337,10 @@ private:
void init();
int biomeIndexAt(df::coord2d p);
bool valid;
bool dirty_designations:1;
bool dirty_tiles:1;
bool dirty_veins:1;
bool dirty_temperatures:1;
bool dirty_occupancies:1;
@ -281,40 +359,53 @@ private:
bool addItemOnGround(df::item *item);
bool removeItemOnGround(df::item *item);
struct IceInfo {
df::tile_bitmask frozen;
df::tile_bitmask dirty;
};
struct ConInfo {
df::tile_bitmask constructed;
df::tiletype tiles[16][16];
df::tile_bitmask dirty;
t_tilearr tiles;
t_blockmaterials mat_type;
t_blockmaterials mat_index;
};
struct TileInfo {
df::tile_bitmask frozen;
df::tile_bitmask dirty_raw;
df::tiletype raw_tiles[16][16];
t_tilearr raw_tiles;
IceInfo *ice_info;
ConInfo *con_info;
df::tile_bitmask dirty_base;
df::tiletype base_tiles[16][16];
t_tilearr base_tiles;
TileInfo();
~TileInfo();
void init_iceinfo();
void init_coninfo();
void set_base_tile(df::coord2d pos, df::tiletype tile);
};
struct BasematInfo {
df::tile_bitmask dirty;
t_blockmaterials mat_type;
t_blockmaterials mat_index;
t_blockmaterials layermat;
df::tile_bitmask vein_dirty;
t_veintype veintype;
t_blockmaterials veinmat;
BasematInfo();
void set_base_mat(TileInfo *tiles, df::coord2d pos, int16_t type, int16_t idx);
};
TileInfo *tiles;
BasematInfo *basemats;
void init_tiles(bool basemat = false);
void ParseTiles(TileInfo *tiles);
void WriteTiles(TileInfo*);
void ParseBasemats(TileInfo *tiles, BasematInfo *bmats);
void WriteVeins(TileInfo *tiles, BasematInfo *bmats);
designations40d designation;
occupancies40d occupancy;
@ -349,6 +440,9 @@ class DFHACK_EXPORT MapCache
return b ? b->Allocate() : false;
}
/// delete the block from memory
void discardBlock(Block *block);
df::tiletype baseTiletypeAt (DFCoord tilecoord)
{
Block *b = BlockAtTile(tilecoord);
@ -507,10 +601,17 @@ class DFHACK_EXPORT MapCache
uint32_t maxTileY() { return y_tmax; }
uint32_t maxZ() { return z_max; }
size_t getBiomeCount() { return biomes.size(); }
const BiomeInfo &getBiomeByIndex(unsigned idx) {
return (idx < biomes.size()) ? biomes[idx] : biome_stub;
}
private:
friend class Block;
friend class BlockInfo;
static const BiomeInfo biome_stub;
bool valid;
bool validgeo;
uint32_t x_bmax;
@ -518,10 +619,7 @@ private:
uint32_t x_tmax;
uint32_t y_tmax;
uint32_t z_max;
std::vector<df::coord2d> geoidx;
std::vector<int> default_soil;
std::vector<int> default_stone;
std::vector< std::vector <int16_t> > layer_mats;
std::vector<BiomeInfo> biomes;
std::map<df::coord2d, df::world_region_details*> region_details;
std::map<DFCoord, Block *> blocks;
};

@ -169,6 +169,9 @@ namespace DFHack
return a.type != b.type || a.index != b.index;
}
DFHACK_EXPORT bool isSoilInorganic(int material);
DFHACK_EXPORT bool isStoneInorganic(int material);
typedef int32_t t_materialIndex;
typedef int16_t t_materialType, t_itemType, t_itemSubtype;

@ -60,7 +60,8 @@ inline void PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::setup(
) {
Impl<mask,i-1>::setup(self, pv, pt);
T t = std::floor(pv[i]);
int32_t t = int32_t(pv[i]);
t -= (pv[i]<t);
pt[i].s = s_curve(pt[i].r0 = pv[i] - t);
unsigned b = unsigned(int32_t(t));

File diff suppressed because it is too large Load Diff

@ -326,7 +326,7 @@ df::feature_init *Maps::getLocalInitFeature(df::coord2d rgn_pos, int32_t index)
return vector_get(features, index);
}
static bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index)
bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index)
{
feature.type = (df::feature_type)-1;
@ -669,702 +669,3 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
return false;
}
#define COPY(a,b) memcpy(&a,&b,sizeof(a))
MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
{
dirty_designations = false;
dirty_tiles = false;
dirty_temperatures = false;
dirty_occupancies = false;
valid = false;
bcoord = _bcoord;
block = Maps::getBlock(bcoord);
tags = NULL;
init();
}
void MapExtras::Block::init()
{
item_counts = NULL;
tiles = NULL;
basemats = NULL;
if(block)
{
COPY(designation, block->designation);
COPY(occupancy, block->occupancy);
COPY(temp1, block->temperature_1);
COPY(temp2, block->temperature_2);
valid = true;
}
else
{
memset(designation,0,sizeof(designation));
memset(occupancy,0,sizeof(occupancy));
memset(temp1,0,sizeof(temp1));
memset(temp2,0,sizeof(temp2));
}
}
bool MapExtras::Block::Allocate()
{
if (block)
return true;
block = Maps::ensureTileBlock(bcoord.x*16, bcoord.y*16, bcoord.z);
if (!block)
return false;
delete[] item_counts;
delete tiles;
delete basemats;
init();
return true;
}
MapExtras::Block::~Block()
{
delete[] item_counts;
delete[] tags;
delete tiles;
delete basemats;
}
void MapExtras::Block::init_tags()
{
if (!tags)
tags = new T_tags[16];
memset(tags,0,sizeof(T_tags)*16);
}
void MapExtras::Block::init_tiles(bool basemat)
{
if (!tiles)
{
tiles = new TileInfo();
if (block)
ParseTiles(tiles);
}
if (basemat && !basemats)
{
basemats = new BasematInfo();
if (block)
ParseBasemats(tiles, basemats);
}
}
MapExtras::Block::TileInfo::TileInfo()
{
frozen.clear();
dirty_raw.clear();
memset(raw_tiles,0,sizeof(raw_tiles));
con_info = NULL;
dirty_base.clear();
memset(base_tiles,0,sizeof(base_tiles));
}
MapExtras::Block::TileInfo::~TileInfo()
{
delete con_info;
}
void MapExtras::Block::TileInfo::init_coninfo()
{
if (con_info)
return;
con_info = new ConInfo();
con_info->constructed.clear();
COPY(con_info->tiles, base_tiles);
memset(con_info->mat_type, -1, sizeof(con_info->mat_type));
memset(con_info->mat_index, -1, sizeof(con_info->mat_index));
}
MapExtras::Block::BasematInfo::BasematInfo()
{
dirty.clear();
memset(mat_type,0,sizeof(mat_type));
memset(mat_index,-1,sizeof(mat_index));
memset(layermat,-1,sizeof(layermat));
}
bool MapExtras::Block::setTiletypeAt(df::coord2d pos, df::tiletype tt, bool force)
{
if (!block)
return false;
if (!basemats)
init_tiles(true);
pos = pos & 15;
dirty_tiles = true;
tiles->raw_tiles[pos.x][pos.y] = tt;
tiles->dirty_raw.setassignment(pos, true);
return true;
}
void MapExtras::Block::ParseTiles(TileInfo *tiles)
{
tiletypes40d icetiles;
BlockInfo::SquashFrozenLiquids(block, icetiles);
COPY(tiles->raw_tiles, block->tiletype);
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
using namespace df::enums::tiletype_material;
df::tiletype tt = tiles->raw_tiles[x][y];
df::coord coord = block->map_pos + df::coord(x,y,0);
// Frozen liquid comes topmost
if (tileMaterial(tt) == FROZEN_LIQUID)
{
tiles->frozen.setassignment(x,y,true);
if (icetiles[x][y] != tiletype::Void)
{
tt = icetiles[x][y];
}
}
// The next layer may be construction
bool is_con = false;
if (tileMaterial(tt) == CONSTRUCTION)
{
df::construction *con = df::construction::find(coord);
if (con)
{
if (!tiles->con_info)
tiles->init_coninfo();
is_con = true;
tiles->con_info->constructed.setassignment(x,y,true);
tiles->con_info->tiles[x][y] = tt;
tiles->con_info->mat_type[x][y] = con->mat_type;
tiles->con_info->mat_index[x][y] = con->mat_index;
tt = con->original_tile;
}
}
// Finally, base material
tiles->base_tiles[x][y] = tt;
// Copy base info back to construction layer
if (tiles->con_info && !is_con)
tiles->con_info->tiles[x][y] = tt;
}
}
}
void MapExtras::Block::ParseBasemats(TileInfo *tiles, BasematInfo *bmats)
{
BlockInfo info;
info.prepare(this);
COPY(bmats->layermat, info.basemats);
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
using namespace df::enums::tiletype_material;
auto tt = tiles->base_tiles[x][y];
auto mat = info.getBaseMaterial(tt, df::coord2d(x,y));
bmats->mat_type[x][y] = mat.mat_type;
bmats->mat_index[x][y] = mat.mat_index;
// Copy base info back to construction layer
if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y))
{
tiles->con_info->mat_type[x][y] = mat.mat_type;
tiles->con_info->mat_index[x][y] = mat.mat_index;
}
}
}
}
bool MapExtras::Block::Write ()
{
if(!valid) return false;
if(dirty_designations)
{
COPY(block->designation, designation);
block->flags.bits.designated = true;
dirty_designations = false;
}
if(dirty_tiles && tiles)
{
dirty_tiles = false;
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if (tiles->dirty_raw.getassignment(x,y))
block->tiletype[x][y] = tiles->raw_tiles[x][y];
}
}
delete tiles; tiles = NULL;
delete basemats; basemats = NULL;
}
if(dirty_temperatures)
{
COPY(block->temperature_1, temp1);
COPY(block->temperature_2, temp2);
dirty_temperatures = false;
}
if(dirty_occupancies)
{
COPY(block->occupancy, occupancy);
dirty_occupancies = false;
}
return true;
}
void MapExtras::BlockInfo::prepare(Block *mblock)
{
this->mblock = mblock;
block = mblock->getRaw();
parent = mblock->getParent();
SquashVeins(block,veinmats);
SquashGrass(block, grass);
if (parent->validgeo)
SquashRocks(block,basemats,&parent->layer_mats);
else
memset(basemats,-1,sizeof(basemats));
for (size_t i = 0; i < block->plants.size(); i++)
{
auto pp = block->plants[i];
plants[pp->pos] = pp;
}
global_feature = Maps::getGlobalInitFeature(block->global_feature);
local_feature = Maps::getLocalInitFeature(block->region_pos, block->local_feature);
}
t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos)
{
using namespace df::enums::tiletype_material;
t_matpair rv(0,-1);
int x = pos.x, y = pos.y;
switch (tileMaterial(tt)) {
case NONE:
case AIR:
rv.mat_type = -1;
break;
case DRIFTWOOD:
case SOIL:
{
rv.mat_index = basemats[x][y];
if (auto raw = df::inorganic_raw::find(rv.mat_index))
{
if (raw->flags.is_set(inorganic_flags::SOIL_ANY))
break;
int biome = mblock->biomeIndexAt(pos);
int idx = vector_get(parent->default_soil, biome, -1);
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
case STONE:
{
rv.mat_index = basemats[x][y];
if (auto raw = df::inorganic_raw::find(rv.mat_index))
{
if (!raw->flags.is_set(inorganic_flags::SOIL_ANY))
break;
int biome = mblock->biomeIndexAt(pos);
int idx = vector_get(parent->default_stone, biome, -1);
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
case MINERAL:
rv.mat_index = veinmats[x][y];
break;
case LAVA_STONE:
if (auto details = parent->region_details[mblock->biomeRegionAt(pos)])
rv.mat_index = details->lava_stone;
break;
case PLANT:
rv.mat_type = MaterialInfo::PLANT_BASE;
if (auto plant = plants[block->map_pos + df::coord(x,y,0)])
{
if (auto raw = df::plant_raw::find(plant->material))
{
rv.mat_type = raw->material_defs.type_basic_mat;
rv.mat_index = raw->material_defs.idx_basic_mat;
}
}
break;
case GRASS_LIGHT:
case GRASS_DARK:
case GRASS_DRY:
case GRASS_DEAD:
rv.mat_type = MaterialInfo::PLANT_BASE;
if (auto raw = df::plant_raw::find(grass[x][y]))
{
rv.mat_type = raw->material_defs.type_basic_mat;
rv.mat_index = raw->material_defs.idx_basic_mat;
}
break;
case FEATURE:
{
auto dsgn = block->designation[x][y];
if (dsgn.bits.feature_local && local_feature)
local_feature->getMaterial(&rv.mat_type, &rv.mat_index);
else if (dsgn.bits.feature_global && global_feature)
global_feature->getMaterial(&rv.mat_type, &rv.mat_index);
break;
}
case CONSTRUCTION: // just a fallback
case MAGMA:
case HFS:
// use generic 'rock'
break;
case FROZEN_LIQUID:
rv.mat_type = builtin_mats::WATER;
break;
case POOL:
case BROOK:
case RIVER:
rv.mat_index = basemats[x][y];
break;
case ASHES:
case FIRE:
case CAMPFIRE:
rv.mat_type = builtin_mats::ASH;
break;
}
return rv;
}
void MapExtras::BlockInfo::SquashVeins(df::map_block *mb, t_blockmaterials & materials)
{
std::vector <df::block_square_event_mineralst *> veins;
Maps::SortBlockEvents(mb,&veins);
memset(materials,-1,sizeof(materials));
for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++)
{
for (size_t i = 0; i < veins.size(); i++)
{
if (veins[i]->getassignment(x,y))
materials[x][y] = veins[i]->inorganic_mat;
}
}
}
void MapExtras::BlockInfo::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen)
{
std::vector <df::block_square_event_frozen_liquidst *> ices;
Maps::SortBlockEvents(mb,NULL,&ices);
memset(frozen,0,sizeof(frozen));
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
for (size_t i = 0; i < ices.size(); i++)
{
df::tiletype tt2 = ices[i]->tiles[x][y];
if (tt2 != tiletype::Void)
{
frozen[x][y] = tt2;
break;
}
}
}
}
void MapExtras::BlockInfo::SquashRocks (df::map_block *mb, t_blockmaterials & materials,
std::vector< std::vector <int16_t> > * layerassign)
{
// get the layer materials
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
materials[x][y] = -1;
uint8_t test = mb->designation[x][y].bits.biome;
if (test >= 9)
continue;
uint8_t idx = mb->region_offset[test];
if (idx < layerassign->size())
materials[x][y] = layerassign->at(idx)[mb->designation[x][y].bits.geolayer_index];
}
}
void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &materials)
{
std::vector<df::block_square_event_grassst*> grasses;
Maps::SortBlockEvents(mb, NULL, NULL, NULL, &grasses);
memset(materials,-1,sizeof(materials));
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
int amount = 0;
for (size_t i = 0; i < grasses.size(); i++)
{
if (grasses[i]->amount[x][y] >= amount)
{
amount = grasses[i]->amount[x][y];
materials[x][y] = grasses[i]->plant_index;
}
}
}
}
int MapExtras::Block::biomeIndexAt(df::coord2d p)
{
if (!block)
return -1;
auto des = index_tile<df::tile_designation>(designation,p);
uint8_t idx = des.bits.biome;
if (idx >= 9)
return -1;
idx = block->region_offset[idx];
if (idx >= parent->geoidx.size())
return -1;
return idx;
}
df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p)
{
if (!block)
return df::coord2d(-30000,-30000);
int idx = biomeIndexAt(p);
if (idx < 0)
return block->region_pos;
return parent->geoidx[idx];
}
int16_t MapExtras::Block::GeoIndexAt(df::coord2d p)
{
df::coord2d biome = biomeRegionAt(p);
if (!biome.isValid())
return -1;
auto pinfo = Maps::getRegionBiome(biome);
if (!pinfo)
return -1;
return pinfo->geo_index;
}
bool MapExtras::Block::GetGlobalFeature(t_feature *out)
{
out->type = (df::feature_type)-1;
if (!valid || block->global_feature < 0)
return false;
return Maps::GetGlobalFeature(*out, block->global_feature);
}
bool MapExtras::Block::GetLocalFeature(t_feature *out)
{
out->type = (df::feature_type)-1;
if (!valid || block->local_feature < 0)
return false;
return ::GetLocalFeature(*out, block->region_pos, block->local_feature);
}
void MapExtras::Block::init_item_counts()
{
if (item_counts) return;
item_counts = new T_item_counts[16];
memset(item_counts, 0, sizeof(T_item_counts)*16);
if (!block) return;
for (size_t i = 0; i < block->items.size(); i++)
{
auto it = df::item::find(block->items[i]);
if (!it || !it->flags.bits.on_ground)
continue;
df::coord tidx = it->pos - block->map_pos;
if (!is_valid_tile_coord(tidx) || tidx.z != 0)
continue;
item_counts[tidx.x][tidx.y]++;
}
}
bool MapExtras::Block::addItemOnGround(df::item *item)
{
if (!block)
return false;
init_item_counts();
bool inserted;
insert_into_vector(block->items, item->id, &inserted);
if (inserted)
{
int &count = index_tile<int&>(item_counts,item->pos);
if (count++ == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = true;
index_tile<df::tile_occupancy&>(block->occupancy,item->pos).bits.item = true;
}
}
return inserted;
}
bool MapExtras::Block::removeItemOnGround(df::item *item)
{
if (!block)
return false;
init_item_counts();
int idx = binsearch_index(block->items, item->id);
if (idx < 0)
return false;
vector_erase_at(block->items, idx);
int &count = index_tile<int&>(item_counts,item->pos);
if (--count == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = false;
auto &occ = index_tile<df::tile_occupancy&>(block->occupancy,item->pos);
occ.bits.item = false;
// Clear the 'site blocked' flag in the building, if any.
// Otherwise the job would be re-suspended without actually checking items.
if (occ.bits.building == tile_building_occ::Planned)
{
if (auto bld = Buildings::findAtTile(item->pos))
{
// TODO: maybe recheck other tiles like the game does.
bld->flags.bits.site_blocked = false;
}
}
}
return true;
}
MapExtras::MapCache::MapCache()
{
valid = 0;
Maps::getSize(x_bmax, y_bmax, z_max);
x_tmax = x_bmax*16; y_tmax = y_bmax*16;
validgeo = Maps::ReadGeology(&layer_mats, &geoidx);
valid = true;
if (auto data = df::global::world->world_data)
{
for (size_t i = 0; i < data->region_details.size(); i++)
{
auto info = data->region_details[i];
region_details[info->pos] = info;
}
}
default_soil.resize(layer_mats.size());
default_stone.resize(layer_mats.size());
for (size_t i = 0; i < layer_mats.size(); i++)
{
default_soil[i] = -1;
default_stone[i] = -1;
for (size_t j = 0; j < layer_mats[i].size(); j++)
{
auto raw = df::inorganic_raw::find(layer_mats[i][j]);
if (!raw)
continue;
bool is_soil = raw->flags.is_set(inorganic_flags::SOIL_ANY);
if (is_soil)
default_soil[i] = layer_mats[i][j];
else if (default_stone[i] == -1)
default_stone[i] = layer_mats[i][j];
}
}
}
MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
{
if(!valid)
return 0;
std::map <DFCoord, Block*>::iterator iter = blocks.find(blockcoord);
if(iter != blocks.end())
{
return (*iter).second;
}
else
{
if(unsigned(blockcoord.x) < x_bmax &&
unsigned(blockcoord.y) < y_bmax &&
unsigned(blockcoord.z) < z_max)
{
Block * nblo = new Block(this, blockcoord);
blocks[blockcoord] = nblo;
return nblo;
}
return 0;
}
}
void MapExtras::MapCache::resetTags()
{
for (auto it = blocks.begin(); it != blocks.end(); ++it)
{
delete[] it->second->tags;
it->second->tags = NULL;
}
}

@ -561,6 +561,25 @@ bool DFHack::parseJobMaterialCategory(df::dfhack_material_category *cat, const s
return true;
}
bool DFHack::isSoilInorganic(int material)
{
auto raw = df::inorganic_raw::find(material);
return raw && raw->flags.is_set(inorganic_flags::SOIL_ANY);
}
bool DFHack::isStoneInorganic(int material)
{
auto raw = df::inorganic_raw::find(material);
if (!raw ||
raw->flags.is_set(inorganic_flags::SOIL_ANY) ||
raw->material.flags.is_set(material_flags::IS_METAL))
return false;
return true;
}
Module* DFHack::createMaterials()
{
return new Materials();

File diff suppressed because it is too large Load Diff

@ -161,6 +161,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(cleanconst cleanconst.cpp)
DFHACK_PLUGIN(3dveins 3dveins.cpp)
endif()
# this is the skeleton plugin. If you want to make your own, make a copy and then change it

@ -44,6 +44,7 @@
using std::string;
using std::endl;
using std::vector;
using namespace DFHack;
using namespace df::enums;
using df::global::ui;

@ -36,6 +36,7 @@
#include "TileTypes.h"
#include "df/job_item.h"
using namespace std;
using std::map;
using std::string;
using std::vector;

@ -67,6 +67,7 @@
#include <df/training_assignment.h>
#include <df/general_ref_contains_itemst.h>
using namespace std;
using std::string;
using std::endl;
using namespace DFHack;

@ -263,6 +263,9 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
out << " / " << inorganic[vein_rock].id
<< " / "
<< inorganic[vein_rock].name
<< " ("
<< ENUM_KEY_STR(inclusion_type,b->veinTypeAt(cursor))
<< ")"
<< endl;
else
out << endl;

@ -28,6 +28,7 @@
#include "df/misc_trait_type.h"
#include "df/unit_misc_trait.h"
using namespace std;
using std::set;
using std::vector;
using std::string;

@ -113,6 +113,7 @@
using std::vector;
using std::string;
using std::stack;
using std::set;
using namespace DFHack;
using namespace df::enums;

@ -97,6 +97,8 @@ void help( color_ostream & out, std::vector<std::string> &commands, int start, i
<< " Subterranean / st: set subterranean flag" << std::endl
<< " Skyview / sv: set skyview flag" << std::endl
<< " Aquifer / aqua: set aquifer flag" << std::endl
<< " Stone: paint specific stone material" << std::endl
<< " Veintype: use specific vein type for stone" << std::endl
<< "See help [option] for more information" << std::endl;
}
else if (option == "shape" || option == "s" ||option == "sh")
@ -165,6 +167,22 @@ void help( color_ostream & out, std::vector<std::string> &commands, int start, i
out << "Available aquifer flags:" << std::endl
<< " ANY, 0, 1" << std::endl;
}
else if (option == "stone")
{
out << "The stone option allows painting any specific stone material." << std::endl
<< "The normal 'material' option is forced to STONE, and cannot" << std::endl
<< "be changed without cancelling the specific stone selection." << std::endl
<< "Note: this feature paints under ice and constructions," << std::endl
<< "instead of replacing them with brute force." << std::endl;
}
else if (option == "veintype")
{
out << "Specifies which vein type to use when painting specific stone." << std::endl
<< "The vein type determines stone drop rate. Available types:" << std::endl;
FOR_ENUM_ITEMS(inclusion_type,i)
out << " " << ENUM_KEY_STR(inclusion_type,i) << std::endl;
out << "Vein type other than CLUSTER forces creation of a vein." << std::endl;
}
}
struct TileType
@ -179,6 +197,8 @@ struct TileType
int subterranean;
int skyview;
int aquifer;
int stone_material;
df::inclusion_type vein_type;
TileType()
{
@ -197,21 +217,27 @@ struct TileType
subterranean = -1;
skyview = -1;
aquifer = -1;
stone_material = -1;
vein_type = inclusion_type::CLUSTER;
}
bool empty()
{
return shape == -1 && material == -1 && special == -1 && variant == -1
&& dig == -1 && hidden == -1 && light == -1 && subterranean == -1
&& skyview == -1 && aquifer == -1;
&& skyview == -1 && aquifer == -1 && stone_material == -1;
}
inline bool matches(const df::tiletype source,
const df::tile_designation des)
const df::tile_designation des,
const t_matpair mat)
{
bool rv = true;
rv &= (shape == -1 || shape == tileShape(source));
rv &= (material == -1 || material == tileMaterial(source));
if (stone_material >= 0)
rv &= isStoneMaterial(source) && mat.mat_type == 0 && mat.mat_index == stone_material;
else
rv &= (material == -1 || material == tileMaterial(source));
rv &= (special == -1 || special == tileSpecial(source));
rv &= (variant == -1 || variant == tileVariant(source));
rv &= (dig == -1 || (dig != 0) == (des.bits.dig != tile_dig_designation::No));
@ -353,6 +379,20 @@ std::ostream &operator<<(std::ostream &stream, const TileType &paint)
needSpace = true;
}
if (paint.stone_material >= 0)
{
if (needSpace)
{
stream << " ";
needSpace = false;
}
stream << MaterialInfo(0,paint.stone_material).getToken()
<< " " << ENUM_KEY_STR(inclusion_type, paint.vein_type);
used = true;
needSpace = true;
}
if (!used)
{
stream << "any";
@ -497,6 +537,9 @@ bool processTileType(color_ostream & out, TileType &paint, std::vector<std::stri
}
else if (option == "material" || option == "mat" || option == "m")
{
// Setting the material conflicts with stone_material
paint.stone_material = -1;
if (is_valid_enum_item((df::tiletype_material)valInt))
{
paint.material = (df::tiletype_material)valInt;
@ -630,6 +673,25 @@ bool processTileType(color_ostream & out, TileType &paint, std::vector<std::stri
found = true;
}
else if (option == "stone")
{
MaterialInfo mat;
if (!mat.findInorganic(value))
out << "Unknown inorganic material: " << value << std::endl;
else if (!isStoneInorganic(mat.index))
out << "Not a stone material: " << value << std::endl;
else
{
paint.material = tiletype_material::STONE;
paint.stone_material = mat.index;
}
}
else if (option == "veintype")
{
if (!find_enum_item(&paint.vein_type, value))
out << "Unknown vein type: " << value << std::endl;
}
else
{
out << "Unknown option: '" << option << "'" << std::endl;
@ -672,12 +734,24 @@ command_result executePaintJob(color_ostream &out)
// Force the game to recompute its walkability cache
df::global::world->reindex_pathfinding = true;
int failures = 0;
for (coord_vec::iterator iter = all_tiles.begin(); iter != all_tiles.end(); ++iter)
{
const df::tiletype source = map.tiletypeAt(*iter);
MapExtras::Block *blk = map.BlockAtTile(*iter);
if (!blk)
continue;
df::tiletype source = map.tiletypeAt(*iter);
df::tile_designation des = map.designationAt(*iter);
if (!filter.matches(source, des))
// Stone painting operates on the base layer
if (paint.stone_material >= 0)
source = blk->baseTiletypeAt(*iter);
t_matpair basemat = blk->baseMaterialAt(*iter);
if (!filter.matches(source, des, basemat))
{
continue;
}
@ -725,7 +799,7 @@ command_result executePaintJob(color_ostream &out)
*/
// Remove direction from directionless tiles
DFHack::TileDirection direction = tileDirection(source);
if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH)))
if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || special == tiletype_special::TRACK || shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH)))
{
direction.whole = 0;
}
@ -738,7 +812,15 @@ command_result executePaintJob(color_ostream &out)
}
// make sure it's not invalid
if(type != tiletype::Void)
map.setTiletypeAt(*iter, type);
{
if (paint.stone_material >= 0)
{
if (!blk->setStoneAt(*iter, type, paint.stone_material, paint.vein_type, true, true))
failures++;
}
else
map.setTiletypeAt(*iter, type);
}
if (paint.hidden > -1)
{
@ -780,6 +862,11 @@ command_result executePaintJob(color_ostream &out)
map.setDesignationAt(*iter, des);
}
if (failures > 0)
out.printerr("Could not update %d tiles of %d.\n", failures, all_tiles.size());
else
out.print("Processed %d tiles.\n", all_tiles.size());
if (map.WriteAll())
{
out.print("OK\n");

@ -61,6 +61,7 @@
#include <stdlib.h>
using std::set;
using std::vector;
using std::string;
using std::endl;

@ -14,7 +14,7 @@
#include "df/enabler.h"
using namespace std;
using std::string;
using std::vector;
using std::map;

@ -45,6 +45,7 @@
using std::vector;
using std::string;
using std::endl;
using std::flush;
using namespace DFHack;
using namespace df::enums;

@ -4,61 +4,117 @@ local args = {...}
local rng = dfhack.random.new(3)
if #args < 3 then
qerror('Usage: devel/test-perlin <fname.pgm> <density> <coeff|expr...x[...y[...z]]>...')
qerror('Usage: devel/test-perlin <fname.pgm> <density> <expr> [-xy <xyscale>] [-z <zscale>]')
end
local fname = table.remove(args,1)
local goal = tonumber(table.remove(args,1)) or qerror('Invalid density')
local expr = table.remove(args,1) or qerror('No expression')
local zscale = 2
local xyscale = 1
local zscale = 6
local coeffs = {}
local fns = {}
for i = 1,#args,2 do
if args[i] == '-xy' then
xyscale = tonumber(args[i+1]) or qerror('Invalid xyscale')
end
if args[i] == '-z' then
zscale = tonumber(args[i+1]) or qerror('Invalid zscale')
end
end
local env = copyall(math)
env.apow = function(x,y) return math.pow(math.abs(x),y) end
local fn_env = copyall(math)
fn_env.rng = rng
fn_env.apow = function(x,y) return math.pow(math.abs(x),y) end
fn_env.spow = function(x,y) return x*math.pow(math.abs(x),y-1) end
-- Noise functions are referenced from expressions
-- as variables of form like "x3a", where:
-- 1) x is one of x/y/z/w independent functions in each octave
-- 2) 3 is the octave number; 0 corresponds to the whole range
-- 3) a is the subtype
-- Independent noise functions: support 4
local ids = { 'x', 'y', 'z', 'w' }
-- Subtype: provides an offset to the coordinates
local subs = {
[''] = { 0, 0, 0 },
a = { 0.5, 0, 0 },
b = { 0, 0.5, 0 },
c = { 0.5, 0.5, 0 },
d = { 0, 0, 0.5 },
e = { 0.5, 0, 0.5 },
f = { 0, 0.5, 0.5 },
g = { 0.5, 0.5, 0.5 },
}
function mkdelta(v)
if v == 0 then
return ''
else
return '+'..v
end
end
for i = 1,#args do
local fn = rng:perlin(3);
local fn2 = rng:perlin(3);
local fn3 = rng:perlin(3);
local fn4 = rng:perlin(3);
function mkexpr(expr)
-- Collect referenced variables
local max_octave = -1
local vars = {}
fns[i] = fn;
for var,id,octave,subtype in string.gmatch(expr,'%f[%w](([xyzw])(%d+)(%a*))%f[^%w]') do
if not vars[var] then
octave = tonumber(octave)
local val = string.match(args[i],'^([+-]?[%d.]+)$')
if val then
coeffs[i] = function(x) return x * val end
else
local argstr = 'x'
if string.match(args[i], '%f[%w]w%f[^%w]') then
argstr = 'x,y,z,w'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5), fn4(x+0.5,y+0.5,z+0.5)
end
elseif string.match(args[i], '%f[%w]z%f[^%w]') then
argstr = 'x,y,z'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5)
end
elseif string.match(args[i], '%f[%w]y%f[^%w]') then
argstr = 'x,y'
fns[i] = function(x,y,z)
return fn(x,y,z), fn2(x,y,z)
if octave > max_octave then
max_octave = octave
end
local sub = subs[subtype] or qerror('Invalid subtype: '..subtype)
vars[var] = { id = id, octave = octave, subtype = subtype, sub = sub }
end
end
if max_octave < 0 then
qerror('No noise function references in expression.')
end
local f,err = load(
'return function('..argstr..') return ('..args[i]..') end',
'=(expr)', 't', env
)
if not f then
qerror(err)
-- Allocate the noise functions
local code = ''
for i = 0,max_octave do
for j,id in ipairs(ids) do
code = code .. 'local _fn_'..i..'_'..id..' = rng:perlin(3)\n';
end
coeffs[i] = f()
end
-- Evaluate variables
code = code .. 'return function(x,y,z)\n'
for var,info in pairs(vars) do
local fn = '_fn_'..info.octave..'_'..info.id
local mul = math.pow(2,info.octave)
mul = math.min(48*4, mul)
code = code .. ' local '..var
.. ' = _fn_'..info.octave..'_'..info.id
.. '(x*'..mul..mkdelta(info.sub[1])
.. ',y*'..mul..mkdelta(info.sub[2])
.. ',z*'..mul..mkdelta(info.sub[3])
.. ')\n'
end
-- Complete and compile the function
code = code .. ' return ('..expr..')\nend\n'
local f,err = load(code, '=(expr)', 't', fn_env)
if not f then
qerror(err)
end
return f()
end
local field_fn = mkexpr(expr)
function render(thresh,file)
local area = 0
local line, arr = '', {}
@ -68,16 +124,10 @@ function render(thresh,file)
line = ''
for zx = 0,1 do
for x = 0,48*4-1 do
local tx = (0.5+x)/(48*2)
local ty = (0.5+y)/(48*2)
local tz = 0.5+(zx+zy*2)/(48*2/zscale)
local v1 = 0
for i = 1,#coeffs do
v1 = v1 + coeffs[i](fns[i](tx,ty,tz))
tx = tx*2
ty = ty*2
tz = tz*2
end
local tx = (0.5+x)/(48*4/xyscale)
local ty = (0.5+y)/(48*4/xyscale)
local tz = 0.3+(zx+zy*2)/(48*4/zscale)
local v1 = field_fn(tx, ty, tz)
local v = -1
if v1 > thresh then
v = v1;
@ -103,12 +153,13 @@ function render(thresh,file)
end
function search(fn,min,max,goal,eps)
while true do
local center = (max+min)/2
local center
for i = 1,32 do
center = (max+min)/2
local cval = fn(center)
print('At '..center..': '..cval)
if math.abs(cval-goal) < eps then
return center
break
end
if cval > goal then
min = center
@ -116,16 +167,19 @@ function search(fn,min,max,goal,eps)
max = center
end
end
return center
end
local thresh = search(render, -1, 1, goal, 0.001)
local thresh = search(render, -2, 2, goal, math.min(0.001,goal/20))
local file,err = io.open(fname, 'wb')
if not file then
print('error: ',err)
return
end
file:write('P5\n768 768\n255\n')
file:write('P5\n')
file:write('# '..goal..' '..expr..' '..xyscale..' '..zscale..'\n')
file:write('768 768\n255\n')
local area = render(thresh, file)
file:close()