diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index bde5bb77e..262a3bd5b 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -77,7 +77,10 @@ phases; just separate them with a space. ``zone`` Generate quickfort ``#zone`` blueprints for designating zones. ``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`` blueprint will be created only if there are stockpiles in the blueprint area. diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 3dd912d28..bb4d03d32 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -92,6 +93,7 @@ struct blueprint_options { bool place = false; bool zone = false; bool query = false; + bool rooms = false; 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::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "rooms", offsetof(blueprint_options, rooms), &df::identity_traits::identity, 0, 0 }, { struct_field_info::END } }; struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn, NULL, "blueprint_options", NULL, blueprint_options_fields); @@ -134,11 +137,36 @@ DFhackCExport command_result plugin_shutdown(color_ostream &) { return CR_OK; } +struct blueprint_processor; struct tile_context { + blueprint_processor *processor; bool pretty = false; df::building* b = NULL; }; +typedef vector bp_row; // index is x coordinate +typedef map bp_area; // key is y coordinate +typedef map 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 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 struct PosHash { size_t operator()(const df::coord &c) const { @@ -974,7 +1002,45 @@ static const char * get_tile_zone(const df::coord &pos, 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 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) return NULL; return "r+"; @@ -1027,28 +1093,6 @@ static bool get_filename(string &fname, return true; } -typedef vector bp_row; // index is x coordinate -typedef map bp_area; // key is y coordinate -typedef map 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, const bp_volume &mapdata) { if (mapdata.begin() == mapdata.end()) @@ -1194,6 +1238,8 @@ static bool do_transform(color_ostream &out, add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone); add_processor(processors, opts, "query", "query", opts.query, get_tile_query, ensure_building); + add_processor(processors, opts, "query", "rooms", opts.rooms, + get_tile_rooms, ensure_building); if (processors.empty()) { out.printerr("no phases requested! nothing to do!\n"); @@ -1212,6 +1258,7 @@ static bool do_transform(color_ostream &out, tile_context ctx; ctx.pretty = pretty; for (blueprint_processor &processor : processors) { + ctx.processor = &processor; if (processor.init_ctx) processor.init_ctx(pos, ctx); const char *tile_str = processor.get_tile(pos, ctx); diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index 564962448..6fe869d3b 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -11,6 +11,7 @@ local valid_phase_list = { 'place', 'zone', 'query', + 'rooms', } valid_phases = utils.invert(valid_phase_list) diff --git a/test/quickfort/ecosystem.lua b/test/quickfort/ecosystem.lua index b59b8909d..27dddc5d5 100644 --- a/test/quickfort/ecosystem.lua +++ b/test/quickfort/ecosystem.lua @@ -332,6 +332,7 @@ function test.end_to_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 + if phases.rooms then do_phase(phases.rooms, area, spec) end -- run any extra commands, if defined by the blueprint spec if spec.extra_fn then