Merge branch 'blueprint' of https://github.com/cdombroski/dfhack into temp

Conflicts:
	NEWS
	Readme.html
develop
expwnent 2015-01-15 22:45:57 -05:00
commit 06c4755168
5 changed files with 698 additions and 3936 deletions

@ -6,6 +6,7 @@ DFHack Future
Fixes Fixes
dfhack script can now be run from other directories on OSX dfhack script can now be run from other directories on OSX
New Plugins New Plugins
blueprint: export part of your fortress to quickfort .csv files
New Scripts New Scripts
Removed Removed
embark.lua embark.lua

File diff suppressed because it is too large Load Diff

@ -1477,6 +1477,23 @@ Options:
:maps: Exports all seventeen detailed maps :maps: Exports all seventeen detailed maps
:all: Equivalent to calling all of the above, in that order :all: Equivalent to calling all of the above, in that order
blueprint
---------
Exports a portion of your fortress into QuickFort style blueprint files.::
blueprint <x> <y> <z> <name> [dig] [build] [place] [query]
Options:
:x,y,z: Size of map area to export
:name: Name of export files
:dig: Export dig commands to "<name>-dig.csv"
:build: Export build commands to "<name>-build.csv"
:place: Export stockpile commands to "<name>-place.csv"
:query: Export query commands to "<name>-query.csv"
If only region and name are given, all exports are performed.
Job management Job management
============== ==============

@ -91,6 +91,7 @@ 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)
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-lib.cpp buildingplan.cpp) DFHACK_PLUGIN(buildingplan buildingplan-lib.cpp buildingplan.cpp)

