Merge pull request #2334 from myk002/myk_blueprint_rooms

[blueprint] split rooms phase from query
develop
Myk 2022-10-12 16:48:28 -07:00 committed by GitHub
commit a1a7a93f74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 25 deletions

@ -40,6 +40,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Misc Improvements ## Misc Improvements
- `blueprint`: new ``--smooth`` option for recording all smoothed floors and walls instead of just the ones that require smoothing for later carving - `blueprint`: new ``--smooth`` option for recording all smoothed floors and walls instead of just the ones that require smoothing for later carving
- `blueprint`: record built constructions in blueprints - `blueprint`: record built constructions in blueprints
- `blueprint`: record stockpile/building/zone names in blueprints
- `blueprint`: record room sizes in blueprints
- `ls`: indent tag listings and wrap them in the right column for better readability - `ls`: indent tag listings and wrap them in the right column for better readability
- `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output. - `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output.
- `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down - `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down

@ -77,7 +77,10 @@ phases; just separate them with a space.
``zone`` ``zone``
Generate quickfort ``#zone`` blueprints for designating zones. Generate quickfort ``#zone`` blueprints for designating zones.
``query`` ``query``
Generate quickfort ``#query`` blueprints for configuring rooms. Generate quickfort ``#query`` blueprints for configuring stockpiles and
naming buildings.
``rooms``
Generate quickfort ``#query`` blueprints for defining rooms.
If no phases are specified, phases are autodetected. For example, a ``#place`` If no phases are specified, phases are autodetected. For example, a ``#place``
blueprint will be created only if there are stockpiles in the blueprint area. blueprint will be created only if there are stockpiles in the blueprint area.

