dfhack/plugins/fortplan.cpp

188 lines
5.8 KiB
C++

#include "buildingplan-lib.h"
#include <fstream>
#include <vector>
DFHACK_PLUGIN("fortplan");
#define PLUGIN_VERSION 0.10
command_result fortplan(color_ostream &out, vector<string> & params);
struct BuildingInfo {
std::string code;
df::building_type type;
std::string name;
BuildingInfo(std::string theCode, df::building_type theType, std::string theName) {
code = theCode;
type = theType;
name = theName;
}
};
class MatchesCode
{
std::string _code;
public:
MatchesCode(const std::string &code) : _code(code) {}
bool operator()(const BuildingInfo &check) const
{
return check.code == _code;
}
};
std::vector<BuildingInfo> buildings;
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands) {
commands.push_back(PluginCommand("fortplan","Lay out buildings in your fortress based on a Quickfort-style CSV input file.",fortplan,false,
"Lay out buildings in your fortress based on a Quickfort-style CSV input file.\n"
"Usage: fortplan [filename]\n"));
buildings.push_back(BuildingInfo("a",df::building_type::Armorstand,"Armor Stand"));
buildings.push_back(BuildingInfo("r",df::building_type::Weaponrack,"Weapon Rack"));
buildings.push_back(BuildingInfo("b",df::building_type::Bed,"Bed"));
buildings.push_back(BuildingInfo("f",df::building_type::Cabinet,"Cabinet"));
buildings.push_back(BuildingInfo("h",df::building_type::Box,"Box"));
buildings.push_back(BuildingInfo("d",df::building_type::Door,"Door"));
buildings.push_back(BuildingInfo("n",df::building_type::Coffin,"Coffin"));
buildings.push_back(BuildingInfo("c",df::building_type::Chair,"Chair"));
buildings.push_back(BuildingInfo("t",df::building_type::Table,"Table"));
out << "Loaded fortplan version " << PLUGIN_VERSION << endl;
return CR_OK;
}
#define DAY_TICKS 1200
DFhackCExport command_result plugin_onupdate(color_ostream &out)
{
static decltype(world->frame_counter) last_frame_count = 0;
if ((world->frame_counter - last_frame_count) >= DAY_TICKS/2)
{
last_frame_count = world->frame_counter;
planner.doCycle();
}
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
planner.reset(out);
is_enabled = enable;
}
return CR_OK;
}
std::string get_working_path()
{
char temp[MAXPATHLEN];
return ( getcwd(temp, MAXPATHLEN) ? std::string( temp ) : std::string("") );
}
command_result fortplan(color_ostream &out, vector<string> & params) {
auto & con = out;
if (params.size()) {
coord32_t cursor;
coord32_t startCursor;
if (!DFHack::Gui::getCursorCoords(cursor.x, cursor.y, cursor.z)) {
con.print("You must have an active in-game cursor.\n");
return CR_FAILURE;
}
DFHack::Gui::getCursorCoords(startCursor.x, startCursor.y, startCursor.z);
std::string cwd = get_working_path();
std::string filename = cwd+"/"+params[0];
con.print("Loading file '%s'...\n",filename.c_str());
std::ifstream infile(filename.c_str());
if (!infile.good()) {
con.print("Could not open the file.\n");
return CR_FAILURE;
}
std::string line;
int lineNum = 0;
while (std::getline(infile, line)) {
lineNum++;
if (lineNum==1) {
auto hashBuild = line.find("#build");
if (hashBuild >= 0) {
auto startLoc = line.find("start(");
if (startLoc != line.npos) {
startLoc += 6;
auto nextDelimiter = line.find(";",startLoc);
std::string startXStr = line.substr(startLoc,nextDelimiter-startLoc);
int startXOffset = std::stoi(startXStr);
startLoc = nextDelimiter+1;
nextDelimiter = line.find(";",startLoc);
std::string startYStr = line.substr(startLoc,nextDelimiter-startLoc);
int startYOffset = std::stoi(startYStr);
startCursor.x -= startXOffset;
startCursor.y -= startYOffset;
DFHack::Gui::setCursorCoords(startCursor.x,startCursor.y,startCursor.z);
auto startEnd = line.find(")",nextDelimiter);
con.print("Starting at (%d,%d,%d) which is described as: %s\n",startCursor.x,startCursor.y,startCursor.z,line.substr(nextDelimiter+1,startEnd-nextDelimiter).c_str());
std::string desc = line.substr(startEnd+1);
if (desc.size()>0) {
con.print("Description of this plan: %s\n",desc.c_str());
}
continue;
} else {
con.print("No start location found for this block\n");
}
} else {
con.print("Not a build file: %s\n",line.c_str());
break;
}
}
int start = 0;
auto nextInd = line.find(',');
std::string curCell = line.substr(start,nextInd-start);
if (strcmp(curCell.substr(0,1).c_str(),"#")==0) {
continue;
}
do {
if (strcmp(curCell.c_str(),"`")!=0) {
con.print("Found a cell with '%s' in it (line %d:%d-%d)\n",curCell.c_str(),lineNum,start,nextInd);
auto buildingIndex = std::find_if(buildings.begin(), buildings.end(), MatchesCode(curCell.c_str()));
// = std::find(validInstructions.begin(), validInstructions.end(), curCell);
if(buildingIndex == buildings.end()) {
con.print("That is not a valid code.\n");
} else {
//con.print("I can build that!\n");
BuildingInfo buildingInfo = *buildingIndex;
con.print("Building a(n) %s.\n",buildingInfo.name.c_str());
planner.allocatePlannedBuilding(buildingInfo.type);
}
}
cursor.x++;
DFHack::Gui::setCursorCoords(cursor.x, cursor.y, cursor.z);
start = nextInd+1;
nextInd = line.find(',',start);
curCell = line.substr(start,nextInd-start);
} while (nextInd != line.npos);
cursor.y++;
cursor.x = startCursor.x;
}
con.print("Done with file.\n");
} else {
con.print("You must supply a filename to read.\n");
}
return CR_OK;
}