capture fortifications and engravings in blueprint

develop
myk002 2022-04-01 11:36:19 -07:00 committed by Myk
parent fbd1fac210
commit 54e8761aa0
12 changed files with 155 additions and 30 deletions

@ -1,4 +1,4 @@
#dig label(track)
#dig label(carve)
trackSE,trackEW,trackEW,trackEW,trackSW
trackNE,,,,trackNW
#>
1 #dig label(track) #dig label(carve)
2 trackSE,trackEW,trackEW,trackEW,trackSW trackSE,trackEW,trackEW,trackEW,trackSW
3 trackNE,,,,trackNW trackNE,,,,trackNW
4 #> #>

@ -1,4 +1,4 @@
#dig label(track)
#dig label(carve)
,,trackS
,,trackNS
trackE,trackEW,trackNSEW,trackEW,trackW
1 #dig label(track) #dig label(carve)
2 ,,trackS ,,trackS
3 ,,trackNS ,,trackNS
4 trackE,trackEW,trackNSEW,trackEW,trackW trackE,trackEW,trackNSEW,trackEW,trackW

@ -0,0 +1,4 @@
#dig label(carve)
,F
F,,F
,F
1 #dig label(carve)
2 ,F
3 F,,F
4 ,F

@ -0,0 +1,3 @@
#dig label(dig)
,d
1 #dig label(dig)
2 ,d

@ -0,0 +1,4 @@
#dig label(smooth)
,s
s,,s
,s
1 #dig label(smooth)
2 ,s
3 s,,s
4 ,s

@ -0,0 +1,4 @@
#notes
description=test carving fortifications
width=3
height=3
1 #notes
2 description=test carving fortifications
3 width=3
4 height=3

@ -1,4 +1,4 @@
#dig label(track)
#dig label(carve)
T(5x2)
,,,,T(-5x-2)
#>
1 #dig label(track) #dig label(carve)
2 T(5x2) T(5x2)
3 ,,,,T(-5x-2) ,,,,T(-5x-2)
4 #> #>

@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `manipulator`: Tweak colors to make the cursor easier to locate
- `autochop`: only designate the amount of trees required to reach ``max_logs``
- `autochop`: preferably designate larger trees over smaller ones
- `blueprint`: ``track`` phase renamed to ``carve``. carved fortifications and (optionally) engravings are now captured in blueprints
## Documentation
- Add more examples to the plugin skeleton files so they are more informative for a newbie

