added changelayer plugin
parent
96064da6ba
commit
e63ba6f87d
@ -0,0 +1,32 @@
|
||||
PROJECT (changelayer)
|
||||
# A list of source files
|
||||
SET(PROJECT_SRCS
|
||||
changelayer.cpp
|
||||
)
|
||||
# A list of headers
|
||||
SET(PROJECT_HDRS
|
||||
)
|
||||
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
# mash them together (headers are marked as headers and nothing will try to compile them)
|
||||
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
|
||||
|
||||
# option to use a thread for no particular reason
|
||||
OPTION(CHANGELAYER_THREAD "Use threads in the changelayer plugin." ON)
|
||||
#linux
|
||||
IF(UNIX)
|
||||
add_definitions(-DLINUX_BUILD)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra linux libs here
|
||||
${PROJECT_LIBS}
|
||||
)
|
||||
# windows
|
||||
ELSE(UNIX)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra linux libs here
|
||||
${PROJECT_LIBS}
|
||||
$(NOINHERIT)
|
||||
)
|
||||
ENDIF(UNIX)
|
||||
# this makes sure all the stuff is put in proper places and linked to dfhack
|
||||
DFHACK_PLUGIN(changelayer ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
|
@ -0,0 +1,390 @@
|
||||
// changelayer plugin
|
||||
// allows changing the material type of geological layers
|
||||
|
||||
// some headers required for a plugin. Nothing special, just the basics.
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
|
||||
// DF data structure definition headers
|
||||
#include "DataDefs.h"
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/Materials.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Gui.h"
|
||||
|
||||
#include "TileTypes.h"
|
||||
|
||||
#include "df/world_data.h"
|
||||
#include "df/world_geo_biome.h"
|
||||
#include "df/world_geo_layer.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using namespace std;
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
using df::global::world;
|
||||
using df::global::cursor;
|
||||
|
||||
const string changelayer_help =
|
||||
" Allows to change the material of whole geology layers.\n"
|
||||
" Can have impact on all surrounding regions, not only your embark!\n"
|
||||
" By default changing stone to soil and vice versa is not allowed.\n"
|
||||
" By default changes only the layer at the cursor position.\n"
|
||||
" Note that one layer can stretch across lots of z levels.\n"
|
||||
" By default changes only the geology which is linked to the biome under the\n"
|
||||
" cursor. That geology might be linked to other biomes as well, though.\n"
|
||||
" Mineral veins and gem clusters will stay on the map.\n"
|
||||
" Use 'changevein' for them.\n\n"
|
||||
" tl;dr: You will end up with changing quite big areas in one go.\n\n"
|
||||
"Options (first parameter MUST be the material id):\n"
|
||||
" all_biomes - Change layer for all biomes on your map.\n"
|
||||
" Result may be undesirable since the same layer\n"
|
||||
" can AND WILL be on different z-levels for different biomes.\n"
|
||||
" Use the tool 'probe' to get an idea how layers and biomes\n"
|
||||
" are distributed on your map.\n"
|
||||
" all_layers - Change all layers on your map.\n"
|
||||
" Candy mountain, anyone?\n"
|
||||
" Will make your map quite boring, but tidy.\n"
|
||||
" force - Allow changing stone to soil and vice versa.\n"
|
||||
" !!THIS CAN HAVE WEIRD EFFECTS, USE WITH CARE!!\n"
|
||||
" Note that soil will not be magically replaced with stone.\n"
|
||||
" You will, however, get a stone floor after digging so it\n"
|
||||
" will allow the floor to be engraved.\n"
|
||||
" Note that stone will not be magically replaced with soil.\n"
|
||||
" You will, however, get a soil floor after digging so it\n"
|
||||
" could be helpful for creating farm plots on maps with no soil.\n"
|
||||
" verbose - Give some more details about what is being changed.\n"
|
||||
" trouble - Give some advice for known problems.\n"
|
||||
"Example:\n"
|
||||
" changelayer GRANITE\n"
|
||||
" Convert layer at cursor position into granite.\n"
|
||||
" changelayer SILTY_CLAY force\n"
|
||||
" Convert layer at cursor position into clay even if it's stone.\n"
|
||||
" changelayer MARBLE allbiomes alllayers\n"
|
||||
" Convert all layers of all biomes into marble.\n";
|
||||
|
||||
const string changelayer_trouble =
|
||||
"Known problems with changelayer:\n\n"
|
||||
" Nothing happens, the material stays the old.\n"
|
||||
" Try pausing/unpausing the game or moving the cursor a bit.\n\n"
|
||||
" Weird stuff happening after using the 'force' option.\n"
|
||||
" Change former stone layers back to stone, soil back to soil.\n"
|
||||
" If in doubt, use the 'probe' tool to find tiles with soil walls\n"
|
||||
" and stone layer type or the other way round.\n";
|
||||
|
||||
|
||||
command_result changelayer (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("changelayer");
|
||||
|
||||
// Mandatory init function. If you have some global state, create it here.
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
// Fill the command list with your commands.
|
||||
commands.clear();
|
||||
commands.push_back(PluginCommand(
|
||||
"changelayer", "Change a whole geology layer.",
|
||||
changelayer, false, /* true means that the command can't be used from non-interactive user interface */
|
||||
// Extended help string. Used by CR_WRONG_USAGE and the help command:
|
||||
changelayer_help.c_str()
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
// This is called right before the plugin library is removed from memory.
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
// You *MUST* kill all threads you created before this returns.
|
||||
// If everything fails, just return CR_FAILURE. Your plugin will be
|
||||
// in a zombie state, but things won't crash.
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
bool conversionAllowed(color_ostream &out, MaterialInfo mi, MaterialInfo ml, bool force);
|
||||
|
||||
// no need to spam the "stone to soil" warning more than once
|
||||
// in case multiple biomes and/or layers are going to be changed
|
||||
static bool warned = false;
|
||||
|
||||
command_result changelayer (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
|
||||
string material;
|
||||
bool force = false;
|
||||
bool all_biomes = false;
|
||||
bool all_layers = false;
|
||||
bool verbose = false;
|
||||
warned = false;
|
||||
|
||||
for(size_t i = 0; i < parameters.size();i++)
|
||||
{
|
||||
if(parameters[i] == "help" || parameters[i] == "?")
|
||||
{
|
||||
out.print(changelayer_help.c_str());
|
||||
return CR_OK;
|
||||
}
|
||||
if(parameters[i] == "trouble")
|
||||
{
|
||||
out.print(changelayer_trouble.c_str());
|
||||
return CR_OK;
|
||||
}
|
||||
if(parameters[i] == "force")
|
||||
force = true;
|
||||
if(parameters[i] == "all_biomes")
|
||||
all_biomes = true;
|
||||
if(parameters[i] == "all_layers")
|
||||
all_layers = true;
|
||||
if(parameters[i] == "verbose")
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
if (!Maps::IsValid())
|
||||
{
|
||||
out.printerr("Map is not available!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
if (parameters.empty())
|
||||
{
|
||||
out.printerr("You need to specify a material!\n");
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
material = parameters[0];
|
||||
|
||||
MaterialInfo mat_new;
|
||||
if (!mat_new.findInorganic(material))
|
||||
{
|
||||
out.printerr("No such material!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
// check if specified material is stone or gem or soil
|
||||
if (mat_new.inorganic->material.flags.is_set(material_flags::IS_METAL) ||
|
||||
mat_new.inorganic->material.flags.is_set(material_flags::NO_STONE_STOCKPILE))
|
||||
{
|
||||
out.printerr("Invalid material - you must select a type of stone or gem or soil.\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
MapExtras::MapCache mc;
|
||||
|
||||
int32_t regionX, regionY, regionZ;
|
||||
Maps::getPosition(regionX,regionY,regionZ);
|
||||
|
||||
int32_t cursorX, cursorY, cursorZ;
|
||||
Gui::getCursorCoords(cursorX,cursorY,cursorZ);
|
||||
if(cursorX == -30000)
|
||||
{
|
||||
out.printerr("No cursor; place cursor over tile.\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
DFCoord cursor (cursorX,cursorY,cursorZ);
|
||||
|
||||
uint32_t blockX = cursorX / 16;
|
||||
uint32_t tileX = cursorX % 16;
|
||||
uint32_t blockY = cursorY / 16;
|
||||
uint32_t tileY = cursorY % 16;
|
||||
|
||||
MapExtras::Block * b = mc.BlockAt(cursor/16);
|
||||
if(!b && !b->valid)
|
||||
{
|
||||
out.printerr("No data.\n");
|
||||
return CR_OK;
|
||||
}
|
||||
mapblock40d & block = b->raw;
|
||||
df::tile_designation &des = block.designation[tileX][tileY];
|
||||
|
||||
// get biome and geolayer at cursor position
|
||||
uint32_t biome = des.bits.biome;
|
||||
uint32_t layer = des.bits.geolayer_index;
|
||||
if(verbose)
|
||||
{
|
||||
out << "biome: " << biome << endl
|
||||
<< "geolayer: " << layer << endl;
|
||||
}
|
||||
|
||||
|
||||
// there is no Maps::WriteGeology or whatever, and I didn't want to mess with the library and add it
|
||||
// so I copied the stuff which reads the geology information and modified it to be able to change it
|
||||
|
||||
// no need to touch the same geology more than once
|
||||
// though it wouldn't matter much since there is not much data to be processed
|
||||
vector<uint16_t> v_geoprocessed;
|
||||
v_geoprocessed.clear();
|
||||
|
||||
// iterate over 8 surrounding regions + local region
|
||||
for (int i = eNorthWest; i < eBiomeCount; i++)
|
||||
{
|
||||
if(verbose)
|
||||
out << "---Biome: " << i;
|
||||
if(!all_biomes && i!=biome)
|
||||
{
|
||||
if(verbose)
|
||||
out << "-skipping" << endl;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(verbose)
|
||||
out << "-checking" << endl;
|
||||
}
|
||||
|
||||
// check against worldmap boundaries, fix if needed
|
||||
// regionX is in embark squares
|
||||
// regionX/16 is in 16x16 embark square regions
|
||||
// i provides -1 .. +1 offset from the current region
|
||||
int bioRX = world->map.region_x / 16 + ((i % 3) - 1);
|
||||
if (bioRX < 0) bioRX = 0;
|
||||
if (bioRX >= world->world_data->world_width) bioRX = world->world_data->world_width - 1;
|
||||
int bioRY = world->map.region_y / 16 + ((i / 3) - 1);
|
||||
if (bioRY < 0) bioRY = 0;
|
||||
if (bioRY >= world->world_data->world_height) bioRY = world->world_data->world_height - 1;
|
||||
|
||||
// get index into geoblock vector
|
||||
uint16_t geoindex = world->world_data->region_map[bioRX][bioRY].geo_index;
|
||||
|
||||
if(verbose)
|
||||
out << "geoindex: " << geoindex << endl;
|
||||
|
||||
bool skip = false;
|
||||
for(int g=0; g<v_geoprocessed.size(); g++)
|
||||
{
|
||||
if(v_geoprocessed.at(g)==geoindex)
|
||||
{
|
||||
if(verbose)
|
||||
out << "already processed" << endl;
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(skip)
|
||||
continue;
|
||||
|
||||
v_geoprocessed.push_back(geoindex);
|
||||
|
||||
/// geology blocks have a vector of layer descriptors
|
||||
// get the vector with pointer to layers
|
||||
df::world_geo_biome *geo_biome = df::world_geo_biome::find(geoindex);
|
||||
if (!geo_biome)
|
||||
{
|
||||
if(verbose)
|
||||
out << "no geology found here." << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
vector <df::world_geo_layer*> &geolayers = geo_biome->layers;
|
||||
|
||||
// complain if layer is out of range
|
||||
// geology has up to 16 layers currently, but size can be < 15 sometimes!
|
||||
if(layer >= geolayers.size() || layer < 0)
|
||||
{
|
||||
if(verbose)
|
||||
out << "layer out of range!";
|
||||
continue;
|
||||
}
|
||||
|
||||
// now let's actually write the new mat id to the layer(s)
|
||||
if(all_layers)
|
||||
{
|
||||
for (size_t j = 0; j < geolayers.size(); j++)
|
||||
{
|
||||
MaterialInfo mat_old;
|
||||
mat_old.decode(0, geolayers[j]->mat_index);
|
||||
if(conversionAllowed(out, mat_new, mat_old, force))
|
||||
{
|
||||
if(verbose)
|
||||
out << "changing geolayer " << j
|
||||
<< " from " << mat_old.getToken()
|
||||
<< " to " << mat_new.getToken()
|
||||
<< endl;
|
||||
geolayers[j]->mat_index = mat_new.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MaterialInfo mat_old;
|
||||
mat_old.decode(0, geolayers[layer]->mat_index);
|
||||
if(conversionAllowed(out, mat_new, mat_old, force))
|
||||
{
|
||||
if(verbose)
|
||||
out << "changing geolayer " << layer
|
||||
<< " from " << mat_old.getToken()
|
||||
<< " to " << mat_new.getToken()
|
||||
<< endl;
|
||||
geolayers[layer]->mat_index = mat_new.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.print("Done.\n");
|
||||
|
||||
// Give control back to DF.
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
// check if user tries to convert soil <-> stone
|
||||
// throw some warning if he does
|
||||
bool conversionAllowed(color_ostream &out, MaterialInfo mat_new, MaterialInfo mat_old, bool force)
|
||||
{
|
||||
// check if current layer mat is stone or soil:
|
||||
// by default don't allow to turn stone to soil and vice versa
|
||||
bool unsafe = false;
|
||||
bool allowed = true;
|
||||
|
||||
// throw warning if user wants to change soil to stone or vice versa
|
||||
// while it does work it might be unsafe and can have weird results
|
||||
// you can't simply convert stone to soil, the rock walls will remain (same the other way round)
|
||||
// the floor will turn into soil, though, so it might be useful for creating farm plots
|
||||
// therefore it's not completely forbidden and can be enabled by the 'force' option
|
||||
|
||||
if ( mat_new.inorganic->flags.is_set(inorganic_flags::SOIL_ANY)
|
||||
&& !mat_old.inorganic->flags.is_set(inorganic_flags::SOIL_ANY))
|
||||
{
|
||||
if(!warned)
|
||||
{
|
||||
out << "Changing a stone layer into soil is probably not wise." << endl
|
||||
<< "The stone will remain and you get a soil floor after digging." << endl;
|
||||
}
|
||||
unsafe = true;
|
||||
}
|
||||
else if ( !mat_new.inorganic->flags.is_set(inorganic_flags::SOIL_ANY)
|
||||
&& mat_old.inorganic->flags.is_set(inorganic_flags::SOIL_ANY))
|
||||
{
|
||||
if(!warned)
|
||||
{
|
||||
out << "Changing a soil layer into stone is probably not wise." << endl
|
||||
<< "The soil will remain and you only get a stone floor after digging." << endl;
|
||||
}
|
||||
unsafe = true;
|
||||
}
|
||||
|
||||
if(unsafe)
|
||||
{
|
||||
if(force)
|
||||
{
|
||||
out << "You've been warned, good luck." << endl;
|
||||
allowed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!warned)
|
||||
{
|
||||
out << "Use the option 'force' if you REALLY want to do that." << endl
|
||||
<< "Weird things can happen with your map, so save your game before trying!" << endl
|
||||
<< "Example: 'changelayer GRANITE force'" << endl;
|
||||
warned = true;
|
||||
}
|
||||
allowed = false;
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
Loading…
Reference in New Issue