2011-08-01 18:21:25 -06:00
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <map>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
|
2011-12-31 04:48:42 -07:00
|
|
|
#include "Core.h"
|
2012-01-15 13:54:14 -07:00
|
|
|
#include "Console.h"
|
|
|
|
#include "Export.h"
|
|
|
|
#include "PluginManager.h"
|
|
|
|
#include "modules/Maps.h"
|
|
|
|
#include "modules/Gui.h"
|
|
|
|
#include "TileTypes.h"
|
|
|
|
#include "modules/MapCache.h"
|
2016-08-13 19:44:01 -06:00
|
|
|
|
2012-12-06 15:46:59 -07:00
|
|
|
#include "df/plant.h"
|
2016-08-13 19:44:01 -06:00
|
|
|
#include "df/world.h"
|
2011-08-01 18:21:25 -06:00
|
|
|
|
|
|
|
using std::vector;
|
|
|
|
using std::string;
|
|
|
|
using namespace DFHack;
|
2012-12-06 15:46:59 -07:00
|
|
|
|
2012-02-21 10:19:17 -07:00
|
|
|
DFHACK_PLUGIN("plants");
|
2014-12-06 16:47:35 -07:00
|
|
|
REQUIRE_GLOBAL(world);
|
|
|
|
|
|
|
|
const uint32_t sapling_to_tree_threshold = 120 * 28 * 12 * 3 - 1; // 3 years minus 1 - let the game handle the actual growing-up
|
2011-08-01 18:21:25 -06:00
|
|
|
|
2012-03-10 04:55:42 -07:00
|
|
|
command_result df_grow (color_ostream &out, vector <string> & parameters)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2012-01-31 09:55:38 -07:00
|
|
|
for(size_t i = 0; i < parameters.size();i++)
|
2011-08-08 17:50:22 -06:00
|
|
|
{
|
|
|
|
if(parameters[i] == "help" || parameters[i] == "?")
|
2013-11-02 11:54:29 -06:00
|
|
|
{
|
2014-08-06 09:03:34 -06:00
|
|
|
out.print("Usage:\n"
|
2013-11-02 11:54:29 -06:00
|
|
|
"This command turns all living saplings on the map into full-grown trees.\n"
|
2014-08-06 09:03:34 -06:00
|
|
|
"With active cursor, work on the targetted one only.\n");
|
2013-11-02 11:54:29 -06:00
|
|
|
return CR_OK;
|
|
|
|
}
|
2011-08-08 17:50:22 -06:00
|
|
|
}
|
2013-11-02 11:54:29 -06:00
|
|
|
|
2012-03-10 04:55:42 -07:00
|
|
|
CoreSuspender suspend;
|
|
|
|
|
2012-01-19 20:44:17 -07:00
|
|
|
if (!Maps::IsValid())
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2012-03-10 04:55:42 -07:00
|
|
|
out.printerr("Map is not available!\n");
|
2011-08-01 18:21:25 -06:00
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
2012-01-19 20:44:17 -07:00
|
|
|
MapExtras::MapCache map;
|
2011-08-01 18:21:25 -06:00
|
|
|
int32_t x,y,z;
|
2014-08-06 09:03:34 -06:00
|
|
|
int grown = 0;
|
2012-03-03 06:38:24 -07:00
|
|
|
if(Gui::getCursorCoords(x,y,z))
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2014-08-06 08:31:42 -06:00
|
|
|
for(size_t i = 0; i < world->plants.all.size(); i++)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2014-08-06 08:31:42 -06:00
|
|
|
df::plant * tree = world->plants.all[i];
|
|
|
|
if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2014-08-06 08:31:42 -06:00
|
|
|
if(tileShape(map.tiletypeAt(DFCoord(x,y,z))) == tiletype_shape::SAPLING &&
|
|
|
|
tileSpecial(map.tiletypeAt(DFCoord(x,y,z))) != tiletype_special::DEAD)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2014-08-06 08:31:42 -06:00
|
|
|
tree->grow_counter = sapling_to_tree_threshold;
|
2014-08-06 09:03:34 -06:00
|
|
|
grown++;
|
2011-08-01 18:21:25 -06:00
|
|
|
}
|
2014-08-06 08:31:42 -06:00
|
|
|
break;
|
2011-08-01 18:21:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-01-21 16:54:57 -07:00
|
|
|
for(size_t i = 0 ; i < world->plants.all.size(); i++)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2012-01-21 16:54:57 -07:00
|
|
|
df::plant *p = world->plants.all[i];
|
2012-02-13 15:56:33 -07:00
|
|
|
df::tiletype ttype = map.tiletypeAt(df::coord(p->pos.x,p->pos.y,p->pos.z));
|
|
|
|
if(!p->flags.bits.is_shrub && tileShape(ttype) == tiletype_shape::SAPLING && tileSpecial(ttype) != tiletype_special::DEAD)
|
2011-08-01 18:21:25 -06:00
|
|
|
{
|
2012-12-06 15:46:59 -07:00
|
|
|
p->grow_counter = sapling_to_tree_threshold;
|
2014-08-06 09:03:34 -06:00
|
|
|
grown++;
|
2011-08-01 18:21:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-06 09:03:34 -06:00
|
|
|
if (grown)
|
|
|
|
out.print("%i plants grown.\n", grown);
|
|
|
|
else
|
|
|
|
out.printerr("No plant(s) found!\n");
|
2011-08-01 18:21:25 -06:00
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2013-10-30 13:19:52 -06:00
|
|
|
command_result df_createplant (color_ostream &out, vector <string> & parameters)
|
|
|
|
{
|
|
|
|
if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
|
2013-11-02 11:54:29 -06:00
|
|
|
{
|
2014-08-06 09:03:34 -06:00
|
|
|
out.print("Usage:\n"
|
2013-11-02 11:54:29 -06:00
|
|
|
"Create a new plant at the cursor.\n"
|
|
|
|
"Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
|
2014-08-06 09:03:34 -06:00
|
|
|
"Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n");
|
2013-11-02 11:54:29 -06:00
|
|
|
return CR_OK;
|
|
|
|
}
|
2013-10-30 13:19:52 -06:00
|
|
|
|
|
|
|
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("No cursor detected - please place the cursor over the location in which you wish to create a new plant.\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
df::map_block *map = Maps::getTileBlock(x, y, z);
|
2014-08-06 08:31:42 -06:00
|
|
|
df::map_block_column *col = Maps::getBlockColumn((x / 48) * 3, (y / 48) * 3);
|
|
|
|
if (!map || !col)
|
2013-10-30 13:19:52 -06:00
|
|
|
{
|
|
|
|
out.printerr("Invalid location selected!\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
int tx = x & 15, ty = y & 15;
|
|
|
|
int mat = tileMaterial(map->tiletype[tx][ty]);
|
|
|
|
if ((tileShape(map->tiletype[tx][ty]) != tiletype_shape::FLOOR) || ((mat != tiletype_material::SOIL) && (mat != tiletype_material::GRASS_DARK) && (mat != tiletype_material::GRASS_LIGHT)))
|
|
|
|
{
|
|
|
|
out.printerr("Plants can only be placed on dirt or grass floors!\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plant_id = -1;
|
|
|
|
df::plant_raw *plant_raw = NULL;
|
|
|
|
for (size_t i = 0; i < world->raws.plants.all.size(); i++)
|
|
|
|
{
|
|
|
|
plant_raw = world->raws.plants.all[i];
|
|
|
|
if (plant_raw->id == parameters[0])
|
|
|
|
{
|
|
|
|
plant_id = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (plant_id == -1)
|
|
|
|
{
|
|
|
|
out.printerr("Invalid plant ID specified!\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
if (plant_raw->flags.is_set(plant_raw_flags::GRASS))
|
|
|
|
{
|
|
|
|
out.printerr("You cannot plant grass using this command.\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-11-04 13:55:31 -07:00
|
|
|
df::plant *plant = df::allocate<df::plant>();
|
2013-10-30 13:19:52 -06:00
|
|
|
if (plant_raw->flags.is_set(plant_raw_flags::TREE))
|
|
|
|
plant->hitpoints = 400000;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plant->hitpoints = 100000;
|
|
|
|
plant->flags.bits.is_shrub = 1;
|
|
|
|
}
|
|
|
|
// for now, always set "watery" for WET-permitted plants, even if they're spawned away from water
|
|
|
|
// the proper method would be to actually look for nearby water features, but it's not clear exactly how that works
|
|
|
|
if (plant_raw->flags.is_set(plant_raw_flags::WET))
|
|
|
|
plant->flags.bits.watery = 1;
|
|
|
|
plant->material = plant_id;
|
|
|
|
plant->pos.x = x;
|
|
|
|
plant->pos.y = y;
|
|
|
|
plant->pos.z = z;
|
|
|
|
plant->update_order = rand() % 10;
|
|
|
|
|
|
|
|
world->plants.all.push_back(plant);
|
|
|
|
switch (plant->flags.whole & 3)
|
|
|
|
{
|
|
|
|
case 0: world->plants.tree_dry.push_back(plant); break;
|
|
|
|
case 1: world->plants.tree_wet.push_back(plant); break;
|
|
|
|
case 2: world->plants.shrub_dry.push_back(plant); break;
|
|
|
|
case 3: world->plants.shrub_wet.push_back(plant); break;
|
|
|
|
}
|
2014-08-06 08:31:42 -06:00
|
|
|
col->plants.push_back(plant);
|
2013-10-30 13:19:52 -06:00
|
|
|
if (plant->flags.bits.is_shrub)
|
|
|
|
map->tiletype[tx][ty] = tiletype::Shrub;
|
|
|
|
else
|
|
|
|
map->tiletype[tx][ty] = tiletype::Sapling;
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2013-11-02 11:54:29 -06:00
|
|
|
command_result df_plant (color_ostream &out, vector <string> & parameters)
|
|
|
|
{
|
|
|
|
if (parameters.size() >= 1)
|
|
|
|
{
|
|
|
|
if (parameters[0] == "grow") {
|
|
|
|
parameters.erase(parameters.begin());
|
|
|
|
return df_grow(out, parameters);
|
2021-06-07 22:51:25 -06:00
|
|
|
} else if (parameters[0] == "create") {
|
2013-11-02 11:54:29 -06:00
|
|
|
parameters.erase(parameters.begin());
|
|
|
|
return df_createplant(out, parameters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
|
2013-10-30 13:19:52 -06:00
|
|
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
|
|
|
{
|
2013-11-02 11:54:29 -06:00
|
|
|
commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false,
|
|
|
|
"Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n"
|
2021-06-07 22:51:25 -06:00
|
|
|
"plant grow help - Grows saplings into trees.\n"
|
|
|
|
"plant create help - Create a new plant.\n"));
|
2013-11-02 11:54:29 -06:00
|
|
|
|
2013-10-30 13:19:52 -06:00
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
|
|
|
{
|
|
|
|
return CR_OK;
|
2013-11-02 11:54:29 -06:00
|
|
|
}
|