Quick and dirty liquids port, some work on tiles.

develop
Petr Mrázek 2011-07-28 02:35:45 +02:00
parent 8dec3546a3
commit 01c1fc6c6a
3 changed files with 595 additions and 31 deletions

@ -135,3 +135,4 @@ DFHACK_PLUGIN(itemhacks itemhacks.cpp)
DFHACK_PLUGIN(notes notes.cpp)
DFHACK_PLUGIN(mode mode.cpp)
#DFHACK_PLUGIN(tiles tiles.cpp)
DFHACK_PLUGIN(liquids liquids.cpp)

@ -0,0 +1,502 @@
#include <iostream>
#include <vector>
#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>
#include <dfhack/extra/MapExtras.h>
using namespace MapExtras;
using namespace DFHack;
typedef vector <DFHack::DFCoord> coord_vec;
class Brush
{
public:
virtual ~Brush(){};
virtual coord_vec points(MapCache & mc,DFHack::DFCoord start) = 0;
};
/**
* 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(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;
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(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;
};
};
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)
{
commands.clear();
commands.push_back(PluginCommand("liquids", "Place magma, water or obsidian.", df_liquids));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( Core * c )
{
return CR_OK;
}
DFhackCExport command_result df_liquids (Core * c, vector <string> & parameters)
{
int32_t x,y,z;
uint32_t x_max,y_max,z_max;
DFHack::Maps * Maps;
DFHack::Gui * Position;
Brush * brush = new RectangleBrush(1,1);
string brushname = "point";
bool end = false;
c->con << "Welcome to the liquid spawner.\nType 'help' or '?' for a list of available commands, 'q' to quit.\nPress 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)
{
c->Resume();
string command = "";
std::stringstream str;
str <<"[" << mode << ":" << brushname << ":" << amount << ":" << flowmode << ":" << setmode << "]#";
if(c->con.lineedit(str.str(),command) == -1)
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
<< "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
<< "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";
}
else if(command == "point" || command == "p")
{
delete brush;
brushname = "point";
brush = new RectangleBrush(1,1);
}
else if(command == "range" || command == "r")
{
std::stringstream str;
str << " :set range width<" << width << "># ";
c->con.lineedit(str.str(),command);
width = command == "" ? width : atoi (command.c_str());
if(width < 1) width = 1;
str.clear();
str << " :set range height<" << height << "># ";
c->con.lineedit(str.str(),command);
height = command == "" ? height : atoi (command.c_str());
if(height < 1) height = 1;
str.clear();
str << " :set range z-levels<" << z_levels << "># ";
c->con.lineedit(str.str(),command);
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();
}
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())
{
c->Suspend();
Maps = c->getMaps();
Maps->Start();
Maps->getSize(x_max,y_max,z_max);
Position = c->getGui();
do
{
if(!Maps->Start())
{
c->con << "Can't see any DF map loaded." << endl;
break;
}
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;
MapCache mcache(Maps);
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);
DFHack::t_designation des = mcache.designationAt(*iter);
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")
{
set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
mcache.setTiletypeAt(*iter, 90);
DFHack::t_designation a = mcache.designationAt(*iter);
a.bits.liquid_type = DFHack::liquid_water;
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++;
}
}
else if(mode== "magma" || mode== "water" || mode == "flowbits")
{
set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end())
{
DFHack::DFCoord current = *iter;
DFHack::t_designation des = mcache.designationAt(current);
uint16_t tt = mcache.tiletypeAt(current);
DFHack::naked_designation & flow = des.bits;
// don't put liquids into places where they don't belong...
if(!DFHack::FlowPassable(tt))
{
iter++;
continue;
}
if(mode != "flowbits")
{
if(setmode == "s.")
{
flow.flow_size = amount;
}
else if(setmode == "s+")
{
if(flow.flow_size < amount)
flow.flow_size = amount;
}
else if(setmode == "s-")
{
if (flow.flow_size > amount)
flow.flow_size = amount;
}
if(amount != 0 && mode == "magma")
{
flow.liquid_type = DFHack::liquid_magma;
mcache.setTemp1At(current,12000);
mcache.setTemp2At(current,12000);
}
else if(amount != 0 && mode == "water")
{
flow.liquid_type = DFHack::liquid_water;
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);
}
seen_blocks.insert(mcache.BlockAt((*iter) / 16));
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;
Maps->Finish();
} while (0);
c->Resume();
}
else
{
c->con << command << " : unknown command." << endl;
}
}
return CR_OK;
}

@ -3,6 +3,7 @@
#include <map>
#include <set>
#include <cstdlib>
#include <sstream>
using std::vector;
using std::string;
@ -24,6 +25,7 @@ class Brush
{
public:
virtual ~Brush(){};
virtual string Name(){return "bogus";};
virtual coord_vec points(MapCache & mc,DFHack::DFCoord start) = 0;
};
/**
@ -51,6 +53,21 @@ public:
y_ = y;
z_ = z;
};
string Name()
{
if(x_ == 1 && y_ == 1 && z_ == 1)
return "point";
else
{
std::stringstream str;
str << x_ << "x" << y_;
if(z_ != 1)
{
str << "x" << z_;
}
return str.str();
}
}
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
@ -88,6 +105,10 @@ class BlockBrush : public Brush
public:
BlockBrush(){};
~BlockBrush(){};
string Name()
{
return "block";
}
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
@ -118,6 +139,10 @@ class ColumnBrush : public Brush
public:
ColumnBrush(){};
~ColumnBrush(){};
string Name()
{
return "column";
}
coord_vec points(MapCache & mc, DFHack::DFCoord start)
{
coord_vec v;
@ -137,9 +162,43 @@ public:
};
};
DFhackCExport command_result df_tile (Core * c, vector <string> & parameters);
DFhackCExport command_result df_tiles (Core * c, vector <string> & parameters);
DFhackCExport command_result df_paint (Core * c, vector <string> & parameters);
struct Settings
{
Settings()
{
brush = new RectangleBrush(1,1);
mode = none;
liquidamount = 0;
}
enum
{
none,
water,
magma,
obsidian_wall,
obsidian_floor,
obsidian_ramp,
river_source,
} mode;
int liquidamount;
enum
{
set_flow,
unset_flow,
ignore_flow,
} flowmode;
enum
{
liquid_set,
liquid_add,
liquid_remove,
} liquidmode;
Brush * brush;
} settings;
DFhackCExport const char * plugin_name ( void )
{
return "tiles";
@ -148,8 +207,8 @@ DFhackCExport const char * plugin_name ( void )
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{
commands.clear();
commands.push_back(PluginCommand("tile", "Set tile painter brush.", df_tile));
commands.push_back(PluginCommand("paint", "Paint with the current brush.", df_paint));
commands.push_back(PluginCommand("tiles", "A tile painter. See 'tile help' for details.", df_tiles));
commands.push_back(PluginCommand("paint", "Paint with the last used brush.", df_paint));
return CR_OK;
}
@ -158,62 +217,64 @@ DFhackCExport command_result plugin_shutdown ( Core * c )
return CR_OK;
}
DFhackCExport command_result df_tile (Core * c, vector <string> & parameters)
DFhackCExport command_result df_tiles (Core * c, vector <string> & parameters)
{
int32_t x,y,z;
uint32_t x_max,y_max,z_max;
DFHack::Maps * Maps;
DFHack::Gui * Position;
Brush * brush = new RectangleBrush(1,1);
string brushname = "point";
Maps = c->getMaps();
Maps->Start();
Maps->getSize(x_max,y_max,z_max);
Position = c->getGui();
string mode="magma";
string flowmode="f+";
string setmode ="s.";
unsigned int amount = 7;
int width = 1, height = 1, z_levels = 1;
string command = "";
if(command=="help" || command == "?")
{
c->con.print
(
"Usage: This command sets the properties of the tile painter brush\n"
" It is best used from the console, or as an alias bound to a hotkey\n"
" After setting the brush\n"
"\n"
"Modes:\n"
"m - switch to magma\n"
"w - switch to water\n"
"o - make obsidian wall instead\n"
"of - make obsidian floors\n"
"rs - make a river source\n"
"f - flow bits only\n"
"none - nothing, the default"
"magma [0-7] - magma, accepts depth\n"
"water [0-7] - water\n"
"obsidian - obsidian wall\n"
"obsfloor - obsidian floors\n"
"obsramp - obsidian ramp (forces 1 z-level brush)\n"
"riversource - an endless source of water (floor tile)\n"
"type ### - plain tiletype painter. For a list of tile types see:\n"
" http://df.magmawiki.com/index.php/DF2010:Tile_types_in_DF_memory\n"
"\n"
"Set-Modes (only for magma/water):\n"
"s+ - only add\n"
"s. - set\n"
"s- - only remove\n"
"Properties (only for magma/water):\n"
"f+ - make the spawned liquid flow\n"
"f. - don't change flow state (read state in flow mode)\n"
"f- - make the spawned liquid static\n"
"0-7 - set liquid amount\n"
"add - set liquid level everywhere\n"
"keep - set liquid level only where liquids are already present\n"
"\n"
"Brush:\n"
"point - single tile [p]\n"
"range - block with cursor at bottom north-west [r]\n"
"#x#[x#] - block with cursor at bottom north-west [r]\n"
" (any place, any size)\n"
" Example:"
" 3x3x2 = rectangle 3x3 x2 z-levels\n"
" The z-level part is optional - ommiting it is\n"
" the same as setting it to 1.\n"
"h#x#[x#] - Same as previous, only the rectangle is 'hollow'.\n"
"block - DF map block with cursor in it\n"
" (regular spaced 16x16x1 blocks)\n"
"column - Column from cursor, up through free space\n"
"line - A line between two points.\n"
"circle [#] - A filled circle, optional # specifies radius in tiles.\n"
"hcircle [#,#] - A hollow circle (ring). First # specifies radius\n"
" second # ring thickness in tiles.\n"
"\n"
"Other:\n"
"q - quit\n"
"help or ? - print this list of commands\n"
"empty line - put liquid\n"
"paint - same effect as if you also the 'paint' command at the same time.\n"
"\n"
"Usage: point the DF cursor at a tile you want to modify\n"
"and use the commands available :)\n"
);
}
else if(command == "m")