@ -6,6 +6,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <regex>
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
@ -92,6 +93,7 @@ struct blueprint_options {
bool place = false; bool place = false;
bool zone = false; bool zone = false;
bool query = false; bool query = false;
bool rooms = false;
static struct_identity _identity; static struct_identity _identity;
}; };
@ -116,6 +118,7 @@ static const struct_field_info blueprint_options_fields[] = {
{ struct_field_info::PRIMITIVE, "place", offsetof(blueprint_options, place), &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 }, { struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &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, "query", offsetof(blueprint_options, query), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "rooms", offsetof(blueprint_options, rooms), &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);
@ -134,11 +137,36 @@ DFhackCExport command_result plugin_shutdown(color_ostream &) {
return CR_OK; return CR_OK;
} }
struct blueprint_processor;
struct tile_context { struct tile_context {
blueprint_processor *processor;
bool pretty = false; bool pretty = false;
df::building* b = NULL; df::building* b = NULL;
}; };
typedef vector<const char *> bp_row; // index is x coordinate
typedef map<int16_t, bp_row> bp_area; // key is y coordinate
typedef map<int16_t, bp_area> bp_volume; // key is z coordinate
typedef const char * (get_tile_fn)(const df::coord &pos,
const tile_context &ctx);
typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx);
struct blueprint_processor {
bp_volume mapdata;
const string mode;
const string phase;
const bool force_create;
get_tile_fn * const get_tile;
init_ctx_fn * const init_ctx;
std::set<df::building *> seen;
blueprint_processor(const string &mode, const string &phase,
bool force_create, get_tile_fn *get_tile,
init_ctx_fn *init_ctx)
: mode(mode), phase(phase), force_create(force_create),
get_tile(get_tile), init_ctx(init_ctx) { }
};
// global engravings cache, cleared when the string cache is cleared // global engravings cache, cleared when the string cache is cleared
struct PosHash { struct PosHash {
size_t operator()(const df::coord &c) const { size_t operator()(const df::coord &c) const {
@ -974,10 +1002,70 @@ static const char * get_tile_zone(const df::coord &pos,
return add_expansion_syntax(zone, get_zone_keys(zone)); return add_expansion_syntax(zone, get_zone_keys(zone));
} }
static const char * get_tile_query(const df::coord &, const tile_context &ctx) { static string csv_sanitize(const string &str) {
static const std::regex pattern("\"");
static const string replacement("\"\"");
return std::regex_replace(str, pattern, replacement);
}
static const char * get_tile_query(const df::coord &pos,
const tile_context &ctx) {
string bld_name, zone_name;
auto & seen = ctx.processor->seen;
if (ctx.b && !seen.count(ctx.b)) {
bld_name = ctx.b->name;
seen.emplace(ctx.b);
}
vector<df::building_civzonest*> civzones;
if (Buildings::findCivzonesAt(&civzones, pos)) {
auto civzone = civzones.back();
if (!seen.count(civzone)) {
zone_name = civzone->name;
seen.emplace(civzone);
}
}
if (!bld_name.size() && !zone_name.size())
return NULL;
std::ostringstream str;
if (bld_name.size())
str << "{givename name=\"" << csv_sanitize(bld_name) << "\"}";
if (zone_name.size())
str << "{namezone name=\"" << csv_sanitize(zone_name) << "\"}";
return cache(str);
}
static const char * get_tile_rooms(const df::coord &, const tile_context &ctx) {
if (!ctx.b || !ctx.b->is_room) if (!ctx.b || !ctx.b->is_room)
return NULL; return NULL;
return "r+";
// get the maximum distance from the center of the building
df::building_extents &room = ctx.b->room;
int32_t x1 = room.x;
int32_t x2 = room.x + room.width - 1;
int32_t y1 = room.y;
int32_t y2 = room.y + room.height - 1;
int32_t dimx = std::max(ctx.b->centerx - x1, x2 - ctx.b->centerx);
int32_t dimy = std::max(ctx.b->centery - y1, y2 - ctx.b->centery);
int32_t max_dim = std::max(dimx, dimy);
switch (max_dim) {
case 0: return "r---&";
case 1: return "r--&";
case 2: return "r-&";
case 3: return "r&";
case 4: return "r+&";
}
std::ostringstream str;
str << "r{+ " << (max_dim - 3) << "}&";
return cache(str);
} }
static bool create_output_dir(color_ostream &out, static bool create_output_dir(color_ostream &out,
@ -1027,28 +1115,6 @@ static bool get_filename(string &fname,
return true; return true;
} }
typedef vector<const char *> bp_row; // index is x coordinate
typedef map<int16_t, bp_row> bp_area; // key is y coordinate
typedef map<int16_t, bp_area> bp_volume; // key is z coordinate
typedef const char * (get_tile_fn)(const df::coord &pos,
const tile_context &ctx);
typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx);
struct blueprint_processor {
bp_volume mapdata;
const string mode;
const string phase;
const bool force_create;
get_tile_fn * const get_tile;
init_ctx_fn * const init_ctx;
blueprint_processor(const string &mode, const string &phase,
bool force_create, get_tile_fn *get_tile,
init_ctx_fn *init_ctx)
: mode(mode), phase(phase), force_create(force_create),
get_tile(get_tile), init_ctx(init_ctx) { }
};
static void write_minimal(ofstream &ofile, const blueprint_options &opts, static void write_minimal(ofstream &ofile, const blueprint_options &opts,
const bp_volume &mapdata) { const bp_volume &mapdata) {
if (mapdata.begin() == mapdata.end()) if (mapdata.begin() == mapdata.end())
@ -1194,6 +1260,8 @@ static bool do_transform(color_ostream &out,
add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone); add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone);
add_processor(processors, opts, "query", "query", opts.query, add_processor(processors, opts, "query", "query", opts.query,
get_tile_query, ensure_building); get_tile_query, ensure_building);
add_processor(processors, opts, "query", "rooms", opts.rooms,
get_tile_rooms, ensure_building);
if (processors.empty()) { if (processors.empty()) {
out.printerr("no phases requested! nothing to do!\n"); out.printerr("no phases requested! nothing to do!\n");
@ -1212,6 +1280,7 @@ static bool do_transform(color_ostream &out,
tile_context ctx; tile_context ctx;
ctx.pretty = pretty; ctx.pretty = pretty;
for (blueprint_processor &processor : processors) { for (blueprint_processor &processor : processors) {
ctx.processor = &processor;
if (processor.init_ctx) if (processor.init_ctx)
processor.init_ctx(pos, ctx); processor.init_ctx(pos, ctx);
const char *tile_str = processor.get_tile(pos, ctx); const char *tile_str = processor.get_tile(pos, ctx);

@ -11,6 +11,7 @@ local valid_phase_list = {
'place', 'place',
'zone', 'zone',
'query', 'query',
'rooms',
} }
valid_phases = utils.invert(valid_phase_list) valid_phases = utils.invert(valid_phase_list)

@ -332,6 +332,7 @@ function test.end_to_end()
if phases.place then do_phase(phases.place, 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.zone then do_phase(phases.zone, area, spec) end
if phases.query then do_phase(phases.query, area, spec) end if phases.query then do_phase(phases.query, area, spec) end
if phases.rooms then do_phase(phases.rooms, area, spec) end
-- run any extra commands, if defined by the blueprint spec -- run any extra commands, if defined by the blueprint spec
if spec.extra_fn then if spec.extra_fn then