2012-03-23 17:13:16 -06:00
|
|
|
#pragma once
|
2012-05-10 23:00:18 -06:00
|
|
|
#include <llimits.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
#include <stack>
|
|
|
|
#include <set>
|
2012-03-23 17:13:16 -06:00
|
|
|
|
|
|
|
typedef vector <df::coord> coord_vec;
|
|
|
|
class Brush
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~Brush(){};
|
|
|
|
virtual coord_vec points(MapExtras::MapCache & mc,DFHack::DFCoord start) = 0;
|
2012-04-15 08:40:19 -06:00
|
|
|
virtual std::string str() const {
|
|
|
|
return "unknown";
|
|
|
|
}
|
2012-03-23 17:13:16 -06:00
|
|
|
};
|
|
|
|
/**
|
|
|
|
* 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(){};
|
2012-04-15 08:40:19 -06:00
|
|
|
std::string str() const {
|
2012-05-10 23:00:18 -06:00
|
|
|
if (x_ == 1 && y_ == 1 && z_ == 1)
|
|
|
|
{
|
2012-04-15 08:40:19 -06:00
|
|
|
return "point";
|
2012-05-10 23:00:18 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << "rect: " << x_ << "/" << y_ << "/" << z_ << std::endl;
|
|
|
|
return ss.str();
|
2012-04-15 08:40:19 -06:00
|
|
|
}
|
|
|
|
}
|
2012-03-23 17:13:16 -06:00
|
|
|
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;
|
|
|
|
};
|
2012-04-15 08:40:19 -06:00
|
|
|
std::string str() const {
|
|
|
|
return "block";
|
|
|
|
}
|
2012-03-23 17:13:16 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
};
|
2012-04-15 08:40:19 -06:00
|
|
|
std::string str() const {
|
|
|
|
return "column";
|
|
|
|
}
|
2012-03-23 17:13:16 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flood-fill water tiles from cursor (for wclean)
|
|
|
|
* example: remove salt flag from a river
|
|
|
|
*/
|
|
|
|
class FloodBrush : public Brush
|
|
|
|
{
|
|
|
|
public:
|
2016-10-15 12:53:10 -06:00
|
|
|
FloodBrush(DFHack::Core *c){c_ = c;};
|
2012-03-23 17:13:16 -06:00
|
|
|
~FloodBrush(){};
|
|
|
|
coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start)
|
|
|
|
{
|
2016-10-15 12:53:10 -06:00
|
|
|
using namespace DFHack;
|
2012-03-23 17:13:16 -06:00
|
|
|
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;
|
|
|
|
}
|
2012-04-15 08:40:19 -06:00
|
|
|
std::string str() const {
|
|
|
|
return "flood";
|
|
|
|
}
|
2012-03-23 17:13:16 -06:00
|
|
|
private:
|
2016-10-15 12:53:10 -06:00
|
|
|
void maybeFlood(DFHack::DFCoord c, std::stack<DFHack::DFCoord> &to_flood,
|
|
|
|
MapExtras::MapCache &mc) {
|
2012-03-23 17:13:16 -06:00
|
|
|
if (mc.testCoord(c)) {
|
|
|
|
to_flood.push(c);
|
|
|
|
}
|
|
|
|
}
|
2016-10-15 12:53:10 -06:00
|
|
|
DFHack::Core *c_;
|
2012-03-23 17:13:16 -06:00
|
|
|
};
|
2012-04-15 08:40:19 -06:00
|
|
|
|
2016-10-15 12:53:10 -06:00
|
|
|
DFHack::command_result parseRectangle(DFHack::color_ostream & out,
|
2012-04-21 11:26:40 -06:00
|
|
|
vector<string> & input, int start, int end,
|
2012-04-19 21:13:07 -06:00
|
|
|
int & width, int & height, int & zLevels,
|
|
|
|
bool hasConsole = true)
|
|
|
|
{
|
2016-10-15 12:53:10 -06:00
|
|
|
using namespace DFHack;
|
2012-05-07 18:31:28 -06:00
|
|
|
int newWidth = 0, newHeight = 0, newZLevels = 0;
|
2012-04-21 11:26:40 -06:00
|
|
|
|
|
|
|
if (end > start + 1)
|
|
|
|
{
|
|
|
|
newWidth = atoi(input[start++].c_str());
|
|
|
|
newHeight = atoi(input[start++].c_str());
|
2012-05-07 18:31:28 -06:00
|
|
|
if (end > start) {
|
|
|
|
newZLevels = atoi(input[start++].c_str());
|
|
|
|
} else {
|
|
|
|
newZLevels = 1; // So 'range w h' won't ask for it.
|
|
|
|
}
|
2012-04-21 11:26:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
string command = "";
|
|
|
|
std::stringstream str;
|
|
|
|
CommandHistory hist;
|
|
|
|
|
|
|
|
if (newWidth < 1) {
|
2012-04-19 21:13:07 -06:00
|
|
|
if (hasConsole) {
|
|
|
|
Console &con = static_cast<Console&>(out);
|
|
|
|
|
|
|
|
str.str("");
|
2012-04-21 11:26:40 -06:00
|
|
|
str << "Set range width <" << width << "> ";
|
2012-04-19 21:13:07 -06:00
|
|
|
con.lineedit(str.str(), command, hist);
|
|
|
|
hist.add(command);
|
2012-05-07 18:31:28 -06:00
|
|
|
newWidth = command.empty() ? width : atoi(command.c_str());
|
2012-04-21 11:26:40 -06:00
|
|
|
} else {
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newHeight < 1) {
|
|
|
|
if (hasConsole) {
|
|
|
|
Console &con = static_cast<Console&>(out);
|
2012-04-19 21:13:07 -06:00
|
|
|
|
|
|
|
str.str("");
|
2012-04-21 11:26:40 -06:00
|
|
|
str << "Set range height <" << height << "> ";
|
2012-04-19 21:13:07 -06:00
|
|
|
con.lineedit(str.str(), command, hist);
|
|
|
|
hist.add(command);
|
2012-05-07 18:31:28 -06:00
|
|
|
newHeight = command.empty() ? height : atoi(command.c_str());
|
2012-04-21 11:26:40 -06:00
|
|
|
} else {
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newZLevels < 1) {
|
|
|
|
if (hasConsole) {
|
|
|
|
Console &con = static_cast<Console&>(out);
|
2012-04-19 21:13:07 -06:00
|
|
|
|
|
|
|
str.str("");
|
2012-04-21 11:26:40 -06:00
|
|
|
str << "Set range z-levels <" << zLevels << "> ";
|
2012-04-19 21:13:07 -06:00
|
|
|
con.lineedit(str.str(), command, hist);
|
|
|
|
hist.add(command);
|
2012-05-07 18:31:28 -06:00
|
|
|
newZLevels = command.empty() ? zLevels : atoi(command.c_str());
|
2012-04-19 21:13:07 -06:00
|
|
|
} else {
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-21 11:26:40 -06:00
|
|
|
width = newWidth < 1? 1 : newWidth;
|
|
|
|
height = newHeight < 1? 1 : newHeight;
|
2012-05-07 18:31:28 -06:00
|
|
|
zLevels = newZLevels < 1? 1 : newZLevels;
|
2012-04-19 21:13:07 -06:00
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::ostream &operator<<(std::ostream &stream, const Brush& brush)
|
|
|
|
{
|
2012-04-15 08:40:19 -06:00
|
|
|
stream << brush.str();
|
2012-04-16 20:43:30 -06:00
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
2012-04-19 21:13:07 -06:00
|
|
|
inline std::ostream &operator<<(std::ostream &stream, const Brush* brush)
|
|
|
|
{
|
2012-04-16 20:43:30 -06:00
|
|
|
stream << brush->str();
|
|
|
|
return stream;
|
2012-04-15 08:40:19 -06:00
|
|
|
}
|