implement --playback-start param and logic

develop
myk002 2021-09-17 11:07:42 -07:00 committed by Myk
parent 53c1582080
commit 596f72f06b
4 changed files with 102 additions and 38 deletions

@ -46,12 +46,13 @@ selected interactively with the ``blueprint gui`` command or, if the GUI is not
used, starts at the active cursor location and extends right and down for the used, starts at the active cursor location and extends right and down for the
requested width and height. requested width and height.
Usage:: **Usage:**
blueprint <width> <height> [<depth>] [<name> [<phases>]] [<options>] ``blueprint <width> <height> [<depth>] [<name> [<phases>]] [<options>]``
blueprint gui [<name> [<phases>]] [<options>]
Examples: ``blueprint gui [<name> [<phases>]] [<options>]``
**Examples:**
``blueprint gui`` ``blueprint gui``
Runs `gui/blueprint`, the interactive frontend, where all configuration for Runs `gui/blueprint`, the interactive frontend, where all configuration for
@ -68,7 +69,7 @@ Examples:
the blueprint start coordinate is set to a specific value instead of using the blueprint start coordinate is set to a specific value instead of using
the in-game cursor position. the in-game cursor position.
Positional Parameters: **Positional Parameters:**
:``width``: Width of the area (in tiles) to translate. :``width``: Width of the area (in tiles) to translate.
:``height``: Height of the area (in tiles) to translate. :``height``: Height of the area (in tiles) to translate.
@ -80,7 +81,7 @@ Positional Parameters:
string must contain some characters other than numbers so the name won't be string must contain some characters other than numbers so the name won't be
confused with the optional ``depth`` parameter. confused with the optional ``depth`` parameter.
Phases: **Phases:**
If you want to generate blueprints only for specific phases, add their names to If you want to generate blueprints only for specific phases, add their names to
the commandline, anywhere after the blueprint base name. You can list multiple the commandline, anywhere after the blueprint base name. You can list multiple
@ -94,36 +95,45 @@ phases; just separate them with a space.
If no phases are specified, all blueprints are created. If no phases are specified, all blueprints are created.
Options: **Options:**
:``-c``, ``--cursor <x>,<y>,<z>``: ``-c``, ``--cursor <x>,<y>,<z>``:
Use the specified map coordinates instead of the current cursor position for Use the specified map coordinates instead of the current cursor position for
the upper left corner of the blueprint range. If this option is specified, the upper left corner of the blueprint range. If this option is specified,
then an active game map cursor is not necessary. then an active game map cursor is not necessary.
:``-f``, ``--format <format>``: ``-f``, ``--format <format>``:
Select the output format of the generated files. See the ``Output Formats`` Select the output format of the generated files. See the ``Output formats``
section below for options. If not specified, the output format defaults to section below for options. If not specified, the output format defaults to
"minimal", which will produce a small, fast ``.csv`` file. "minimal", which will produce a small, fast ``.csv`` file.
:``-h``, ``--help``: ``-h``, ``--help``:
Show command help text. Show command help text.
:``-t``, ``--splitby <strategy>``: ``-s``, ``--playback-start <x>,<y>,<comment>``:
Specify the column and row offsets (relative to the upper-left corner of the
blueprint, which is ``1,1``) where the player should put the cursor when the
blueprint is played back with `quickfort`, in
`quickfort start marker <quickfort-start>` format, for example:
``10,10,central stairs``. If there is a space in the comment, you will need
to surround the parameter string in double quotes: ``"-s10,10,central stairs"`` or
``--playback-start "10,10,central stairs"`` or
``"--playback-start=10,10,central stairs"``.
``-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
"none", which will create a standard quickfort "none", which will create a standard quickfort
`multi-blueprint <quickfort-packaging>` file. `multi-blueprint <quickfort-packaging>` file.
Output formats: **Output formats:**
Here are the values that can be passed to the ``--format`` flag: Here are the values that can be passed to the ``--format`` flag:
:minimal: :``minimal``:
Creates ``.csv`` files with minimal file size that are fast to read and Creates ``.csv`` files with minimal file size that are fast to read and
write. This is the default. write. This is the default.
:pretty: :``pretty``:
Makes the blueprints in the ``.csv`` files easier to read and edit with a text Makes the blueprints in the ``.csv`` files easier to read and edit with a text
editor by adding extra spacing and alignment markers. editor by adding extra spacing and alignment markers.
Splitting output into multiple files: **Splitting output into multiple files:**
The ``--splitby`` flag can take any of the following values: The ``--splitby`` flag can take any of the following values:

