blueprint: write blueprints to blueprints/ subdir

to enable writing to a subdir that may not exist, blueprint now automatically
creates folder trees. E.g. ``blueprint 30 30 1 rooms/dining dig`` will create
the file ``blueprints/rooms/dining-dig.csv``). Previously it would fail if the
``blueprints/rooms/`` directory didn't already exist.
develop
Myk Taylor 2020-07-15 16:57:14 -07:00
parent 89283026ae
commit e2334387a9
4 changed files with 67 additions and 26 deletions

@ -100,6 +100,7 @@ Milo Christiansen milochristiansen
MithrilTuxedo MithrilTuxedo MithrilTuxedo MithrilTuxedo
mizipzor mizipzor mizipzor mizipzor
moversti moversti moversti moversti
Myk Taylor myk002
napagokc napagokc napagokc napagokc
Neil Little nmlittle Neil Little nmlittle
Nick Rart nickrart comestible Nick Rart nickrart comestible

@ -41,6 +41,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `RemoteFortressReader`: fixed a couple crashes that could result from decoding invalid enum items (``site_realization_building_type`` and ``improvement_type``) - `RemoteFortressReader`: fixed a couple crashes that could result from decoding invalid enum items (``site_realization_building_type`` and ``improvement_type``)
## Misc Improvements ## Misc Improvements
- `blueprint`: now writes blueprints to the ``blueprints/`` subfolder instead of the df root folder
- `blueprint`: now automatically creates folder trees when organizing blueprints into subfolders (e.g. ``blueprint 30 30 1 rooms/dining dig`` will create the file ``blueprints/rooms/dining-dig.csv``); previously it would fail if the ``blueprints/rooms/`` directory didn't already exist
- `confirm`: added a confirmation dialog for convicting dwarves of crimes - `confirm`: added a confirmation dialog for convicting dwarves of crimes
## Ruby ## Ruby

@ -93,7 +93,8 @@ if(BUILD_SUPPORTED)
dfhack_plugin(automaterial automaterial.cpp) dfhack_plugin(automaterial automaterial.cpp)
dfhack_plugin(automelt automelt.cpp) dfhack_plugin(automelt automelt.cpp)
dfhack_plugin(autotrade autotrade.cpp) dfhack_plugin(autotrade autotrade.cpp)
dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) # stdc++fs required for std::experimental::filesystem; once we move to c++17 this can be removed since it's in the STL there
dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua stdc++fs)
dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua)
dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES buildingplan-lib) dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES buildingplan-lib)