@ -7,6 +7,7 @@
#include <algorithm>
#include <sstream>
#include <unordered_map>
#include "Console.h"
#include "DataDefs.h"
@ -31,6 +32,7 @@
#include "df/building_trapst.h"
#include "df/building_water_wheelst.h"
#include "df/building_workshopst.h"
#include "df/engraving.h"
#include "df/world.h"
using std::endl;
@ -74,12 +76,15 @@ struct blueprint_options {
// base name to use for generated files
string name;
// whether to capture engravings and smooth the tiles that will be engraved
bool engrave = false;
// whether to autodetect which phases to output
bool auto_phase = false;
// if not autodetecting, which phases to output
bool dig = false;
bool track = false;
bool carve = false;
bool build = false;
bool place = false;
bool zone = false;
@ -98,9 +103,10 @@ 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, "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, "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, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "track", offsetof(blueprint_options, track), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "carve", offsetof(blueprint_options, carve), &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, "zone", offsetof(blueprint_options, zone), &df::identity_traits<bool>::identity, 0, 0 },
@ -125,6 +131,14 @@ struct tile_context {
df::building* b = NULL;
};
// global engravings cache, cleared when the string cache is cleared
struct PosHash {
size_t operator()(const df::coord &c) const {
return c.x * 65537 + c.y * 513 + c.z;
}
};
static std::unordered_map<df::coord, df::engraving *, PosHash> engravings_cache;
// We use const char * throughout this code instead of std::string to avoid
// having to allocate memory for all the small string literals. This
// significantly speeds up processing and allows us to handle very large maps
@ -140,6 +154,7 @@ static const char * cache(const char *str) {
static std::set<string> _cache;
if (!str) {
_cache.clear();
engravings_cache.clear();
return NULL;
}
return _cache.emplace(str).first->c_str();
@ -170,8 +185,6 @@ static const char * get_tile_dig(const df::coord &pos, const tile_context &) {
case tiletype_shape::PEBBLES:
case tiletype_shape::BROOK_TOP:
return "d";
case tiletype_shape::FORTIFICATION:
return "F";
case tiletype_shape::STAIR_UP:
return "u";
case tiletype_shape::STAIR_DOWN:
@ -186,6 +199,48 @@ static const char * get_tile_dig(const df::coord &pos, const tile_context &) {
}
}
static const char * get_tile_smooth_minimal(const df::coord &pos,
const tile_context &) {
df::tiletype *tt = Maps::getTileType(pos);
if (!tt)
return NULL;
switch (tileShape(*tt))
{
case tiletype_shape::FORTIFICATION:
return "s";
default:
break;
}
return NULL;
}
static const char * get_tile_smooth(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 &&
engravings_cache.count(pos))
return "s";
break;
default:
break;
}
return NULL;
}
static const char * get_track_str(const char *prefix, df::tiletype tt) {
TileDirection tdir = tileDirection(tt);
@ -198,7 +253,8 @@ static const char * get_track_str(const char *prefix, df::tiletype tt) {
return cache(prefix + dir);
}
static const char * get_tile_track(const df::coord &pos, const tile_context &) {
static const char * get_tile_carve_minimal(const df::coord &pos,
const tile_context &) {
df::tiletype *tt = Maps::getTileType(pos);
if (!tt)
return NULL;
@ -213,6 +269,32 @@ static const char * get_tile_track(const df::coord &pos, const tile_context &) {
if (tileSpecial(*tt) == tiletype_special::TRACK)
return get_track_str("trackramp", *tt);
break;
case tiletype_shape::FORTIFICATION:
return "F";
default:
break;
}
return NULL;
}
static const char * get_tile_carve(const df::coord &pos, const tile_context &tc) {
const char * tile_carve_minimal = get_tile_carve_minimal(pos, tc);
if (tile_carve_minimal)
return tile_carve_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 &&
engravings_cache.count(pos))
return "e";
break;
default:
break;
}
@ -1002,10 +1084,20 @@ static bool do_transform(color_ostream &out,
static const bp_area EMPTY_AREA;
static const bp_row EMPTY_ROW;
if (opts.engrave) {
// initialize the engravings cache
for (auto engraving : world->engravings) {
engravings_cache.emplace(engraving->pos, engraving);
}
}
vector<blueprint_processor> processors;
add_processor(processors, opts, "dig", "dig", opts.dig, get_tile_dig);
add_processor(processors, opts, "dig", "track", opts.track, get_tile_track);
add_processor(processors, opts, "dig", "smooth", opts.carve,
opts.engrave ? get_tile_smooth : get_tile_smooth_minimal);
add_processor(processors, opts, "dig", "carve", opts.carve,
opts.engrave ? get_tile_carve : get_tile_carve_minimal);
add_processor(processors, opts, "build", "build", opts.build,
get_tile_build, ensure_building);
add_processor(processors, opts, "place", "place", opts.place,

@ -36,7 +36,7 @@ function print_help() print(help_text) end
local valid_phase_list = {
'dig',
'track',
'carve',
'build',
'place',
'zone',
@ -148,6 +148,7 @@ local function process_args(opts, args)
local positionals = argparse.processArgsGetopt(args, {
{'c', 'cursor', hasArg=true,
handler=function(optarg) parse_cursor(opts, optarg) end},
{'e', 'engrave', handler=function() opts.engrave = true end},
{'f', 'format', hasArg=true,
handler=function(optarg) parse_format(opts, optarg) end},
{'h', 'help', handler=function() opts.help = true end},

@ -29,6 +29,18 @@ function test.parse_gui_commandline()
name='blueprint', start={x=1,y=2,z=3}},
opts)
opts = {}
b.parse_gui_commandline(opts, {'-e'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',
name='blueprint', engrave=true,},
opts)
opts = {}
b.parse_gui_commandline(opts, {'--engrave'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',
name='blueprint', engrave=true,},
opts)
opts = {}
b.parse_gui_commandline(opts, {'-fminimal'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',

@ -264,6 +264,23 @@ local function reset_area(area, spec)
dfhack.run_command('tiletypes-here', '--quiet', get_cursor_arg(pos))
end
local function do_phase(phase_data, area, spec)
quickfort_run(phase_data.listnum, area.pos, spec.start)
end
-- run a #dig blueprint (or just designate the whole block if there is no data)
-- and then run dig-now to materialize the designations
local function do_dig_phase(phase_data, area, spec)
if phase_data then
do_phase(phase_data, area, spec)
else
designate_area(area.pos, spec)
end
-- run dig-now to dig out designated tiles
run_dig_now(area)
end
function test.end_to_end()
-- read in test plan
local sets = get_blueprint_sets()
@ -282,27 +299,14 @@ function test.end_to_end()
goto continue
end
-- quickfort run #dig blueprint (or just designate the whole block if
-- there is no #dig blueprint)
local phases = set.phases
if phases.dig then
quickfort_run(phases.dig.listnum, area.pos, spec.start)
else
designate_area(area.pos, spec)
end
-- run dig-now to dig out designated tiles
run_dig_now(area)
-- quickfort run remaining blueprints
for _,phase_name in ipairs(phase_names) do
if phase_name ~= 'dig' and phases[phase_name] then
quickfort_run(phases[phase_name].listnum, area.pos, spec.start)
if phase_name == 'track' then
run_dig_now(area)
end
end
end
do_dig_phase(phases.dig, area, spec)
if phases.smooth then do_dig_phase(phases.smooth, area, spec) end
if phases.carve then do_dig_phase(phases.carve, area, spec) end
if phases.build then do_phase(phases.build, area, spec) end
if phases.place then do_phase(phases.place, area, spec) end
if phases.zone then do_phase(phases.zone, area, spec) end
if phases.query then do_phase(phases.query, area, spec) end
-- run blueprint to generate files in output dir
run_blueprint(basename, set, area.pos)