Expose the liquids plugin engine to lua, and make a wrapper gui script.

develop
Alexander Gavrilov 2012-08-25 10:37:03 +04:00
parent 21904fd607
commit 41ad42d0fd
8 changed files with 491 additions and 129 deletions

@ -48,3 +48,6 @@ keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
# browse rooms of same owner # browse rooms of same owner
keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work
# interface for the liquids plugin
keybinding add Alt-L@dwarfmode/LookAround gui/liquids

@ -97,6 +97,9 @@ namespace DFHack
/* /*
* Cursor and window coords * Cursor and window coords
*/ */
DFHACK_EXPORT df::coord getViewportPos();
DFHACK_EXPORT df::coord getCursorPos();
DFHACK_EXPORT bool getViewCoords (int32_t &x, int32_t &y, int32_t &z); DFHACK_EXPORT bool getViewCoords (int32_t &x, int32_t &y, int32_t &z);
DFHACK_EXPORT bool setViewCoords (const int32_t x, const int32_t y, const int32_t z); DFHACK_EXPORT bool setViewCoords (const int32_t x, const int32_t y, const int32_t z);

@ -18,7 +18,7 @@ function simulateInput(screen,...)
error('Invalid keycode: '..arg) error('Invalid keycode: '..arg)
end end
end end
if type(arg) == 'number' then if type(kv) == 'number' then
keys[#keys+1] = kv keys[#keys+1] = kv
end end
end end
@ -277,6 +277,9 @@ end
function Screen:onDismiss() function Screen:onDismiss()
end end
function Screen:onDestroy()
end
function Screen:onResize(w,h) function Screen:onResize(w,h)
self:updateLayout() self:updateLayout()
end end

@ -6,6 +6,9 @@ local gui = require('gui')
local utils = require('utils') local utils = require('utils')
local dscreen = dfhack.screen local dscreen = dfhack.screen
local g_cursor = df.global.cursor
local g_sel_rect = df.global.selection_rect
local world_map = df.global.world.map local world_map = df.global.world.map
AREA_MAP_WIDTH = 23 AREA_MAP_WIDTH = 23
@ -43,8 +46,8 @@ function getPanelLayout()
end end
function getCursorPos() function getCursorPos()
if df.global.cursor.x ~= -30000 then if g_cursor ~= -30000 then
return copyall(df.global.cursor) return copyall(g_cursor)
end end
end end
@ -56,6 +59,51 @@ function clearCursorPos()
df.global.cursor = xyz2pos(nil) df.global.cursor = xyz2pos(nil)
end end
function getSelection()
local p1, p2
if g_sel_rect.start_x ~= -30000 then
p1 = xyz2pos(g_sel_rect.start_x, g_sel_rect.start_y, g_sel_rect.start_z)
end
if g_sel_rect.end_x ~= -30000 then
p2 = xyz2pos(g_sel_rect.end_x, g_sel_rect.end_y, g_sel_rect.end_z)
end
return p1, p2
end
function setSelectionStart(pos)
g_sel_rect.start_x = pos.x
g_sel_rect.start_y = pos.y
g_sel_rect.start_z = pos.z
end
function setSelectionEnd(pos)
g_sel_rect.end_x = pos.x
g_sel_rect.end_y = pos.y
g_sel_rect.end_z = pos.z
end
function clearSelection()
g_sel_rect.start_x = -30000
g_sel_rect.start_y = -30000
g_sel_rect.start_z = -30000
g_sel_rect.end_x = -30000
g_sel_rect.end_y = -30000
g_sel_rect.end_z = -30000
end
function getSelectionRange(p1, p2)
local r1 = xyz2pos(
math.min(p1.x, p2.x), math.min(p1.y, p2.y), math.min(p1.z, p2.z)
)
local r2 = xyz2pos(
math.max(p1.x, p2.x), math.max(p1.y, p2.y), math.max(p1.z, p2.z)
)
local sz = xyz2pos(
r2.x - r1.x + 1, r2.y - r1.y + 1, r2.z - r1.z + 1
)
return r1, sz, r2
end
Viewport = defclass(Viewport) Viewport = defclass(Viewport)
function Viewport.make(map,x,y,z) function Viewport.make(map,x,y,z)

@ -998,6 +998,23 @@ df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed)
return ws; return ws;
} }
df::coord Gui::getViewportPos()
{
if (!df::global::window_x || !df::global::window_y || !df::global::window_z)
return df::coord(0,0,0);
return df::coord(*df::global::window_x, *df::global::window_y, *df::global::window_z);
}
df::coord Gui::getCursorPos()
{
using df::global::cursor;
if (!cursor)
return df::coord();
return df::coord(cursor->x, cursor->y, cursor->z);
}
bool Gui::getViewCoords (int32_t &x, int32_t &y, int32_t &z) bool Gui::getViewCoords (int32_t &x, int32_t &y, int32_t &z)
{ {
x = *df::global::window_x; x = *df::global::window_x;

@ -27,6 +27,7 @@
#include <set> #include <set>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <memory>
using std::vector; using std::vector;
using std::string; using std::string;
using std::endl; using std::endl;
@ -41,6 +42,7 @@ using std::set;
#include "modules/Gui.h" #include "modules/Gui.h"
#include "TileTypes.h" #include "TileTypes.h"
#include "modules/MapCache.h" #include "modules/MapCache.h"
#include "LuaTools.h"
#include "Brushes.h" #include "Brushes.h"
using namespace MapExtras; using namespace MapExtras;
using namespace DFHack; using namespace DFHack;
@ -50,7 +52,6 @@ CommandHistory liquids_hist;
command_result df_liquids (color_ostream &out, vector <string> & parameters); command_result df_liquids (color_ostream &out, vector <string> & parameters);
command_result df_liquids_here (color_ostream &out, vector <string> & parameters); command_result df_liquids_here (color_ostream &out, vector <string> & parameters);
command_result df_liquids_execute (color_ostream &out);
DFHACK_PLUGIN("liquids"); DFHACK_PLUGIN("liquids");
@ -74,13 +75,49 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK; return CR_OK;
} }
// static stuff to be remembered between sessions enum BrushType {
static string brushname = "point"; B_POINT, B_RANGE, B_BLOCK, B_COLUMN, B_FLOOD
static string mode="magma"; };
static string flowmode="f+";
static string _setmode ="s."; static const char *brush_name[] = {
static unsigned int amount = 7; "point", "range", "block", "column", "flood", NULL
static int width = 1, height = 1, z_levels = 1; };
enum PaintMode {
P_WATER, P_MAGMA, P_OBSIDIAN, P_OBSIDIAN_FLOOR,
P_RIVER_SOURCE, P_FLOW_BITS, P_WCLEAN
};
static const char *paint_mode_name[] = {
"water", "magma", "obsidian", "obsidian_floor",
"riversource", "flowbits", "wclean", NULL
};
enum ModifyMode {
M_INC, M_KEEP, M_DEC
};
static const char *modify_mode_name[] = {
"+", ".", "-", NULL
};
struct OperationMode {
BrushType brush;
PaintMode paint;
ModifyMode flowmode;
ModifyMode setmode;
unsigned int amount;
df::coord size;
OperationMode() :
brush(B_POINT), paint(P_MAGMA),
flowmode(M_INC), setmode(M_KEEP), amount(7),
size(1,1,1)
{}
} cur_mode;
command_result df_liquids_execute(color_ostream &out);
command_result df_liquids_execute(color_ostream &out, OperationMode &mode, df::coord pos);
command_result df_liquids (color_ostream &out_, vector <string> & parameters) command_result df_liquids (color_ostream &out_, vector <string> & parameters)
{ {
@ -117,10 +154,11 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
string input = ""; string input = "";
std::stringstream str; std::stringstream str;
str <<"[" << mode << ":" << brushname; str <<"[" << paint_mode_name[cur_mode.paint] << ":" << brush_name[cur_mode.brush];
if (brushname == "range") if (cur_mode.brush == B_RANGE)
str << "(w" << width << ":h" << height << ":z" << z_levels << ")"; str << "(w" << cur_mode.size.x << ":h" << cur_mode.size.y << ":z" << cur_mode.size.z << ")";
str << ":" << amount << ":" << flowmode << ":" << _setmode << "]#"; str << ":" << cur_mode.amount << ":f" << modify_mode_name[cur_mode.flowmode]
<< ":s" << modify_mode_name[cur_mode.setmode] << "]#";
if(out.lineedit(str.str(),input,liquids_hist) == -1) if(out.lineedit(str.str(),input,liquids_hist) == -1)
return CR_FAILURE; return CR_FAILURE;
liquids_hist.add(input); liquids_hist.add(input);
@ -168,38 +206,39 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
} }
else if(command == "m") else if(command == "m")
{ {
mode = "magma"; cur_mode.paint = P_MAGMA;
} }
else if(command == "o") else if(command == "o")
{ {
mode = "obsidian"; cur_mode.paint = P_OBSIDIAN;
} }
else if(command == "of") else if(command == "of")
{ {
mode = "obsidian_floor"; cur_mode.paint = P_OBSIDIAN_FLOOR;
} }
else if(command == "w") else if(command == "w")
{ {
mode = "water"; cur_mode.paint = P_WATER;
} }
else if(command == "f") else if(command == "f")
{ {
mode = "flowbits"; cur_mode.paint = P_FLOW_BITS;
} }
else if(command == "rs") else if(command == "rs")
{ {
mode = "riversource"; cur_mode.paint = P_RIVER_SOURCE;
} }
else if(command == "wclean") else if(command == "wclean")
{ {
mode = "wclean"; cur_mode.paint = P_WCLEAN;
} }
else if(command == "point" || command == "p") else if(command == "point" || command == "p")
{ {
brushname = "point"; cur_mode.brush = B_POINT;
} }
else if(command == "range" || command == "r") else if(command == "range" || command == "r")
{ {
int width, height, z_levels;
command_result res = parseRectangle(out, commands, 1, commands.size(), command_result res = parseRectangle(out, commands, 1, commands.size(),
width, height, z_levels); width, height, z_levels);
if (res != CR_OK) if (res != CR_OK)
@ -209,24 +248,26 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
if (width == 1 && height == 1 && z_levels == 1) if (width == 1 && height == 1 && z_levels == 1)
{ {
brushname = "point"; cur_mode.brush = B_POINT;
cur_mode.size = df::coord(1, 1, 1);
} }
else else
{ {
brushname = "range"; cur_mode.brush = B_RANGE;
cur_mode.size = df::coord(width, height, z_levels);
} }
} }
else if(command == "block") else if(command == "block")
{ {
brushname = "block"; cur_mode.brush = B_BLOCK;
} }
else if(command == "column") else if(command == "column")
{ {
brushname = "column"; cur_mode.brush = B_COLUMN;
} }
else if(command == "flood") else if(command == "flood")
{ {
brushname = "flood"; cur_mode.brush = B_FLOOD;
} }
else if(command == "q") else if(command == "q")
{ {
@ -234,45 +275,45 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
} }
else if(command == "f+") else if(command == "f+")
{ {
flowmode = "f+"; cur_mode.flowmode = M_INC;
} }
else if(command == "f-") else if(command == "f-")
{ {
flowmode = "f-"; cur_mode.flowmode = M_DEC;
} }
else if(command == "f.") else if(command == "f.")
{ {
flowmode = "f."; cur_mode.flowmode = M_KEEP;
} }
else if(command == "s+") else if(command == "s+")
{ {
_setmode = "s+"; cur_mode.setmode = M_INC;
} }
else if(command == "s-") else if(command == "s-")
{ {
_setmode = "s-"; cur_mode.setmode = M_DEC;
} }
else if(command == "s.") else if(command == "s.")
{ {
_setmode = "s."; cur_mode.setmode = M_KEEP;
} }
// blah blah, bad code, bite me. // blah blah, bad code, bite me.
else if(command == "0") else if(command == "0")
amount = 0; cur_mode.amount = 0;
else if(command == "1") else if(command == "1")
amount = 1; cur_mode.amount = 1;
else if(command == "2") else if(command == "2")
amount = 2; cur_mode.amount = 2;
else if(command == "3") else if(command == "3")
amount = 3; cur_mode.amount = 3;
else if(command == "4") else if(command == "4")
amount = 4; cur_mode.amount = 4;
else if(command == "5") else if(command == "5")
amount = 5; cur_mode.amount = 5;
else if(command == "6") else if(command == "6")
amount = 6; cur_mode.amount = 6;
else if(command == "7") else if(command == "7")
amount = 7; cur_mode.amount = 7;
else if(command.empty()) else if(command.empty())
{ {
df_liquids_execute(out); df_liquids_execute(out);
@ -298,78 +339,78 @@ command_result df_liquids_here (color_ostream &out, vector <string> & parameters
} }
out.print("Run liquids-here with these parameters: "); out.print("Run liquids-here with these parameters: ");
out << "[" << mode << ":" << brushname; out << "[" << paint_mode_name[cur_mode.paint] << ":" << brush_name[cur_mode.brush];
if (brushname == "range") if (cur_mode.brush == B_RANGE)
out << "(w" << width << ":h" << height << ":z" << z_levels << ")"; out << "(w" << cur_mode.size.x << ":h" << cur_mode.size.y << ":z" << cur_mode.size.z << ")";
out << ":" << amount << ":" << flowmode << ":" << _setmode << "]\n"; out << ":" << cur_mode.amount << ":f" << modify_mode_name[cur_mode.flowmode]
<< ":" << modify_mode_name[cur_mode.setmode] << "]\n";
return df_liquids_execute(out); return df_liquids_execute(out);
} }
command_result df_liquids_execute(color_ostream &out) command_result df_liquids_execute(color_ostream &out)
{ {
// create brush type depending on old parameters CoreSuspender suspend;
Brush * brush;
if (brushname == "point") auto cursor = Gui::getCursorPos();
if (!cursor.isValid())
{ {
brush = new RectangleBrush(1,1,1,0,0,0); out.printerr("Can't get cursor coords! Make sure you have a cursor active in DF.\n");
//width = 1; return CR_WRONG_USAGE;
//height = 1;
//z_levels = 1;
}
else if (brushname == "range")
{
brush = new RectangleBrush(width,height,z_levels,0,0,0);
} }
else if(brushname == "block")
auto rv = df_liquids_execute(out, cur_mode, cursor);
if (rv == CR_OK)
out << "OK" << endl;
return rv;
}
command_result df_liquids_execute(color_ostream &out, OperationMode &cur_mode, df::coord cursor)
{
// create brush type depending on old parameters
Brush *brush;
switch (cur_mode.brush)
{ {
case B_POINT:
brush = new RectangleBrush(1,1,1,0,0,0);
break;
case B_RANGE:
brush = new RectangleBrush(cur_mode.size.x,cur_mode.size.y,cur_mode.size.z,0,0,0);
break;
case B_BLOCK:
brush = new BlockBrush(); brush = new BlockBrush();
} break;
else if(brushname == "column") case B_COLUMN:
{
brush = new ColumnBrush(); brush = new ColumnBrush();
} break;
else if(brushname == "flood") case B_FLOOD:
{
brush = new FloodBrush(&Core::getInstance()); brush = new FloodBrush(&Core::getInstance());
} break;
else default:
{
// this should never happen! // this should never happen!
out << "Old brushtype is invalid! Resetting to point brush.\n"; out << "Old brushtype is invalid! Resetting to point brush.\n";
brushname = "point"; cur_mode.brush = B_POINT;
width = 1; brush = new RectangleBrush(1,1,1,0,0,0);
height = 1;
z_levels = 1;
brush = new RectangleBrush(width,height,z_levels,0,0,0);
} }
CoreSuspender suspend; std::auto_ptr<Brush> brush_ref(brush);
do
{
if (!Maps::IsValid()) if (!Maps::IsValid())
{ {
out << "Can't see any DF map loaded." << endl; out << "Can't see any DF map loaded." << endl;
break;; return CR_FAILURE;
}
int32_t x,y,z;
if(!Gui::getCursorCoords(x,y,z))
{
out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl;
break;
} }
out << "cursor coords: " << x << "/" << y << "/" << z << endl;
MapCache mcache; MapCache mcache;
DFHack::DFCoord cursor(x,y,z);
coord_vec all_tiles = brush->points(mcache,cursor); coord_vec all_tiles = brush->points(mcache,cursor);
out << "working..." << endl;
// Force the game to recompute its walkability cache // Force the game to recompute its walkability cache
df::global::world->reindex_pathfinding = true; df::global::world->reindex_pathfinding = true;
if(mode == "obsidian") switch (cur_mode.paint)
{
case P_OBSIDIAN:
{ {
coord_vec::iterator iter = all_tiles.begin(); coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end()) while (iter != all_tiles.end())
@ -383,8 +424,9 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setDesignationAt(*iter, des); mcache.setDesignationAt(*iter, des);
iter ++; iter ++;
} }
break;
} }
if(mode == "obsidian_floor") case P_OBSIDIAN_FLOOR:
{ {
coord_vec::iterator iter = all_tiles.begin(); coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end()) while (iter != all_tiles.end())
@ -392,8 +434,9 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1)); mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1));
iter ++; iter ++;
} }
break;
} }
else if(mode == "riversource") case P_RIVER_SOURCE:
{ {
coord_vec::iterator iter = all_tiles.begin(); coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end()) while (iter != all_tiles.end())
@ -413,8 +456,9 @@ command_result df_liquids_execute(color_ostream &out)
iter++; iter++;
} }
break;
} }
else if(mode=="wclean") case P_WCLEAN:
{ {
coord_vec::iterator iter = all_tiles.begin(); coord_vec::iterator iter = all_tiles.begin();
while (iter != all_tiles.end()) while (iter != all_tiles.end())
@ -426,8 +470,11 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setDesignationAt(current,des); mcache.setDesignationAt(current,des);
iter++; iter++;
} }
break;
} }
else if(mode== "magma" || mode== "water" || mode == "flowbits") case P_MAGMA:
case P_WATER:
case P_FLOW_BITS:
{ {
set <Block *> seen_blocks; set <Block *> seen_blocks;
coord_vec::iterator iter = all_tiles.begin(); coord_vec::iterator iter = all_tiles.begin();
@ -450,30 +497,29 @@ command_result df_liquids_execute(color_ostream &out)
iter++; iter++;
continue; continue;
} }
if(mode != "flowbits") if(cur_mode.paint != P_FLOW_BITS)
{ {
unsigned old_amount = des.bits.flow_size; unsigned old_amount = des.bits.flow_size;
unsigned new_amount = old_amount; unsigned new_amount = old_amount;
df::tile_liquid old_liquid = des.bits.liquid_type; df::tile_liquid old_liquid = des.bits.liquid_type;
df::tile_liquid new_liquid = old_liquid; df::tile_liquid new_liquid = old_liquid;
// Compute new liquid type and amount // Compute new liquid type and amount
if(_setmode == "s.") switch (cur_mode.setmode)
{ {
new_amount = amount; case M_KEEP:
} new_amount = cur_mode.amount;
else if(_setmode == "s+") break;
{ case M_INC:
if(old_amount < amount) if(old_amount < cur_mode.amount)
new_amount = amount; new_amount = cur_mode.amount;
} break;
else if(_setmode == "s-") case M_DEC:
{ if (old_amount > cur_mode.amount)
if (old_amount > amount) new_amount = cur_mode.amount;
new_amount = amount;
} }
if (mode == "magma") if (cur_mode.paint == P_MAGMA)
new_liquid = tile_liquid::Magma; new_liquid = tile_liquid::Magma;
else if (mode == "water") else if (cur_mode.paint == P_WATER)
new_liquid = tile_liquid::Water; new_liquid = tile_liquid::Water;
// Store new amount and type // Store new amount and type
des.bits.flow_size = new_amount; des.bits.flow_size = new_amount;
@ -508,34 +554,64 @@ command_result df_liquids_execute(color_ostream &out)
set <Block *>::iterator biter = seen_blocks.begin(); set <Block *>::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end()) while (biter != seen_blocks.end())
{ {
if(flowmode == "f+") switch (cur_mode.flowmode)
{ {
case M_INC:
(*biter)->enableBlockUpdates(true); (*biter)->enableBlockUpdates(true);
} break;
else if(flowmode == "f-") case M_DEC:
{
if (auto block = (*biter)->getRaw()) if (auto block = (*biter)->getRaw())
{ {
block->flags.bits.update_liquid = false; block->flags.bits.update_liquid = false;
block->flags.bits.update_liquid_twice = false; block->flags.bits.update_liquid_twice = false;
} }
} break;
else case M_KEEP:
{ {
auto bflags = (*biter)->BlockFlags(); auto bflags = (*biter)->BlockFlags();
out << "flow bit 1 = " << bflags.bits.update_liquid << endl; out << "flow bit 1 = " << bflags.bits.update_liquid << endl;
out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl; out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl;
} }
}
biter ++; biter ++;
} }
break;
} }
if(mcache.WriteAll()) }
out << "OK" << endl;
else if(!mcache.WriteAll())
{
out << "Something failed horribly! RUN!" << endl; out << "Something failed horribly! RUN!" << endl;
} while (0); return CR_FAILURE;
}
// cleanup
delete brush;
return CR_OK; return CR_OK;
} }
static int paint(lua_State *L)
{
df::coord pos;
OperationMode mode;
lua_settop(L, 7);
Lua::CheckDFAssign(L, &pos, 1);
if (!pos.isValid())
luaL_argerror(L, 1, "invalid cursor position");
mode.brush = (BrushType)luaL_checkoption(L, 2, NULL, brush_name);
mode.paint = (PaintMode)luaL_checkoption(L, 3, NULL, paint_mode_name);
mode.amount = luaL_optint(L, 4, 7);
if (mode.amount < 0 || mode.amount > 7)
luaL_argerror(L, 4, "invalid liquid amount");
if (!lua_isnil(L, 5))
Lua::CheckDFAssign(L, &mode.size, 5);
mode.setmode = (ModifyMode)luaL_checkoption(L, 6, ".", modify_mode_name);
mode.flowmode = (ModifyMode)luaL_checkoption(L, 7, "+", modify_mode_name);
lua_pushboolean(L, df_liquids_execute(*Lua::GetOutput(L), mode, pos));
return 1;
}
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(paint),
DFHACK_LUA_END
};