@ -0,0 +1,678 @@
//Blueprint
//By cdombroski
//Translates a region of tiles specified by the cursor and arguments/prompts into a series of blueprint files suitable for digfort/buildingplan/quickfort
#include <Console.h>
#include <PluginManager.h>
#include "modules/Buildings.h"
#include "modules/Gui.h"
#include "modules/MapCache.h"
#include "df/building_axle_horizontalst.h"
#include "df/building_bridgest.h"
#include "df/building_constructionst.h"
#include "df/building_furnacest.h"
#include "df/building_rollersst.h"
#include "df/building_screw_pumpst.h"
#include "df/building_siegeenginest.h"
#include "df/building_trapst.h"
#include "df/building_water_wheelst.h"
#include "df/building_workshopst.h"
using std::string;
using std::endl;
using std::vector;
using std::ofstream;
using std::swap;
using std::find;
using std::pair;
using namespace DFHack;
using namespace df::enums;
DFHACK_PLUGIN("blueprint");
enum phase {DIG=1, BUILD=2, PLACE=4, QUERY=8};
command_result blueprint(color_ostream &out, vector <string> &parameters);
DFhackCExport command_result plugin_init(color_ostream &out, vector<PluginCommand> &commands)
{
commands.push_back(PluginCommand("blueprint", "Convert map tiles into a blueprint", blueprint, false));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown(color_ostream &out)
{
return CR_OK;
}
command_result help(color_ostream &out)
{
out << "blueprint width height depth name [dig] [build] [place] [query]" << endl
<< " width, height, depth: area to translate in tiles" << endl
<< " name: base name for blueprint files" << endl
<< " dig: generate blueprints for digging" << endl
<< " build: generate blueprints for building" << endl
<< " place: generate blueprints for stockpiles" << endl
<< " query: generate blueprints for querying (room designations)" << endl
<< " defaults to generating all blueprints" << endl
<< 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
<< " following a \"name-phase.csv\" pattern. Translation starts at the current" << endl
<< " cursor location and includes all tiles in the range specified." << endl;
return CR_OK;
}
pair<uint32_t, uint32_t> get_building_size(df::building* b)
{
return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1);
}
char get_tile_dig(MapExtras::MapCache mc, int32_t x, int32_t y, int32_t z)
{
df::tiletype tt = mc.tiletypeAt(DFCoord(x, y, z));
df::tiletype_shape ts = tileShape(tt);
switch (ts)
{
case tiletype_shape::EMPTY:
case tiletype_shape::RAMP_TOP:
return 'h';
case tiletype_shape::FLOOR:
case tiletype_shape::BOULDER:
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:
return 'j';
case tiletype_shape::STAIR_UPDOWN:
return 'i';
case tiletype_shape::RAMP:
return 'r';
default:
return ' ';
}
}
string get_tile_build(uint32_t x, uint32_t y, df::building* b)
{
if (! b)
return " ";
bool at_nw_corner = x == b->x1 && y == b->y1;
bool at_se_corner = x == b->x2 && y == b->y2;
bool at_center = x == b->centerx && y == b->centery;
pair<uint32_t, uint32_t> size = get_building_size(b);
stringstream out = stringstream();
switch(b->getType())
{
case building_type::Armorstand:
return "a";
case building_type::Bed:
return "b";
case building_type::Chair:
return "c";
case building_type::Door:
return "d";
case building_type::Floodgate:
return "x";
case building_type::Cabinet:
return "f";
case building_type::Box:
return "h";
//case building_type::Kennel is missing
case building_type::FarmPlot:
if(!at_nw_corner)
return "`";
out << "p(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::Weaponrack:
return "r";
case building_type::Statue:
return "s";
case building_type::Table:
return "t";
case building_type::RoadPaved:
if(! at_nw_corner)
return "`";
out << "o(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::RoadDirt:
if(! at_nw_corner)
return "`";
out << "O(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::Bridge:
if(! at_nw_corner)
return "`";
switch(((df::building_bridgest*) b)->direction)
{
case df::building_bridgest::T_direction::Down:
out << "gx";
break;
case df::building_bridgest::T_direction::Left:
out << "ga";
break;
case df::building_bridgest::T_direction::Up:
out << "gw";
break;
case df::building_bridgest::T_direction::Right:
out << "gd";
break;
case df::building_bridgest::T_direction::Retracting:
out << "gs";
break;
}
out << "(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::Well:
return "l";
case building_type::SiegeEngine:
if (! at_center)
return "`";
return ((df::building_siegeenginest*) b)->type == df::siegeengine_type::Ballista ? "ib" : "ic";
case building_type::Workshop:
if (! at_center)
return "`";
switch (((df::building_workshopst*) b)->type)
{
case workshop_type::Leatherworks:
return "we";
case workshop_type::Quern:
return "wq";
case workshop_type::Millstone:
return "wM";
case workshop_type::Loom:
return "wo";
case workshop_type::Clothiers:
return "wk";
case workshop_type::Bowyers:
return "wb";
case workshop_type::Carpenters:
return "wc";
case workshop_type::MetalsmithsForge:
return "wf";
case workshop_type::MagmaForge:
return "wv";
case workshop_type::Jewelers:
return "wj";
case workshop_type::Masons:
return "wm";
case workshop_type::Butchers:
return "wu";
case workshop_type::Tanners:
return "wn";
case workshop_type::Craftsdwarfs:
return "wr";
case workshop_type::Siege:
return "ws";
case workshop_type::Mechanics:
return "wt";
case workshop_type::Still:
return "wl";
case workshop_type::Farmers:
return "ww";
case workshop_type::Kitchen:
return "wz";
case workshop_type::Fishery:
return "wh";
case workshop_type::Ashery:
return "wy";
case workshop_type::Dyers:
return "wd";
case workshop_type::Custom:
//can't do anything with custom workshop
return "`";
}
case building_type::Furnace:
if (! at_center)
return "`";
switch (((df::building_furnacest*) b)->type)
{
case furnace_type::WoodFurnace:
return "ew";
case furnace_type::Smelter:
return "es";
case furnace_type::GlassFurnace:
return "eg";
case furnace_type::Kiln:
return "ek";
case furnace_type::MagmaSmelter:
return "el";
case furnace_type::MagmaGlassFurnace:
return "ea";
case furnace_type::MagmaKiln:
return "en";
case furnace_type::Custom:
//can't do anything with custom furnace
return "`";
}
case building_type::WindowGlass:
return "y";
case building_type::WindowGem:
return "Y";
case building_type::Construction:
switch (((df::building_constructionst*) b)->type)
{
case construction_type::Fortification:
return "CF";
case construction_type::Wall:
return "CW";
case construction_type::Floor:
return "Cf";
case construction_type::UpStair:
return "Cu";
case construction_type::DownStair:
return "Cj";
case construction_type::UpDownStair:
return "Cx";
case construction_type::Ramp:
return "Cr";
case construction_type::TrackN:
return "trackN";
case construction_type::TrackS:
return "trackS";
case construction_type::TrackE:
return "trackE";
case construction_type::TrackW:
return "trackW";
case construction_type::TrackNS:
return "trackNS";
case construction_type::TrackNE:
return "trackNE";
case construction_type::TrackNW:
return "trackNW";
case construction_type::TrackSE:
return "trackSE";
case construction_type::TrackSW:
return "trackSW";
case construction_type::TrackEW:
return "trackEW";
case construction_type::TrackNSE:
return "trackNSE";
case construction_type::TrackNSW:
return "trackNSW";
case construction_type::TrackNEW:
return "trackNEW";
case construction_type::TrackSEW:
return "trackSEW";
case construction_type::TrackNSEW:
return "trackNSEW";
case construction_type::TrackRampN:
return "trackrampN";
case construction_type::TrackRampS:
return "trackrampS";
case construction_type::TrackRampE:
return "trackrampE";
case construction_type::TrackRampW:
return "trackrampW";
case construction_type::TrackRampNS:
return "trackrampNS";
case construction_type::TrackRampNE:
return "trackrampNE";
case construction_type::TrackRampNW:
return "trackrampNW";
case construction_type::TrackRampSE:
return "trackrampSE";
case construction_type::TrackRampSW:
return "trackrampSW";
case construction_type::TrackRampEW:
return "trackrampEW";
case construction_type::TrackRampNSE:
return "trackrampNSE";
case construction_type::TrackRampNSW:
return "trackrampNSW";
case construction_type::TrackRampNEW:
return "trackrampNEW";
case construction_type::TrackRampSEW:
return "trackrampSEW";
case construction_type::TrackRampNSEW:
return "trackrampNSEW";
}
case building_type::Shop:
if (! at_center)
return "`";
return "z";
case building_type::AnimalTrap:
return "m";
case building_type::Chain:
return "v";
case building_type::Cage:
return "j";
case building_type::TradeDepot:
if (! at_center)
return "`";
return "D";
case building_type::Trap:
switch (((df::building_trapst*) b)->trap_type)
{
case trap_type::StoneFallTrap:
return "Ts";
case trap_type::WeaponTrap:
return "Tw";
case trap_type::Lever:
return "Tl";
case trap_type::PressurePlate:
return "Tp";
case trap_type::CageTrap:
return "Tc";
case trap_type::TrackStop:
df::building_trapst* ts = (df::building_trapst*) b;
out << "CS";
if (ts->use_dump)
{
if (ts->dump_x_shift == 0)
{
if (ts->dump_y_shift > 0)
out << "dd";
else
out << "d";
}
else
{
if (ts->dump_x_shift > 0)
out << "ddd";
else
out << "dddd";
}
}
switch (ts->friction)
{
case 10:
out << "a";
case 50:
out << "a";
case 500:
out << "a";
case 10000:
out << "a";
}
return out.str();
}
case building_type::ScrewPump:
if (! at_se_corner) //screw pumps anchor at bottom/right
return "`";
switch (((df::building_screw_pumpst*) b)->direction)
{
case screw_pump_direction::FromNorth:
return "Msu";
case screw_pump_direction::FromEast:
return "Msk";
case screw_pump_direction::FromSouth:
return "Msm";
case screw_pump_direction::FromWest:
return "Msh";
}
case building_type::WaterWheel:
if (! at_center)
return "`";
//s swaps orientation which defaults to vertical
return ((df::building_water_wheelst*) b)->is_vertical ? "Mw" : "Mws";
case building_type::Windmill:
if (! at_center)
return "`";
return "Mm";
case building_type::GearAssembly:
return "Mg";
case building_type::AxleHorizontal:
if (! at_nw_corner) //a guess based on how constructions work
return "`";
//same as water wheel but reversed
out << "Mh" << (((df::building_axle_horizontalst*) b)->is_vertical ? "s" : "")
<< "(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::AxleVertical:
return "Mv";
case building_type::Rollers:
if (! at_nw_corner)
return "`";
out << "Mr";
switch (((df::building_rollersst*) b)->direction)
{
case screw_pump_direction::FromNorth:
break;
case screw_pump_direction::FromEast:
out << "s";
case screw_pump_direction::FromSouth:
out << "s";
case screw_pump_direction::FromWest:
out << "s";
}
out << "(" << size.first << "x" << size.second << ")";
return out.str();
case building_type::Support:
return "S";
case building_type::ArcheryTarget:
return "A";
case building_type::TractionBench:
return "R";
case building_type::Hatch:
return "H";
case building_type::Slab:
//how to mine alt key?!?
//alt+s
return " ";
case building_type::NestBox:
return "N";
case building_type::Hive:
//alt+h
return " ";
case building_type::GrateWall:
return "W";
case building_type::GrateFloor:
return "G";
case building_type::BarsVertical:
return "B";
case building_type::BarsFloor:
//alt+b
return " ";
default:
return " ";
}
}
string get_tile_place(uint32_t x, uint32_t y, df::building* b)
{
if (! b || b->getType() != building_type::Stockpile)
return " ";
if (b->x1 != x || b->y1 != y)
return "`";
pair<uint32_t, uint32_t> size = get_building_size(b);
df::building_stockpilest* sp = (df::building_stockpilest*) b;
stringstream out = stringstream();
switch (sp->settings.flags.whole)
{
case df::stockpile_group_set::mask_animals:
out << "a";
break;
case df::stockpile_group_set::mask_food:
out << "f";
break;
case df::stockpile_group_set::mask_furniture:
out << "u";
break;
case df::stockpile_group_set::mask_corpses:
out << "y";
break;
case df::stockpile_group_set::mask_refuse:
out << "r";
break;
case df::stockpile_group_set::mask_wood:
out << "w";
break;
case df::stockpile_group_set::mask_stone:
out << "s";
break;
case df::stockpile_group_set::mask_gems:
out << "e";
break;
case df::stockpile_group_set::mask_bars_blocks:
out << "b";
break;
case df::stockpile_group_set::mask_cloth:
out << "h";
break;
case df::stockpile_group_set::mask_leather:
out << "l";
break;
case df::stockpile_group_set::mask_ammo:
out << "z";
break;
case df::stockpile_group_set::mask_coins:
out << "n";
break;
case df::stockpile_group_set::mask_finished_goods:
out << "g";
break;
case df::stockpile_group_set::mask_weapons:
out << "p";
break;
case df::stockpile_group_set::mask_armor:
out << "d";
break;
default: //multiple stockpile type
return "`";
}
out << "("<< size.first << "x" << size.second << ")";
return out.str();
}
string get_tile_query(df::building* b)
{
if (b && b->is_room)
return "r+";
return " ";
}
command_result do_transform(DFCoord start, DFCoord end, string name, uint32_t phases)
{
ofstream dig, build, place, query;
if (phases & QUERY)
{
query = ofstream(name + "-query.csv", ofstream::trunc);
query << "#query" << endl;
}
if (phases & PLACE)
{
place = ofstream(name + "-place.csv", ofstream::trunc);
place << "#place" << endl;
}
if (phases & BUILD)
{
build = ofstream(name + "-build.csv", ofstream::trunc);
build << "#build" << endl;
}
if (phases & DIG)
{
dig = ofstream(name + "-dig.csv", ofstream::trunc);
dig << "#dig" << endl;
}
if (start.x > end.x)
{
swap(start.x, end.x);
start.x++;
end.x++;
}
if (start.y > end.y)
{
swap(start.y, end.y);
start.y++;
end.y++;
}
if (start.z > end.z)
{
swap(start.z, end.z);
start.z++;
end.z++;
}
MapExtras::MapCache mc;
for (int32_t z = start.z; z < end.z; z++)
{
for (int32_t y = start.y; y < end.y; y++)
{
for (int32_t x = start.x; x < end.x; x++)
{
df::building* b = DFHack::Buildings::findAtTile(DFCoord(x, y, z));
if (phases & QUERY)
query << get_tile_query(b) << ',';
if (phases & PLACE)
place << get_tile_place(x, y, b) << ',';
if (phases & BUILD)
build << get_tile_build(x, y, b) << ',';
if (phases & DIG)
dig << get_tile_dig(mc, x, y, z) << ',';
}
if (phases & QUERY)
query << "#" << endl;
if (phases & PLACE)
place << "#" << endl;
if (phases & BUILD)
build << "#" << endl;
if (phases & DIG)
dig << "#" << endl;
}
if (z < end.z - 1)
{
if (phases & QUERY)
query << "#<" << endl;
if (phases & PLACE)
place << "#<" << endl;
if (phases & BUILD)
build << "#<" << endl;
if (phases & DIG)
dig << "#<" << endl;
}
}
if (phases & QUERY)
query.close();
if (phases & PLACE)
place.close();
if (phases & BUILD)
build.close();
if (phases & DIG)
dig.close();
return CR_OK;
}
bool cmd_option_exists(vector<string>& parameters, const string& option)
{
return find(parameters.begin(), parameters.end(), option) != parameters.end();
}
command_result blueprint(color_ostream &out, vector<string> &parameters)
{
if (parameters.size() < 4 || parameters.size() > 8)
return help(out);
CoreSuspender suspend;
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
int32_t x, y, z;
if (!Gui::getCursorCoords(x, y, z))
{
out.printerr("Can't get cursor coords! Make sure you have an active cursor in DF.\n");
return CR_FAILURE;
}
DFCoord start (x, y, z);
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;
if (cmd_option_exists(parameters, "dig"))
option |= DIG;
if (cmd_option_exists(parameters, "build"))
option |= BUILD;
if (cmd_option_exists(parameters, "place"))
option |= PLACE;
if (cmd_option_exists(parameters, "query"))
option |= QUERY;
return do_transform(start, end, parameters[3], option);
}