2011-07-27 18:35:45 -06:00
# include <iostream>
# include <vector>
2012-01-05 16:35:05 -07:00
# include <stack>
2011-07-27 18:35:45 -06:00
# include <map>
# include <set>
# include <cstdlib>
# include <sstream>
using std : : vector ;
using std : : string ;
using std : : endl ;
using std : : set ;
2011-12-31 04:48:42 -07:00
# include "Core.h"
2012-01-15 13:54:14 -07:00
# include "Console.h"
# include "Export.h"
# include "PluginManager.h"
# include "modules/Vegetation.h"
# include "modules/Maps.h"
# include "modules/Gui.h"
# include "TileTypes.h"
# include "modules/MapCache.h"
2011-07-27 18:35:45 -06:00
using namespace MapExtras ;
using namespace DFHack ;
2012-01-21 17:31:15 -07:00
using namespace df : : enums ;
typedef vector < df : : coord > coord_vec ;
2011-07-27 18:35:45 -06:00
class Brush
{
public :
virtual ~ Brush ( ) { } ;
virtual coord_vec points ( 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
{
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 ( 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 ( ) { } ;
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 ( MapCache & mc , DFHack : : DFCoord start )
{
coord_vec v ;
2011-10-24 18:38:21 -06:00
DFHack : : DFCoord blockc = start / 16 ;
2011-07-27 18:35:45 -06:00
DFHack : : DFCoord iterc = blockc * 16 ;
if ( ! mc . testCoord ( start ) )
return v ;
2011-10-24 18:38:21 -06:00
auto starty = iterc . y ;
2011-07-27 18:35:45 -06:00
for ( int xi = 0 ; xi < 16 ; xi + + )
{
for ( int yi = 0 ; yi < 16 ; yi + + )
{
v . push_back ( iterc ) ;
iterc . y + + ;
}
2011-10-24 18:38:21 -06:00
iterc . y = starty ;
2011-07-27 18:35:45 -06:00
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 ( 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 ;
} ;
} ;
2012-01-05 16:35:05 -07:00
/**
* 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 ( 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 ( ) ;
t_designation des = mc . designationAt ( xy ) ;
if ( seen . find ( xy ) = = seen . end ( )
& & des . bits . flow_size & & des . bits . liquid_type ! = liquid_magma ) {
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 ) ;
uint16_t 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 ;
}
private :
2012-01-06 04:10:11 -07:00
void maybeFlood ( DFCoord c , std : : stack < DFCoord > & to_flood , MapCache & mc ) {
2012-01-05 16:35:05 -07:00
if ( mc . testCoord ( c ) ) {
to_flood . push ( c ) ;
}
}
Core * c_ ;
} ;
2011-08-13 06:42:09 -06:00
CommandHistory liquids_hist ;
2011-07-27 18:35:45 -06:00
DFhackCExport command_result df_liquids ( Core * c , vector < string > & parameters ) ;
DFhackCExport const char * plugin_name ( void )
{
return " liquids " ;
}
DFhackCExport command_result plugin_init ( Core * c , std : : vector < PluginCommand > & commands )
{
2011-08-13 06:42:09 -06:00
liquids_hist . load ( " liquids.history " ) ;
2011-07-27 18:35:45 -06:00
commands . clear ( ) ;
2011-08-05 20:37:29 -06:00
commands . push_back ( PluginCommand ( " liquids " , " Place magma, water or obsidian. " , df_liquids , true ) ) ;
2011-07-27 18:35:45 -06:00
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( Core * c )
{
2011-08-13 06:42:09 -06:00
liquids_hist . save ( " liquids.history " ) ;
2011-07-27 18:35:45 -06:00
return CR_OK ;
}
DFhackCExport command_result df_liquids ( Core * c , vector < string > & parameters )
{
int32_t x , y , z ;
DFHack : : Gui * Position ;
2011-08-08 17:50:22 -06:00
for ( int i = 0 ; i < parameters . size ( ) ; i + + )
{
if ( parameters [ i ] = = " help " | | parameters [ i ] = = " ? " )
{
c - > con . print ( " This tool allows placing magma, water and other similar things. \n "
" It is interactive and further help is available when you run it. \n "
) ;
return CR_OK ;
}
}
2012-01-24 04:36:30 -07:00
if ( ! Maps : : IsValid ( ) )
{
c - > con . printerr ( " Map is not available! \n " ) ;
return CR_FAILURE ;
}
2011-07-27 18:35:45 -06:00
Brush * brush = new RectangleBrush ( 1 , 1 ) ;
string brushname = " point " ;
bool end = false ;
c - > con < < " Welcome to the liquid spawner. \n Type 'help' or '?' for a list of available commands, 'q' to quit. \n Press return after a command to confirm. " < < std : : endl ;
string mode = " magma " ;
string flowmode = " f+ " ;
string setmode = " s. " ;
unsigned int amount = 7 ;
int width = 1 , height = 1 , z_levels = 1 ;
while ( ! end )
{
string command = " " ;
std : : stringstream str ;
str < < " [ " < < mode < < " : " < < brushname < < " : " < < amount < < " : " < < flowmode < < " : " < < setmode < < " ]# " ;
2011-08-13 06:42:09 -06:00
if ( c - > con . lineedit ( str . str ( ) , command , liquids_hist ) = = - 1 )
2011-07-27 18:35:45 -06:00
return CR_FAILURE ;
if ( command = = " help " | | command = = " ? " )
{
c - > con < < " Modes: " < < endl
< < " m - switch to magma " < < endl
< < " w - switch to water " < < endl
< < " o - make obsidian wall instead " < < endl
< < " of - make obsidian floors " < < endl
< < " rs - make a river source " < < endl
< < " f - flow bits only " < < endl
2011-09-01 17:25:01 -06:00
< < " wclean - remove salt and stagnant flags from tiles " < < endl
2011-07-27 18:35:45 -06:00
< < " Set-Modes (only for magma/water): " < < endl
< < " s+ - only add " < < endl
< < " s. - set " < < endl
< < " s- - only remove " < < endl
< < " Properties (only for magma/water): " < < endl
< < " f+ - make the spawned liquid flow " < < endl
< < " f. - don't change flow state (read state in flow mode) " < < endl
< < " f- - make the spawned liquid static " < < endl
< < " 0-7 - set liquid amount " < < endl
< < " Brush: " < < endl
< < " point - single tile [p] " < < endl
< < " range - block with cursor at bottom north-west [r] " < < endl
< < " (any place, any size) " < < endl
< < " block - DF map block with cursor in it " < < endl
< < " (regular spaced 16x16x1 blocks) " < < endl
< < " column - Column from cursor, up through free space " < < endl
2012-01-05 16:35:05 -07:00
< < " flood - Flood-fill water tiles from cursor " < < endl
< < " (only makes sense with wclean) " < < endl
2011-07-27 18:35:45 -06:00
< < " Other: " < < endl
< < " q - quit " < < endl
< < " help or ? - print this list of commands " < < endl
< < " empty line - put liquid " < < endl
< < endl
< < " Usage: point the DF cursor at a tile you want to modify " < < endl
< < " and use the commands available :) " < < endl ;
}
else if ( command = = " m " )
{
mode = " magma " ;
}
else if ( command = = " o " )
{
mode = " obsidian " ;
}
else if ( command = = " of " )
{
mode = " obsidian_floor " ;
}
else if ( command = = " w " )
{
mode = " water " ;
}
else if ( command = = " f " )
{
mode = " flowbits " ;
}
else if ( command = = " rs " )
{
mode = " riversource " ;
}
2011-09-01 17:25:01 -06:00
else if ( command = = " wclean " )
{
mode = " wclean " ;
}
2011-07-27 18:35:45 -06:00
else if ( command = = " point " | | command = = " p " )
{
delete brush ;
brushname = " point " ;
brush = new RectangleBrush ( 1 , 1 ) ;
}
else if ( command = = " range " | | command = = " r " )
{
std : : stringstream str ;
2011-08-13 06:42:09 -06:00
CommandHistory range_hist ;
2011-07-27 18:35:45 -06:00
str < < " :set range width< " < < width < < " ># " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( str . str ( ) , command , range_hist ) ;
range_hist . add ( command ) ;
2011-07-27 18:35:45 -06:00
width = command = = " " ? width : atoi ( command . c_str ( ) ) ;
if ( width < 1 ) width = 1 ;
2011-08-14 17:30:15 -06:00
str . str ( " " ) ;
2011-07-27 18:35:45 -06:00
str < < " :set range height< " < < height < < " ># " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( str . str ( ) , command , range_hist ) ;
range_hist . add ( command ) ;
2011-07-27 18:35:45 -06:00
height = command = = " " ? height : atoi ( command . c_str ( ) ) ;
if ( height < 1 ) height = 1 ;
2011-08-14 17:30:15 -06:00
str . str ( " " ) ;
2011-07-27 18:35:45 -06:00
str < < " :set range z-levels< " < < z_levels < < " ># " ;
2011-08-13 06:42:09 -06:00
c - > con . lineedit ( str . str ( ) , command , range_hist ) ;
range_hist . add ( command ) ;
2011-07-27 18:35:45 -06:00
z_levels = command = = " " ? z_levels : atoi ( command . c_str ( ) ) ;
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 ( ) ;
}
2012-01-05 16:35:05 -07:00
else if ( command = = " flood " )
{
delete brush ;
brushname = " flood " ;
brush = new FloodBrush ( c ) ;
}
2011-07-27 18:35:45 -06:00
else if ( command = = " q " )
{
end = true ;
}
else if ( command = = " f+ " )
{
flowmode = " f+ " ;
}
else if ( command = = " f- " )
{
flowmode = " f- " ;
}
else if ( command = = " f. " )
{
flowmode = " f. " ;
}
else if ( command = = " s+ " )
{
setmode = " s+ " ;
}
else if ( command = = " s- " )
{
setmode = " s- " ;
}
else if ( command = = " s. " )
{
setmode = " s. " ;
}
// blah blah, bad code, bite me.
else if ( command = = " 0 " )
amount = 0 ;
else if ( command = = " 1 " )
amount = 1 ;
else if ( command = = " 2 " )
amount = 2 ;
else if ( command = = " 3 " )
amount = 3 ;
else if ( command = = " 4 " )
amount = 4 ;
else if ( command = = " 5 " )
amount = 5 ;
else if ( command = = " 6 " )
amount = 6 ;
else if ( command = = " 7 " )
amount = 7 ;
else if ( command . empty ( ) )
{
2012-01-21 17:31:15 -07:00
CoreSuspender suspend ( c ) ;
2011-07-27 18:35:45 -06:00
Position = c - > getGui ( ) ;
do
{
2012-01-19 20:44:17 -07:00
if ( ! Maps : : IsValid ( ) )
2011-07-27 18:35:45 -06:00
{
c - > con < < " Can't see any DF map loaded. " < < endl ;
2012-01-19 20:44:17 -07:00
break ; ;
2011-07-27 18:35:45 -06:00
}
if ( ! Position - > getCursorCoords ( x , y , z ) )
{
c - > con < < " Can't get cursor coords! Make sure you have a cursor active in DF. " < < endl ;
break ;
}
c - > con < < " cursor coords: " < < x < < " / " < < y < < " / " < < z < < endl ;
2012-01-19 20:44:17 -07:00
MapCache mcache ;
2011-07-27 18:35:45 -06:00
DFHack : : DFCoord cursor ( x , y , z ) ;
coord_vec all_tiles = brush - > points ( mcache , cursor ) ;
c - > con < < " working... " < < endl ;
if ( mode = = " obsidian " )
{
coord_vec : : iterator iter = all_tiles . begin ( ) ;
while ( iter ! = all_tiles . end ( ) )
{
mcache . setTiletypeAt ( * iter , 331 ) ;
mcache . setTemp1At ( * iter , 10015 ) ;
mcache . setTemp2At ( * iter , 10015 ) ;
2012-01-19 13:11:52 -07:00
df : : tile_designation des = mcache . designationAt ( * iter ) ;
2011-07-27 18:35:45 -06:00
des . bits . flow_size = 0 ;
mcache . setDesignationAt ( * iter , des ) ;
iter + + ;
}
}
if ( mode = = " obsidian_floor " )
{
coord_vec : : iterator iter = all_tiles . begin ( ) ;
while ( iter ! = all_tiles . end ( ) )
{
mcache . setTiletypeAt ( * iter , 340 ) ;
iter + + ;
}
}
else if ( mode = = " riversource " )
{
coord_vec : : iterator iter = all_tiles . begin ( ) ;
while ( iter ! = all_tiles . end ( ) )
{
mcache . setTiletypeAt ( * iter , 90 ) ;
2012-01-19 13:11:52 -07:00
df : : tile_designation a = mcache . designationAt ( * iter ) ;
2012-01-21 17:31:15 -07:00
a . bits . liquid_type = tile_liquid : : Water ;
2011-07-27 18:35:45 -06:00
a . bits . liquid_static = false ;
a . bits . flow_size = 7 ;
mcache . setTemp1At ( * iter , 10015 ) ;
mcache . setTemp2At ( * iter , 10015 ) ;
mcache . setDesignationAt ( * iter , a ) ;
Block * b = mcache . BlockAt ( ( * iter ) / 16 ) ;
DFHack : : t_blockflags bf = b - > BlockFlags ( ) ;
bf . bits . liquid_1 = true ;
bf . bits . liquid_2 = true ;
b - > setBlockFlags ( bf ) ;
iter + + ;
}
}
2011-09-01 17:25:01 -06:00
else if ( mode = = " wclean " )
{
coord_vec : : iterator iter = all_tiles . begin ( ) ;
while ( iter ! = all_tiles . end ( ) )
{
DFHack : : DFCoord current = * iter ;
2012-01-19 13:11:52 -07:00
df : : tile_designation des = mcache . designationAt ( current ) ;
2011-09-01 17:25:01 -06:00
des . bits . water_salt = false ;
des . bits . water_stagnant = false ;
mcache . setDesignationAt ( current , des ) ;
iter + + ;
}
}
2011-07-27 18:35:45 -06:00
else if ( mode = = " magma " | | mode = = " water " | | mode = = " flowbits " )
{
set < Block * > seen_blocks ;
coord_vec : : iterator iter = all_tiles . begin ( ) ;
while ( iter ! = all_tiles . end ( ) )
{
2011-10-24 18:38:21 -06:00
DFHack : : DFCoord current = * iter ; // current tile coord
DFHack : : DFCoord curblock = current / 16 ; // current block coord
// check if the block is actually there
if ( ! mcache . BlockAt ( curblock ) )
{
iter + + ;
continue ;
}
2012-01-19 13:11:52 -07:00
df : : tile_designation des = mcache . designationAt ( current ) ;
2011-07-27 18:35:45 -06:00
uint16_t tt = mcache . tiletypeAt ( current ) ;
// don't put liquids into places where they don't belong...
if ( ! DFHack : : FlowPassable ( tt ) )
{
iter + + ;
continue ;
}
if ( mode ! = " flowbits " )
{
if ( setmode = = " s. " )
{
2012-01-19 13:11:52 -07:00
des . bits . flow_size = amount ;
2011-07-27 18:35:45 -06:00
}
else if ( setmode = = " s+ " )
{
2012-01-19 13:11:52 -07:00
if ( des . bits . flow_size < amount )
des . bits . flow_size = amount ;
2011-07-27 18:35:45 -06:00
}
else if ( setmode = = " s- " )
{
2012-01-19 13:11:52 -07:00
if ( des . bits . flow_size > amount )
des . bits . flow_size = amount ;
2011-07-27 18:35:45 -06:00
}
if ( amount ! = 0 & & mode = = " magma " )
{
2012-01-21 17:31:15 -07:00
des . bits . liquid_type = tile_liquid : : Magma ;
2011-07-27 18:35:45 -06:00
mcache . setTemp1At ( current , 12000 ) ;
mcache . setTemp2At ( current , 12000 ) ;
}
else if ( amount ! = 0 & & mode = = " water " )
{
2012-01-21 17:31:15 -07:00
des . bits . liquid_type = tile_liquid : : Water ;
2011-07-27 18:35:45 -06:00
mcache . setTemp1At ( current , 10015 ) ;
mcache . setTemp2At ( current , 10015 ) ;
}
else if ( amount = = 0 & & ( mode = = " water " | | mode = = " magma " ) )
{
// reset temperature to sane default
mcache . setTemp1At ( current , 10015 ) ;
mcache . setTemp2At ( current , 10015 ) ;
}
mcache . setDesignationAt ( current , des ) ;
}
2011-10-24 18:38:21 -06:00
seen_blocks . insert ( mcache . BlockAt ( current / 16 ) ) ;
2011-07-27 18:35:45 -06:00
iter + + ;
}
set < Block * > : : iterator biter = seen_blocks . begin ( ) ;
while ( biter ! = seen_blocks . end ( ) )
{
DFHack : : t_blockflags bflags = ( * biter ) - > BlockFlags ( ) ;
if ( flowmode = = " f+ " )
{
bflags . bits . liquid_1 = true ;
bflags . bits . liquid_2 = true ;
( * biter ) - > setBlockFlags ( bflags ) ;
}
else if ( flowmode = = " f- " )
{
bflags . bits . liquid_1 = false ;
bflags . bits . liquid_2 = false ;
( * biter ) - > setBlockFlags ( bflags ) ;
}
else
{
c - > con < < " flow bit 1 = " < < bflags . bits . liquid_1 < < endl ;
c - > con < < " flow bit 2 = " < < bflags . bits . liquid_2 < < endl ;
}
biter + + ;
}
}
if ( mcache . WriteAll ( ) )
c - > con < < " OK " < < endl ;
else
c - > con < < " Something failed horribly! RUN! " < < endl ;
} while ( 0 ) ;
}
else
{
c - > con < < command < < " : unknown command. " < < endl ;
}
}
return CR_OK ;
}