@ -0,0 +1,11 @@
local _ENV = mkmodule('plugins.liquids')
--[[
Native functions:
* paint(pos,brush,paint,amount,size,setmode,flowmode)
--]]
return _ENV

@ -0,0 +1,201 @@
-- Interface front-end for liquids plugin.
local utils = require 'utils'
local gui = require 'gui'
local guidm = require 'gui.dwarfmode'
local liquids = require('plugins.liquids')
local sel_rect = df.global.selection_rect
local brushes = {
{ tag = 'range', caption = 'Rectangle', range = true },
{ tag = 'block', caption = '16x16 block' },
{ tag = 'column', caption = 'Column' },
{ tag = 'flood', caption = 'Flood' },
}
local paints = {
{ tag = 'water', caption = 'Water', liquid = true, key = 'w' },
{ tag = 'magma', caption = 'Magma', liquid = true, key = 'l' },
{ tag = 'obsidian', caption = 'Obsidian Wall' },
{ tag = 'obsidian_floor', caption = 'Obsidian Floor' },
{ tag = 'riversource', caption = 'River Source' },
{ tag = 'flowbits', caption = 'Flow Updates' },
{ tag = 'wclean', caption = 'Clean Salt/Stagnant' },
}
local flowbits = {
{ tag = '+', caption = 'Enable Updates' },
{ tag = '-', caption = 'Disable Updates' },
{ tag = '.', caption = 'Keep Updates' },
}
local setmode = {
{ tag = '.', caption = 'Set Exactly' },
{ tag = '+', caption = 'Only Increase' },
{ tag = '-', caption = 'Only Decrease' },
}
Toggle = defclass(Toggle)
function Toggle:init(items)
self:init_fields{
items = items,
selected = 1
}
return self
end
function Toggle:get()
return self.items[self.selected]
end
function Toggle:render(dc)
local item = self:get()
if item then
dc:string(item.caption)
if item.key then
dc:string(" ("):string(item.key, COLOR_LIGHTGREEN):string(")")
end
else
dc:string('NONE', COLOR_RED)
end
end
function Toggle:step(delta)
if #self.items > 1 then
delta = delta or 1
self.selected = 1 + (self.selected + delta - 1) % #self.items
end
end
LiquidsUI = defclass(LiquidsUI, guidm.MenuOverlay)
LiquidsUI.focus_path = 'liquids'
function LiquidsUI:init()
self:init_fields{
brush = mkinstance(Toggle):init(brushes),
paint = mkinstance(Toggle):init(paints),
flow = mkinstance(Toggle):init(flowbits),
set = mkinstance(Toggle):init(setmode),
amount = 7,
}
guidm.MenuOverlay.init(self)
return self
end
function LiquidsUI:onDestroy()
guidm.clearSelection()
end
function LiquidsUI:onRenderBody(dc)
dc:clear():seek(1,1):string("Paint Liquids Cheat", COLOR_WHITE)
local cursor = guidm.getCursorPos()
local block = dfhack.maps.getTileBlock(cursor)
local tile = block.tiletype[cursor.x%16][cursor.y%16]
local dsgn = block.designation[cursor.x%16][cursor.y%16]
dc:seek(2,3):string(df.tiletype.attrs[tile].caption, COLOR_CYAN):newline(2)
if dsgn.flow_size > 0 then
if dsgn.liquid_type == df.tile_liquid.Magma then
dc:pen(COLOR_RED):string("Magma")
else
dc:pen(COLOR_BLUE)
if dsgn.water_stagnant then dc:string("Stagnant ") end
if dsgn.water_salt then dc:string("Salty ") end
dc:string("Water")
end
dc:string(" ["..dsgn.flow_size.."/7]")
else
dc:string('No Liquid', COLOR_DARKGREY)
end
dc:newline():pen(COLOR_GREY)
dc:newline(1):string("b", COLOR_LIGHTGREEN):string(": ")
self.brush:render(dc)
dc:newline(1):string("p", COLOR_LIGHTGREEN):string(": ")
self.paint:render(dc)
local liquid = self.paint:get().liquid
dc:newline()
if liquid then
dc:newline(1):string("Amount: "..self.amount)
dc:advance(1):string("("):string("-+", COLOR_LIGHTGREEN):string(")")
dc:newline(3):string("s", COLOR_LIGHTGREEN):string(": ")
self.set:render(dc)
else
dc:advance(0,2)
end
dc:newline():newline(1):string("f", COLOR_LIGHTGREEN):string(": ")
self.flow:render(dc)
dc:newline():newline(1):pen(COLOR_WHITE)
dc:string("Esc", COLOR_LIGHTGREEN):string(": Back, ")
dc:string("Enter", COLOR_LIGHTGREEN):string(": Paint")
end
function LiquidsUI:onInput(keys)
local liquid = self.paint:get().liquid
if keys.CUSTOM_B then
self.brush:step()
elseif keys.CUSTOM_P then
self.paint:step()
elseif liquid and keys.SECONDSCROLL_UP then
self.amount = math.max(0, self.amount-1)
elseif liquid and keys.SECONDSCROLL_DOWN then
self.amount = math.min(7, self.amount+1)
elseif liquid and keys.CUSTOM_S then
self.set:step()
elseif keys.CUSTOM_F then
self.flow:step()
elseif keys.LEAVESCREEN then
if guidm.getSelection() then
guidm.clearSelection()
return
end
self:dismiss()
self:sendInputToParent('CURSOR_DOWN_Z')
self:sendInputToParent('CURSOR_UP_Z')
elseif keys.SELECT then
local cursor = guidm.getCursorPos()
local sp = guidm.getSelection()
local size = nil
if self.brush:get().range then
if not sp then
guidm.setSelectionStart(cursor)
return
else
guidm.clearSelection()
cursor, size = guidm.getSelectionRange(cursor, sp)
end
else
guidm.clearSelection()
end
liquids.paint(
cursor,
self.brush:get().tag, self.paint:get().tag,
self.amount, size,
self.set:get().tag, self.flow:get().tag
)
elseif self:propagateMoveKeys(keys) then
return
elseif keys.D_LOOK_ARENA_WATER then
self.paint.selected = 1
elseif keys.D_LOOK_ARENA_MAGMA then
self.paint.selected = 2
end
end
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/LookAround') then
qerror("This script requires the main dwarfmode view in 'k' mode")
end
local list = mkinstance(LiquidsUI):init()
list:show()