@ -3,6 +3,8 @@
//Translates a region of tiles specified by the cursor and arguments/prompts into a series of blueprint files suitable for digfort/buildingplan/quickfort //Translates a region of tiles specified by the cursor and arguments/prompts into a series of blueprint files suitable for digfort/buildingplan/quickfort
#include <algorithm> #include <algorithm>
#include <experimental/filesystem> // This is just <filesystem> in c++17, but we're currently compiling with c++11
#include <sstream>
#include <Console.h> #include <Console.h>
#include <PluginManager.h> #include <PluginManager.h>
@ -33,6 +35,8 @@ using std::pair;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
namespace filesystem = std::experimental::filesystem;
DFHACK_PLUGIN("blueprint"); DFHACK_PLUGIN("blueprint");
enum phase {DIG=1, BUILD=2, PLACE=4, QUERY=8}; enum phase {DIG=1, BUILD=2, PLACE=4, QUERY=8};
@ -62,9 +66,10 @@ command_result help(color_ostream &out)
<< " defaults to generating all blueprints" << endl << " defaults to generating all blueprints" << endl
<< endl << endl
<< "blueprint translates a portion of your fortress into blueprints suitable for" << endl << "blueprint translates a portion of your fortress into blueprints suitable for" << endl
<< " digfort/fortplan/quickfort. Blueprints are created in the DF folder with names" << endl << " digfort/fortplan/quickfort. Blueprints are created in the \"blueprints\"" << endl
<< " following a \"name-phase.csv\" pattern. Translation starts at the current" << endl << " subdirectory of the DF folder with names following a \"name-phase.csv\" pattern." << endl
<< " cursor location and includes all tiles in the range specified." << endl; << " Translation starts at the current cursor location and includes all tiles in the" << endl
<< " range specified." << endl;
return CR_OK; return CR_OK;
} }
@ -557,32 +562,52 @@ string get_tile_query(df::building* b)
return " "; return " ";
} }
command_result do_transform(DFCoord start, DFCoord end, string name, uint32_t phases) void init_stream(ofstream &out, filesystem::path basename, std::string target)
{
filesystem::path out_path(basename);
out_path += "-";
out_path += target;
out_path += ".csv";
out.open(out_path, ofstream::trunc);
out << "#" << target << endl;
}
command_result do_transform(DFCoord start, DFCoord end, string name, uint32_t phases, std::ostringstream &err)
{ {
ofstream dig, build, place, query; ofstream dig, build, place, query;
filesystem::path basename = "blueprints";
basename /= name;
basename.make_preferred();
// create output directory if it doesn't already exist
std::error_code ec;
if (!filesystem::create_directories(basename.parent_path(), ec) && ec)
{
err << "could not create output directory: " << basename.parent_path()
<< " (" << ec.message() << ")";
return CR_FAILURE;
}
if (phases & QUERY) if (phases & QUERY)
{ {
//query = ofstream((name + "-query.csv").c_str(), ofstream::trunc); //query = ofstream((name + "-query.csv").c_str(), ofstream::trunc);
query.open(name+"-query.csv", ofstream::trunc); init_stream(query, basename, "query");
query << "#query" << endl;
} }
if (phases & PLACE) if (phases & PLACE)
{ {
//place = ofstream(name + "-place.csv", ofstream::trunc); //place = ofstream(name + "-place.csv", ofstream::trunc);
place.open(name+"-place.csv", ofstream::trunc); init_stream(place, basename, "place");
place << "#place" << endl;
} }
if (phases & BUILD) if (phases & BUILD)
{ {
//build = ofstream(name + "-build.csv", ofstream::trunc); //build = ofstream(name + "-build.csv", ofstream::trunc);
build.open(name+"-build.csv", ofstream::trunc); init_stream(build, basename, "build");
build << "#build" << endl;
} }
if (phases & DIG) if (phases & DIG)
{ {
//dig = ofstream(name + "-dig.csv", ofstream::trunc); //dig = ofstream(name + "-dig.csv", ofstream::trunc);
dig.open(name+"-dig.csv", ofstream::trunc); init_stream(dig, basename, "dig");
dig << "#dig" << endl;
} }
if (start.x > end.x) if (start.x > end.x)
{ {
@ -675,9 +700,13 @@ command_result blueprint(color_ostream &out, vector<string> &parameters)
} }
DFCoord start (x, y, z); DFCoord start (x, y, z);
DFCoord end (x + stoi(parameters[0]), y + stoi(parameters[1]), z + stoi(parameters[2])); DFCoord end (x + stoi(parameters[0]), y + stoi(parameters[1]), z + stoi(parameters[2]));
if (parameters.size() == 4)
return do_transform(start, end, parameters[3], DIG | BUILD | PLACE | QUERY);
uint32_t option = 0; uint32_t option = 0;
if (parameters.size() == 4)
{
option = DIG | BUILD | PLACE | QUERY;
}
else
{
if (cmd_option_exists(parameters, "dig")) if (cmd_option_exists(parameters, "dig"))
option |= DIG; option |= DIG;
if (cmd_option_exists(parameters, "build")) if (cmd_option_exists(parameters, "build"))
@ -686,7 +715,12 @@ command_result blueprint(color_ostream &out, vector<string> &parameters)
option |= PLACE; option |= PLACE;
if (cmd_option_exists(parameters, "query")) if (cmd_option_exists(parameters, "query"))
option |= QUERY; option |= QUERY;
return do_transform(start, end, parameters[3], option); }
std::ostringstream err;
DFHack::command_result result = do_transform(start, end, parameters[3], option, err);
if (result != CR_OK)
out.printerr("%s\n", err.str().c_str());
return result;
} }
static int create(lua_State *L, uint32_t options) { static int create(lua_State *L, uint32_t options) {
@ -701,9 +735,12 @@ static int create(lua_State *L, uint32_t options) {
luaL_argerror(L, 2, "invalid end position"); luaL_argerror(L, 2, "invalid end position");
string filename(lua_tostring(L, 3)); string filename(lua_tostring(L, 3));
lua_pushboolean(L, do_transform(start, end, filename, options)); std::ostringstream err;
DFHack::command_result result = do_transform(start, end, filename, options, err);
if (result != CR_OK)
luaL_error(L, "%s", err.str().c_str());
lua_pushboolean(L, result);
return 1; return 1;
} }
static int dig(lua_State *L) { static int dig(lua_State *L) {