dfhack/plugins/Brushes.h

218 lines
5.4 KiB
C++

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