@ -757,6 +757,8 @@ be "2" if it is not otherwise set, etc. Labels that are explicitly defined must
start with a letter to ensure the auto-generated labels don't conflict with start with a letter to ensure the auto-generated labels don't conflict with
user-defined labels. user-defined labels.
.. _quickfort-start:
Start positions Start positions
``````````````` ```````````````
@ -787,6 +789,9 @@ to the ``masonw`` blueprint above could look like this::
#meta start(center of workshop) a mason workshop #meta start(center of workshop) a mason workshop
/masonw /masonw
You can use semicolons, commas, or spaces to separate the elements of the
``start()`` marker, whatever is most convenient.
.. _quickfort-hidden: .. _quickfort-hidden:
Hiding blueprints Hiding blueprints

@ -55,6 +55,11 @@ struct blueprint_options {
// for it. // for it.
string format; string format;
// offset and comment to write in the quickfort start() modeline marker
// if not set, coordinates are set to 0 and the comment will be empty
df::coord2d playback_start = df::coord2d(0, 0);
string playback_start_comment;
// file splitting strategy. this could be an enum if we set up the // file splitting strategy. this could be an enum if we set up the
// boilerplate for it. // boilerplate for it.
string split_strategy; string split_strategy;
@ -80,19 +85,21 @@ struct blueprint_options {
static struct_identity _identity; static struct_identity _identity;
}; };
static const struct_field_info blueprint_options_fields[] = { static const struct_field_info blueprint_options_fields[] = {
{ struct_field_info::PRIMITIVE, "help", offsetof(blueprint_options, help), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "help", offsetof(blueprint_options, help), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::SUBSTRUCT, "start", offsetof(blueprint_options, start), &df::coord::_identity, 0, 0 }, { struct_field_info::SUBSTRUCT, "start", offsetof(blueprint_options, start), &df::coord::_identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "format", offsetof(blueprint_options, format), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::PRIMITIVE, "format", offsetof(blueprint_options, format), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "split_strategy", offsetof(blueprint_options, split_strategy), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::SUBSTRUCT, "playback_start", offsetof(blueprint_options, playback_start), &df::coord2d::_identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "width", offsetof(blueprint_options, width), &df::identity_traits<int32_t>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "playback_start_comment", offsetof(blueprint_options, playback_start_comment), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "height", offsetof(blueprint_options, height), &df::identity_traits<int32_t>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "split_strategy", offsetof(blueprint_options, split_strategy), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "depth", offsetof(blueprint_options, depth), &df::identity_traits<int32_t>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "width", offsetof(blueprint_options, width), &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, "height", offsetof(blueprint_options, height), &df::identity_traits<int32_t>::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, "depth", offsetof(blueprint_options, depth), &df::identity_traits<int32_t>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "name", offsetof(blueprint_options, name), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "build", offsetof(blueprint_options, build), &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, "place", offsetof(blueprint_options, place), &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, "query", offsetof(blueprint_options, query), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "build", offsetof(blueprint_options, build), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "place", offsetof(blueprint_options, place), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::END } { struct_field_info::END }
}; };
struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn<blueprint_options>, NULL, "blueprint_options", NULL, blueprint_options_fields); struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn<blueprint_options>, NULL, "blueprint_options", NULL, blueprint_options_fields);
@ -716,9 +723,17 @@ static void write_pretty(ofstream &ofile, const blueprint_options &opts,
} }
} }
static string get_modeline(const string &phase) { static string get_modeline(const blueprint_options &opts, const string &phase) {
std::ostringstream modeline; std::ostringstream modeline;
modeline << "#" << phase << " label(" << phase << ")"; modeline << "#" << phase << " label(" << phase << ")";
if (opts.playback_start.x > 0) {
modeline << " start(" << opts.playback_start.x
<< ";" << opts.playback_start.y;
if (!opts.playback_start_comment.empty()) {
modeline << ";" << opts.playback_start_comment;
}
modeline << ")";
}
return modeline.str(); return modeline.str();
} }
@ -735,7 +750,7 @@ static bool write_blueprint(color_ostream &out,
output_files[fname] = new ofstream(fname, ofstream::trunc); output_files[fname] = new ofstream(fname, ofstream::trunc);
ofstream &ofile = *output_files[fname]; ofstream &ofile = *output_files[fname];
ofile << get_modeline(processor.phase) << endl; ofile << get_modeline(opts, processor.phase) << endl;
if (pretty) if (pretty)
write_pretty(ofile, opts, processor.mapdata); write_pretty(ofile, opts, processor.mapdata);

@ -70,14 +70,36 @@ local function parse_enum(opts, valid, name, val)
opts[name] = val opts[name] = val
end end
local function parse_split_strategy(opts, strategy)
parse_enum(opts, valid_split_strategies, 'split_strategy', strategy)
end
local function parse_format(opts, file_format) local function parse_format(opts, file_format)
parse_enum(opts, valid_formats, 'format', file_format) parse_enum(opts, valid_formats, 'format', file_format)
end end
local function is_int(val)
return val and val == math.floor(val)
end
local function is_positive_int(val)
return is_int(val) and val > 0
end
local function parse_start(opts, args)
local arg_list = argparse.stringList(args)
local x_str, y_str = table.remove(arg_list, 1), table.remove(arg_list, 1)
local x, y = tonumber(x_str), tonumber(y_str)
if not is_positive_int(x) or not is_positive_int(y) then
qerror(('playback start offsets must be positive integers: "%s", "%s"')
:format(x_str, y_str))
end
if not opts.playback_start then opts.playback_start = {} end
opts.playback_start.x, opts.start.y = x, y
opts.playback_start_comment = table.concat(arg_list, ', ')
end
local function parse_split_strategy(opts, strategy)
parse_enum(opts, valid_split_strategies, 'split_strategy', strategy)
end
local function parse_positionals(opts, args, start_argidx) local function parse_positionals(opts, args, start_argidx)
local argidx = start_argidx or 1 local argidx = start_argidx or 1
@ -125,6 +147,8 @@ local function process_args(opts, args)
{'f', 'format', hasArg=true, {'f', 'format', hasArg=true,
handler=function(optarg) parse_format(opts, optarg) end}, handler=function(optarg) parse_format(opts, optarg) end},
{'h', 'help', handler=function() opts.help = true end}, {'h', 'help', handler=function() opts.help = true end},
{'s', 'playback-start', hasArg=true,
handler=function(optarg) parse_start(opts, optarg) 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},
}) })
@ -146,9 +170,8 @@ end
-- dimension must be a non-nil integer that is >= 1 (or at least non-zero if -- dimension must be a non-nil integer that is >= 1 (or at least non-zero if
-- negative_ok is true) -- negative_ok is true)
local function is_bad_dim(dim, negative_ok) local function is_bad_dim(dim, negative_ok)
return not dim or return not is_int(dim) or
(not negative_ok and dim < 1 or dim == 0) or (not negative_ok and dim < 1 or dim == 0)
dim ~= math.floor(dim)
end end
function parse_commandline(opts, ...) function parse_commandline(opts, ...)
@ -171,6 +194,17 @@ function parse_commandline(opts, ...)
opts.depth = depth opts.depth = depth
end end
if opts.playback_start and opts.playback_start.x > 0 then
if opts.playback_start.x > width then
qerror(('playback start x offset outside width of blueprint: %d')
:format(opts.playback_start.x))
end
if opts.playback_start.y > height then
qerror(('playback start y offset outside height of blueprint: %d')
:format(opts.playback_start.y))
end
end
parse_positionals(opts, positionals, depth and 4 or 3) parse_positionals(opts, positionals, depth and 4 or 3)
end end