Merge pull request #2330 from myk002/myk_blueprint_smooth

[blueprint] add option to capture smoothed tiles
develop
Myk 2022-10-12 10:19:21 -07:00 committed by GitHub
commit aa4b1ef29e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 59 deletions

@ -103,6 +103,10 @@ Options
to surround the parameter string in double quotes: to surround the parameter string in double quotes:
``"-s10,10,central stairs"`` or ``--playback-start "10,10,central stairs"`` ``"-s10,10,central stairs"`` or ``--playback-start "10,10,central stairs"``
or ``"--playback-start=10,10,central stairs"``. or ``"--playback-start=10,10,central stairs"``.
``--smooth``
Record all smooth tiles in the ``smooth`` phase. If this parameter is not
specified, only tiles that will later be carved into fortifications or
engraved will be smoothed.
``-t``, ``--splitby <strategy>`` ``-t``, ``--splitby <strategy>``
Split blueprints into multiple files. See the `Splitting output into Split blueprints into multiple files. See the `Splitting output into
multiple files`_ section below for details. If not specified, defaults to multiple files`_ section below for details. If not specified, defaults to

@ -76,6 +76,8 @@ struct blueprint_options {
// base name to use for generated files // base name to use for generated files
string name; string name;
// whether to capture all smoothed tiles
bool smooth = false;
// whether to capture engravings and smooth the tiles that will be engraved // whether to capture engravings and smooth the tiles that will be engraved
bool engrave = false; bool engrave = false;
@ -103,6 +105,7 @@ static const struct_field_info blueprint_options_fields[] = {
{ struct_field_info::PRIMITIVE, "height", offsetof(blueprint_options, height), &df::identity_traits<int32_t>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "height", offsetof(blueprint_options, height), &df::identity_traits<int32_t>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "depth", offsetof(blueprint_options, depth), &df::identity_traits<int32_t>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "depth", offsetof(blueprint_options, depth), &df::identity_traits<int32_t>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "name", offsetof(blueprint_options, name), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::PRIMITIVE, "name", offsetof(blueprint_options, name), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "smooth", offsetof(blueprint_options, smooth), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "engrave", offsetof(blueprint_options, engrave), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "engrave", offsetof(blueprint_options, engrave), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "auto_phase", offsetof(blueprint_options, auto_phase), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "auto_phase", offsetof(blueprint_options, auto_phase), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 },
@ -219,7 +222,7 @@ static const char * get_tile_smooth_minimal(const df::coord &pos,
return NULL; return NULL;
} }
static const char * get_tile_smooth(const df::coord &pos, static const char * get_tile_smooth_with_engravings(const df::coord &pos,
const tile_context &tc) { const tile_context &tc) {
const char * smooth_minimal = get_tile_smooth_minimal(pos, tc); const char * smooth_minimal = get_tile_smooth_minimal(pos, tc);
if (smooth_minimal) if (smooth_minimal)
@ -244,6 +247,30 @@ static const char * get_tile_smooth(const df::coord &pos,
return NULL; return NULL;
} }
static const char * get_tile_smooth_all(const df::coord &pos,
const tile_context &tc) {
const char * smooth_minimal = get_tile_smooth_minimal(pos, tc);
if (smooth_minimal)
return smooth_minimal;
df::tiletype *tt = Maps::getTileType(pos);
if (!tt)
return NULL;
switch (tileShape(*tt))
{
case tiletype_shape::FLOOR:
case tiletype_shape::WALL:
if (tileSpecial(*tt) == tiletype_special::SMOOTH)
return "s";
break;
default:
break;
}
return NULL;
}
static const char * get_track_str(const char *prefix, df::tiletype tt) { static const char * get_track_str(const char *prefix, df::tiletype tt) {
TileDirection tdir = tileDirection(tt); TileDirection tdir = tileDirection(tt);
@ -1096,9 +1123,13 @@ static bool do_transform(color_ostream &out,
vector<blueprint_processor> processors; vector<blueprint_processor> processors;
get_tile_fn* smooth_get_tile_fn = get_tile_smooth_minimal;
if (opts.engrave) smooth_get_tile_fn = get_tile_smooth_with_engravings;
if (opts.smooth) smooth_get_tile_fn = get_tile_smooth_all;
add_processor(processors, opts, "dig", "dig", opts.dig, get_tile_dig); add_processor(processors, opts, "dig", "dig", opts.dig, get_tile_dig);
add_processor(processors, opts, "dig", "smooth", opts.carve, add_processor(processors, opts, "dig", "smooth", opts.carve,
opts.engrave ? get_tile_smooth : get_tile_smooth_minimal); smooth_get_tile_fn);
add_processor(processors, opts, "dig", "carve", opts.carve, add_processor(processors, opts, "dig", "carve", opts.carve,
opts.engrave ? get_tile_carve : get_tile_carve_minimal); opts.engrave ? get_tile_carve : get_tile_carve_minimal);
add_processor(processors, opts, "build", "build", opts.build, add_processor(processors, opts, "build", "build", opts.build,
@ -1187,21 +1218,9 @@ static bool get_options(color_ostream &out,
return true; return true;
} }
static void print_help(color_ostream &out) {
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
if (!lua_checkstack(L, 1) ||
!Lua::PushModulePublic(out, L, "plugins.blueprint", "print_help") ||
!Lua::SafeCall(out, L, 0, 0))
{
out.printerr("Failed to load blueprint Lua code\n");
}
}
// returns whether blueprint generation was successful. populates files with the // returns whether blueprint generation was successful. populates files with the
// names of the files that were generated // names of the files that were generated
static bool do_blueprint(color_ostream &out, static command_result do_blueprint(color_ostream &out,
const vector<string> &parameters, const vector<string> &parameters,
vector<string> &files) { vector<string> &files) {
CoreSuspender suspend; CoreSuspender suspend;
@ -1221,13 +1240,12 @@ static bool do_blueprint(color_ostream &out,
blueprint_options options; blueprint_options options;
if (!get_options(out, options, parameters) || options.help) { if (!get_options(out, options, parameters) || options.help) {
print_help(out); return CR_WRONG_USAGE;
return options.help;
} }
if (!Maps::IsValid()) { if (!Maps::IsValid()) {
out.printerr("Map is not available!\n"); out.printerr("Map is not available!\n");
return false; return CR_FAILURE;
} }
// start coordinates can come from either the commandline or the map cursor // start coordinates can come from either the commandline or the map cursor
@ -1236,13 +1254,13 @@ static bool do_blueprint(color_ostream &out,
if (!Gui::getCursorCoords(start)) { if (!Gui::getCursorCoords(start)) {
out.printerr("Can't get cursor coords! Make sure you specify the" out.printerr("Can't get cursor coords! Make sure you specify the"
" --cursor parameter or have an active cursor in DF.\n"); " --cursor parameter or have an active cursor in DF.\n");
return false; return CR_FAILURE;
} }
} }
if (!Maps::isValidTilePos(start)) { if (!Maps::isValidTilePos(start)) {
out.printerr("Invalid start position: %d,%d,%d\n", out.printerr("Invalid start position: %d,%d,%d\n",
start.x, start.y, start.z); start.x, start.y, start.z);
return false; return CR_FAILURE;
} }
// end coords are one beyond the last processed coordinate. note that // end coords are one beyond the last processed coordinate. note that
@ -1265,7 +1283,7 @@ static bool do_blueprint(color_ostream &out,
bool ok = do_transform(out, start, end, options, files); bool ok = do_transform(out, start, end, options, files);
cache(NULL); cache(NULL);
return ok; return ok ? CR_OK : CR_FAILURE;
} }
// entrypoint when called from Lua. returns the names of the generated files // entrypoint when called from Lua. returns the names of the generated files
@ -1284,7 +1302,7 @@ static int run(lua_State *L) {
color_ostream *out = Lua::GetOutput(L); color_ostream *out = Lua::GetOutput(L);
if (!out) if (!out)
out = &Core::getInstance().getConsole(); out = &Core::getInstance().getConsole();
if (do_blueprint(*out, argv, files)) { if (CR_OK == do_blueprint(*out, argv, files)) {
Lua::PushVector(L, files); Lua::PushVector(L, files);
return 1; return 1;
} }
@ -1294,13 +1312,13 @@ static int run(lua_State *L) {
command_result blueprint(color_ostream &out, vector<string> &parameters) { command_result blueprint(color_ostream &out, vector<string> &parameters) {
vector<string> files; vector<string> files;
if (do_blueprint(out, parameters, files)) { command_result cr = do_blueprint(out, parameters, files);
if (cr == CR_OK) {
out.print("Generated blueprint file(s):\n"); out.print("Generated blueprint file(s):\n");
for (string &fname : files) for (string &fname : files)
out.print(" %s\n", fname.c_str()); out.print(" %s\n", fname.c_str());
return CR_OK;
} }
return CR_FAILURE; return cr;
} }
DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_PLUGIN_LUA_COMMANDS {

@ -3,37 +3,6 @@ local _ENV = mkmodule('plugins.blueprint')
local argparse = require('argparse') local argparse = require('argparse')
local utils = require('utils') local utils = require('utils')
-- the info here is very basic and minimal, so hopefully we won't need to change
-- it when features are added and the full blueprint docs in Plugins.rst are
-- updated.
local help_text = [=[
blueprint
=========
Records the structure of a portion of your fortress in quickfort blueprints.
Usage:
blueprint <width> <height> [<depth>] [<name> [<phases>]] [<options>]
blueprint gui [<name> [<phases>]] [<options>]
Examples:
blueprint gui
Runs gui/blueprint, the interactive blueprint frontend, where all
configuration can be set visually and interactively.
blueprint 30 40 bedrooms
Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting
from the active cursor on the current z-level. Output files are written to
the "blueprints" directory.
See the online DFHack documentation for more examples and details.
]=]
function print_help() print(help_text) end
local valid_phase_list = { local valid_phase_list = {
'dig', 'dig',
'carve', 'carve',
@ -154,6 +123,7 @@ local function process_args(opts, args)
{'h', 'help', handler=function() opts.help = true end}, {'h', 'help', handler=function() opts.help = true end},
{'s', 'playback-start', hasArg=true, {'s', 'playback-start', hasArg=true,
handler=function(optarg) parse_start(opts, optarg) end}, handler=function(optarg) parse_start(opts, optarg) end},
{nil, 'smooth', handler=function() opts.smooth = true end},
{'t', 'splitby', hasArg=true, {'t', 'splitby', hasArg=true,
handler=function(optarg) parse_split_strategy(opts, optarg) end}, handler=function(optarg) parse_split_strategy(opts, optarg) end},
}) })

@ -35,6 +35,12 @@ function test.parse_gui_commandline()
name='blueprint', engrave=true,}, name='blueprint', engrave=true,},
opts) opts)
opts = {}
b.parse_gui_commandline(opts, {'--smooth'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',
name='blueprint', smooth=true,},
opts)
opts = {} opts = {}
b.parse_gui_commandline(opts, {'--engrave'}) b.parse_gui_commandline(opts, {'--engrave'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none', expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',