2011-05-13 11:37:48 -06:00
//
# include <iostream>
# include <vector>
2011-08-10 20:39:12 -06:00
# include <map>
# include <set>
# include <cstdlib>
# include <sstream>
using std : : vector ;
using std : : string ;
using std : : endl ;
using std : : set ;
# include <dfhack/Core.h>
# include <dfhack/Console.h>
# include <dfhack/Export.h>
# include <dfhack/PluginManager.h>
# include <dfhack/modules/Vegetation.h>
# include <dfhack/modules/Maps.h>
# include <dfhack/modules/Gui.h>
# include <dfhack/TileTypes.h>
2011-05-13 11:37:48 -06:00
# include <dfhack/extra/MapExtras.h>
2011-08-10 20:39:12 -06:00
using namespace MapExtras ;
using namespace DFHack ;
2011-05-13 11:37:48 -06:00
2011-07-18 14:45:00 -06:00
//zilpin: These two functions were giving me compile errors in VS2008, so I cheated with the C style loop below, just to get it to build.
//Original code is commented out.
2011-05-13 11:37:48 -06:00
void tolower ( std : : string & str )
{
2011-08-10 20:39:12 -06:00
//The C++ way...
2011-07-18 14:45:00 -06:00
//std::transform(str.begin(), str.end(), str.begin(), std::bind2nd(std::ptr_fun(&std::tolower<char> ), std::locale("")));
2011-08-10 20:39:12 -06:00
//The C way...
for ( char * c = ( char * ) str . c_str ( ) ; * c ; + + c )
{
* c = tolower ( * c ) ;
}
2011-05-13 11:37:48 -06:00
}
void toupper ( std : : string & str )
{
2011-07-18 14:45:00 -06:00
//std::transform(str.begin(), str.end(), str.begin(), std::bind2nd(std::ptr_fun(&std::toupper<char>), std::locale("")));
2011-08-10 20:39:12 -06:00
for ( char * c = ( char * ) str . c_str ( ) ; * c ; + + c )
{
* c = toupper ( * c ) ;
}
2011-05-13 11:37:48 -06:00
}
int toint ( const std : : string & str , int failValue = 0 )
{
std : : istringstream ss ( str ) ;
int valInt ;
ss > > valInt ;
if ( ss . fail ( ) )
{
return failValue ;
}
return valInt ;
}
2011-08-10 20:39:12 -06:00
struct TileType
{
2011-05-13 11:37:48 -06:00
DFHack : : TileShape shape ;
DFHack : : TileMaterial material ;
DFHack : : TileSpecial special ;
TileType ( )
{
shape = DFHack : : tileshape_invalid ;
material = DFHack : : tilematerial_invalid ;
special = DFHack : : tilespecial_invalid ;
}
bool empty ( )
{
return shape = = - 1 & & material = = - 1 & & special = = - 1 ;
}
} ;
std : : ostream & operator < < ( std : : ostream & stream , const TileType & paint )
{
bool used = false ;
bool needSpace = false ;
if ( paint . special > = 0 )
{
stream < < DFHack : : TileSpecialString [ paint . special ] ;
used = true ;
needSpace = true ;
}
if ( paint . material > = 0 )
{
if ( needSpace )
{
stream < < " " ;
needSpace = false ;
}
stream < < DFHack : : TileMaterialString [ paint . material ] ;
used = true ;
needSpace = true ;
}
if ( paint . shape > = 0 )
{
if ( needSpace )
{
stream < < " " ;
needSpace = false ;
}
stream < < DFHack : : TileShapeString [ paint . shape ] ;
used = true ;
}
if ( ! used )
{
stream < < " any " ;
}
return stream ;
}
bool processTileType ( TileType & paint , const std : : string & option , const std : : string & value )
{
std : : string val = value ;
toupper ( val ) ;
int valInt ;
if ( val = = " ANY " )
{
valInt = - 1 ;
}
else
{
valInt = toint ( value , - 2 ) ;
}
bool found = false ;
if ( option = = " shape " | | option = = " sh " | | option = = " s " )
{
2011-08-05 20:37:29 -06:00
if ( valInt > = - 1 & & valInt < DFHack : : tileshape_count )
2011-05-13 11:37:48 -06:00
{
paint . shape = ( DFHack : : TileShape ) valInt ;
found = true ;
}
else
{
for ( int i = 0 ; i < DFHack : : tileshape_count ; i + + )
{
if ( val = = DFHack : : TileShapeString [ i ] )
{
paint . shape = ( DFHack : : TileShape ) i ;
found = true ;
break ;
}
}
if ( ! found )
{
std : : cout < < " Unknown tile shape: " < < value < < std : : endl ;
}
}
}
else if ( option = = " material " | | option = = " mat " | | option = = " m " )
{
if ( valInt > = - 1 & & valInt < DFHack : : tilematerial_count )
{
paint . material = ( DFHack : : TileMaterial ) valInt ;
found = true ;
}
else
{
for ( int i = 0 ; i < DFHack : : tilematerial_count ; i + + )
{
if ( val = = DFHack : : TileMaterialString [ i ] )
{
paint . material = ( DFHack : : TileMaterial ) i ;
found = true ;
break ;
}
}
if ( ! found )
{
std : : cout < < " Unknown tile material: " < < value < < std : : endl ;
}
}
}
else if ( option = = " special " | | option = = " sp " )
{
2011-08-05 20:37:29 -06:00
if ( valInt > = - 1 & & valInt < DFHack : : tilespecial_count )
2011-05-13 11:37:48 -06:00
{
paint . special = ( DFHack : : TileSpecial ) valInt ;
found = true ;
}
else
{
for ( int i = 0 ; i < DFHack : : tilespecial_count ; i + + )
{
if ( val = = DFHack : : TileSpecialString [ i ] )
{
paint . special = ( DFHack : : TileSpecial ) i ;
found = true ;
break ;
}
}
if ( ! found )
{
std : : cout < < " Unknown tile special: " < < value < < std : : endl ;
}
}
}
else
{
std : : cout < < " Unknown option: ' " < < option < < " ' " < < std : : endl ;
}
return found ;
}
2011-08-10 20:39:12 -06:00
void help ( std : : ostream & out , const std : : string & option )
2011-05-13 11:37:48 -06:00
{
if ( option . empty ( ) )
{
2011-08-10 20:39:12 -06:00
out < < " Commands: " < < std : : endl
< < " quit / q : quit " < < std : : endl
< < " filter / f [options] : change filter options " < < std : : endl
< < " paint / p [options] : change paint options " < < std : : endl
< < " point / p : set point brush " < < std : : endl
< < " range / r : set range brush " < < std : : endl
< < " block : set block brush " < < std : : endl
< < " column : set column brush " < < std : : endl
< < std : : endl
< < " Filter/paint options: " < < std : : endl
< < " Shape / sh / s: set tile shape information " < < std : : endl
< < " Material / mat / m: set tile material information " < < std : : endl
< < " Special / s: set special tile information " < < std : : endl
< < " See help [option] for more information " < < std : : endl ;
2011-05-13 11:37:48 -06:00
}
2011-08-14 17:59:57 -06:00
else if ( option = = " shape " | | option = = " s " | | option = = " sh " )
2011-05-13 11:37:48 -06:00
{
2011-08-10 20:39:12 -06:00
out < < " Available shapes: " < < std : : endl
< < " ANY " < < std : : endl ;
2011-05-13 11:37:48 -06:00
for ( int i = 0 ; i < DFHack : : tileshape_count ; i + + )
{
2011-08-10 20:39:12 -06:00
out < < " " < < DFHack : : TileShapeString [ i ] < < std : : endl ;
2011-05-13 11:37:48 -06:00
}
}
2011-08-14 17:59:57 -06:00
else if ( option = = " material " | | option = = " mat " | | option = = " m " )
2011-05-13 11:37:48 -06:00
{
2011-08-10 20:39:12 -06:00
out < < " Available materials: " < < std : : endl
< < " ANY " < < std : : endl ;
2011-05-13 11:37:48 -06:00
for ( int i = 0 ; i < DFHack : : tilematerial_count ; i + + )
{
2011-08-10 20:39:12 -06:00
out < < " " < < DFHack : : TileMaterialString [ i ] < < std : : endl ;
2011-05-13 11:37:48 -06:00
}
}
else if ( option = = " special " )
{
2011-08-10 20:39:12 -06:00
out < < " Available specials: " < < std : : endl
< < " ANY " < < std : : endl ;
2011-05-13 11:37:48 -06:00
for ( int i = 0 ; i < DFHack : : tilespecial_count ; i + + )
{
2011-08-10 20:39:12 -06:00
out < < " " < < DFHack : : TileSpecialString [ i ] < < std : : endl ;
2011-05-13 11:37:48 -06:00
}
}
}
typedef std : : vector < DFHack : : DFCoord > coord_vec ;
class Brush
{
public :
virtual ~ Brush ( ) { } ;
virtual coord_vec points ( MapExtras : : MapCache & mc , DFHack : : DFCoord start ) = 0 ;
} ;
/**
* generic 3 D rectangle brush . you can specify the dimensions of
* the rectangle and optionally which tile is its ' center '
*/
class RectangleBrush : public Brush
{
int x_ , y_ , z_ ;
int cx_ , cy_ , cz_ ;
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 ( ) { } ;
} ;
/**
* 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 ;
for ( int xi = 0 ; xi < 16 ; xi + + )
{
for ( int yi = 0 ; yi < 16 ; yi + + )
{
v . push_back ( iterc ) ;
iterc . y + + ;
}
iterc . x + + ;
}
return v ;
} ;
} ;
/**
* 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 ) )
{
uint16_t tt = mc . tiletypeAt ( start ) ;
if ( DFHack : : LowPassable ( tt ) | | juststarted & & DFHack : : HighPassable ( tt ) )
{
v . push_back ( start ) ;
juststarted = false ;
start . z + + ;
}
else break ;
}
return v ;
} ;
} ;
2011-08-13 06:42:09 -06:00
CommandHistory tiletypes_hist ;
2011-08-10 20:39:12 -06:00
DFhackCExport command_result df_tiletypes ( Core * c , vector < string > & parameters ) ;
DFhackCExport const char * plugin_name ( void )
{
return " tiletypes " ;
}
DFhackCExport command_result plugin_init ( Core * c , std : : vector < PluginCommand > & commands )
{
2011-08-13 06:42:09 -06:00
tiletypes_hist . load ( " tiletypes.history " ) ;
2011-08-10 20:39:12 -06:00
commands . clear ( ) ;
commands . push_back ( PluginCommand ( " tiletypes " , " Paint map tiles freely, similar to liquids. " , df_tiletypes , true ) ) ;
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( Core * c )
{
2011-08-13 06:42:09 -06:00
tiletypes_hist . save ( " tiletypes.history " ) ;
2011-08-10 20:39:12 -06:00
return CR_OK ;
}
DFhackCExport command_result df_tiletypes ( Core * c , vector < string > & parameters )
2011-05-13 11:37:48 -06:00
{
uint32_t x_max = 0 , y_max = 0 , z_max = 0 ;
int32_t x = 0 , y = 0 , z = 0 ;
2011-08-10 20:39:12 -06:00
DFHack : : Maps * maps ;
DFHack : : Gui * gui ;
2011-08-14 00:42:21 -06:00
for ( int i = 0 ; i < parameters . size ( ) ; i + + )
{
if ( parameters [ i ] = = " help " | | parameters [ i ] = = " ? " )
{
c - > con . print ( " This tool allows painting tiles types with a brush, using an optional filter. \n "
" The tool is interactive, similarly to the liquids tool. \n "
" Further help is available inside. \n "
) ;
return CR_OK ;
}
}
2011-05-13 11:37:48 -06:00
TileType filter , paint ;
Brush * brush = new RectangleBrush ( 1 , 1 ) ;
bool end = false ;
std : : string brushname = " point " ;
int width = 1 , height = 1 , z_levels = 1 ;
2011-08-14 00:42:21 -06:00
c - > con < < " Welcome to the tiletype tool. \n Type 'help' or '?' for a list of available commands, 'q' to quit. \n Press return after a command to confirm. " < < std : : endl ;
2011-08-14 17:59:57 -06:00
c - > con . printerr ( " THIS TOOL CAN BE DANGEROUS. YOU'VE BEEN WARNED. \n " ) ;
2011-05-13 11:37:48 -06:00
while ( ! end )
{
2011-08-10 20:39:12 -06:00
c - > con < < " Filter: " < < filter < < std : : endl
< < " Paint: " < < paint < < std : : endl
< < " Brush: " < < brushname < < std : : endl ;
2011-05-13 11:37:48 -06:00
std : : string input = " " ;
std : : string command = " " ;
std : : string option = " " ;
std : : string value = " " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( " tiletypes> " , input , tiletypes_hist ) ;
tiletypes_hist . add ( input ) ;
2011-05-13 11:37:48 -06:00
std : : istringstream ss ( input ) ;
ss > > command > > option > > value ;
tolower ( command ) ;
tolower ( option ) ;
if ( command = = " help " | | command = = " ? " )
{
2011-08-10 20:39:12 -06:00
help ( c - > con , option ) ;
2011-05-13 11:37:48 -06:00
}
else if ( command = = " quit " | | command = = " q " )
{
end = true ;
}
else if ( command = = " filter " | | command = = " f " )
{
processTileType ( filter , option , value ) ;
}
else if ( command = = " paint " | | ( command = = " p " & & ! option . empty ( ) ) )
{
processTileType ( paint , option , value ) ;
}
else if ( command = = " point " | | command = = " p " )
{
delete brush ;
brushname = " point " ;
brush = new RectangleBrush ( 1 , 1 ) ;
}
else if ( command = = " range " | | command = = " r " )
{
2011-08-10 20:39:12 -06:00
std : : stringstream ss ;
2011-08-13 06:42:09 -06:00
CommandHistory hist ;
2011-08-10 20:39:12 -06:00
ss < < " Set range width < " < < width < < " > " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( ss . str ( ) , command , hist ) ;
2011-05-13 11:37:48 -06:00
width = command = = " " ? width : toint ( command ) ;
if ( width < 1 ) width = 1 ;
2011-08-10 20:39:12 -06:00
ss . str ( " " ) ;
ss < < " Set range height < " < < height < < " > " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( ss . str ( ) , command , hist ) ;
2011-05-13 11:37:48 -06:00
height = command = = " " ? height : toint ( command ) ;
if ( height < 1 ) height = 1 ;
2011-08-10 20:39:12 -06:00
ss . str ( " " ) ;
ss < < " Set range z-levels < " < < z_levels < < " > " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( ss . str ( ) , command , hist ) ;
2011-05-13 11:37:48 -06:00
z_levels = command = = " " ? z_levels : toint ( command ) ;
if ( z_levels < 1 ) z_levels = 1 ;
delete brush ;
if ( width = = 1 & & height = = 1 & & z_levels = = 1 )
{
brushname = " point " ;
}
else
{
brushname = " range " ;
}
brush = new RectangleBrush ( width , height , z_levels , 0 , 0 , 0 ) ;
}
else if ( command = = " block " )
{
delete brush ;
brushname = " block " ;
brush = new BlockBrush ( ) ;
}
else if ( command = = " column " )
{
delete brush ;
brushname = " column " ;
brush = new ColumnBrush ( ) ;
}
else if ( command . empty ( ) )
{
if ( paint . empty ( ) )
{
2011-08-10 20:39:12 -06:00
c - > con . printerr ( " Set the paint first. \n " ) ;
2011-05-13 11:37:48 -06:00
continue ;
}
2011-08-10 20:39:12 -06:00
c - > Suspend ( ) ;
maps = c - > getMaps ( ) ;
gui = c - > getGui ( ) ;
2011-05-13 11:37:48 -06:00
if ( ! maps - > Start ( ) )
{
2011-08-10 20:39:12 -06:00
c - > con . printerr ( " Cannot get map info! \n " ) ;
c - > Resume ( ) ;
return CR_FAILURE ;
2011-05-13 11:37:48 -06:00
}
maps - > getSize ( x_max , y_max , z_max ) ;
if ( ! ( gui - > Start ( ) & & gui - > getCursorCoords ( x , y , z ) ) )
{
2011-08-10 20:39:12 -06:00
c - > con . printerr ( " Can't get cursor coords! Make sure you have a cursor active in DF. \n " ) ;
c - > Resume ( ) ;
return CR_FAILURE ;
2011-05-13 11:37:48 -06:00
}
2011-08-10 20:39:12 -06:00
c - > con . print ( " Cursor coords: (%d, %d, %d) \n " , x , y , z ) ;
2011-05-13 11:37:48 -06:00
DFHack : : DFCoord cursor ( x , y , z ) ;
MapExtras : : MapCache map ( maps ) ;
coord_vec all_tiles = brush - > points ( map , cursor ) ;
2011-08-10 20:39:12 -06:00
c - > con . print ( " working... \n " ) ;
2011-05-13 11:37:48 -06:00
for ( coord_vec : : iterator iter = all_tiles . begin ( ) ; iter ! = all_tiles . end ( ) ; + + iter )
{
const DFHack : : TileRow * source = DFHack : : getTileRow ( map . tiletypeAt ( * iter ) ) ;
if ( ( filter . shape > - 1 & & filter . shape ! = source - > shape )
2011-08-10 20:39:12 -06:00
| | ( filter . material > - 1 & & filter . material ! = source - > material )
| | ( filter . special > - 1 & & filter . special ! = source - > special ) )
2011-05-13 11:37:48 -06:00
{
continue ;
}
DFHack : : TileShape shape = paint . shape ;
if ( shape < 0 )
{
shape = source - > shape ;
}
DFHack : : TileMaterial material = paint . material ;
if ( material < 0 )
{
material = source - > material ;
}
DFHack : : TileSpecial special = paint . special ;
if ( special < 0 )
{
special = source - > special ;
}
int32_t type = DFHack : : findTileType ( shape , material , source - > variant , special , source - > direction ) ;
2011-08-14 17:30:15 -06:00
// make sure it's not invalid
if ( type ! = - 1 )
map . setTiletypeAt ( * iter , type ) ;
2011-05-13 11:37:48 -06:00
// Remove liquid from walls, etc
if ( ! DFHack : : FlowPassable ( shape ) )
{
DFHack : : t_designation des = map . designationAt ( * iter ) ;
des . bits . flow_size = 0 ;
map . setDesignationAt ( * iter , des ) ;
}
}
if ( map . WriteAll ( ) )
{
2011-08-10 20:39:12 -06:00
c - > con . print ( " OK \n " ) ;
2011-05-13 11:37:48 -06:00
}
else
{
2011-08-10 20:39:12 -06:00
c - > con . printerr ( " Something failed horribly! RUN! \n " ) ;
2011-05-13 11:37:48 -06:00
}
maps - > Finish ( ) ;
2011-08-10 20:39:12 -06:00
c - > Resume ( ) ;
2011-05-13 11:37:48 -06:00
}
}
2011-08-10 20:39:12 -06:00
return CR_OK ;
2011-05-13 11:37:48 -06:00
}