603 lines
16 KiB
C++
603 lines
16 KiB
C++
/*
|
|
https://github.com/peterix/dfhack
|
|
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any
|
|
damages arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
purpose, including commercial applications, and to alter it and
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must
|
|
not claim that you wrote the original software. If you use this
|
|
software in a product, an acknowledgment in the product documentation
|
|
would be appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
must not be misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
*/
|
|
|
|
|
|
#include "Internal.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
#include <cstdlib>
|
|
using namespace std;
|
|
|
|
#include "modules/Maps.h"
|
|
#include "Error.h"
|
|
#include "VersionInfo.h"
|
|
#include "MemAccess.h"
|
|
#include "ModuleFactory.h"
|
|
#include "Core.h"
|
|
|
|
#include "DataDefs.h"
|
|
#include "df/world_data.h"
|
|
#include "df/world_underground_region.h"
|
|
#include "df/world_geo_biome.h"
|
|
#include "df/world_geo_layer.h"
|
|
#include "df/feature_init.h"
|
|
|
|
using namespace DFHack;
|
|
using namespace DFHack::Simple;
|
|
using namespace df::enums;
|
|
using df::global::world;
|
|
|
|
const char * DFHack::sa_feature(df::feature_type index)
|
|
{
|
|
switch(index)
|
|
{
|
|
case feature_type::outdoor_river:
|
|
return "River";
|
|
case feature_type::cave:
|
|
return "Cave";
|
|
case feature_type::pit:
|
|
return "Pit";
|
|
case feature_type::magma_pool:
|
|
return "Magma pool";
|
|
case feature_type::volcano:
|
|
return "Volcano";
|
|
case feature_type::deep_special_tube:
|
|
return "Adamantine deposit";
|
|
case feature_type::deep_surface_portal:
|
|
return "Underworld portal";
|
|
case feature_type::subterranean_from_layer:
|
|
return "Cavern";
|
|
case feature_type::magma_core_from_layer:
|
|
return "Magma sea";
|
|
case feature_type::feature_underworld_from_layer:
|
|
return "Underworld";
|
|
default:
|
|
return "Unknown/Error";
|
|
}
|
|
};
|
|
|
|
bool Maps::IsValid ()
|
|
{
|
|
return (world->map.block_index != NULL);
|
|
}
|
|
|
|
// getter for map size
|
|
void Maps::getSize (uint32_t& x, uint32_t& y, uint32_t& z)
|
|
{
|
|
if (!IsValid())
|
|
throw DFHack::Error::ModuleNotInitialized();
|
|
x = world->map.x_count_block;
|
|
y = world->map.y_count_block;
|
|
z = world->map.z_count_block;
|
|
}
|
|
|
|
// getter for map position
|
|
void Maps::getPosition (int32_t& x, int32_t& y, int32_t& z)
|
|
{
|
|
if (!IsValid())
|
|
throw DFHack::Error::ModuleNotInitialized();
|
|
x = world->map.region_x;
|
|
y = world->map.region_y;
|
|
z = world->map.region_z;
|
|
}
|
|
|
|
/*
|
|
* Block reading
|
|
*/
|
|
|
|
df::map_block *Maps::getBlock (int32_t blockx, int32_t blocky, int32_t blockz)
|
|
{
|
|
if (!IsValid())
|
|
return NULL;
|
|
if ((blockx < 0) || (blocky < 0) || (blockz < 0))
|
|
return NULL;
|
|
if ((blockx >= world->map.x_count_block) || (blocky >= world->map.y_count_block) || (blockz >= world->map.z_count_block))
|
|
return NULL;
|
|
return world->map.block_index[blockx][blocky][blockz];
|
|
}
|
|
|
|
df::map_block *Maps::getBlockAbs (int32_t x, int32_t y, int32_t z)
|
|
{
|
|
if (!IsValid())
|
|
return NULL;
|
|
if ((x < 0) || (y < 0) || (z < 0))
|
|
return NULL;
|
|
if ((x >= world->map.x_count) || (y >= world->map.y_count) || (z >= world->map.z_count))
|
|
return NULL;
|
|
return world->map.block_index[x >> 4][y >> 4][z];
|
|
}
|
|
|
|
bool Maps::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer)
|
|
{
|
|
df::map_block * block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
buffer->position = DFCoord(x,y,z);
|
|
memcpy(buffer->tiletypes,block->tiletype, sizeof(tiletypes40d));
|
|
memcpy(buffer->designation,block->designation, sizeof(designations40d));
|
|
memcpy(buffer->occupancy,block->occupancy, sizeof(occupancies40d));
|
|
memcpy(buffer->biome_indices,block->region_offset, sizeof(block->region_offset));
|
|
buffer->global_feature = block->global_feature;
|
|
buffer->local_feature = block->local_feature;
|
|
buffer->mystery = block->unk2;
|
|
buffer->origin = block;
|
|
buffer->blockflags.whole = block->flags.as_int();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Tiletypes
|
|
*/
|
|
|
|
bool Maps::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(buffer, block->tiletype, sizeof(tiletypes40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(block->tiletype, buffer, sizeof(tiletypes40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Dirty bit
|
|
*/
|
|
bool Maps::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
dirtybit = block->flags.is_set(block_flags::Designated);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
block->flags.set(block_flags::Designated, dirtybit);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/*
|
|
/*
|
|
* Block flags
|
|
*/
|
|
// FIXME: maybe truncates, still bullshit
|
|
bool Maps::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
blockflags.whole = block->flags.as_int();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//FIXME: maybe truncated, still bullshit
|
|
bool Maps::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
return (block->flags = blockflags.whole);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Designations
|
|
*/
|
|
bool Maps::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(buffer, block->designation, sizeof(designations40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(block->designation, buffer, sizeof(designations40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Occupancies
|
|
*/
|
|
bool Maps::ReadOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(buffer, block->occupancy, sizeof(occupancies40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::WriteOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(block->occupancy, buffer, sizeof(occupancies40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Temperatures
|
|
*/
|
|
bool Maps::ReadTemperatures(uint32_t x, uint32_t y, uint32_t z, t_temperatures *temp1, t_temperatures *temp2)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
if(temp1)
|
|
memcpy(temp1, block->temperature_1, sizeof(t_temperatures));
|
|
if(temp2)
|
|
memcpy(temp2, block->temperature_2, sizeof(t_temperatures));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool Maps::WriteTemperatures (uint32_t x, uint32_t y, uint32_t z, t_temperatures *temp1, t_temperatures *temp2)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
if(temp1)
|
|
memcpy(block->temperature_1, temp1, sizeof(t_temperatures));
|
|
if(temp2)
|
|
memcpy(block->temperature_2, temp2, sizeof(t_temperatures));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Region Offsets - used for layer geology
|
|
*/
|
|
bool Maps::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
memcpy(buffer, block->region_offset,sizeof(biome_indices40d));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::GetGlobalFeature(t_feature &feature, int32_t index)
|
|
{
|
|
feature.type = (df::feature_type)-1;
|
|
if (!world->world_data)
|
|
return false;
|
|
|
|
if ((index < 0) || (index >= world->world_data->underground_regions.size()))
|
|
return false;
|
|
|
|
df::feature_init *f = world->world_data->underground_regions[index]->feature_init;
|
|
|
|
feature.discovered = false;
|
|
feature.origin = f;
|
|
feature.type = f->getType();
|
|
f->getMaterial(&feature.main_material, &feature.sub_material);
|
|
return true;
|
|
}
|
|
|
|
bool Maps::GetLocalFeature(t_feature &feature, df::coord2d coord, int32_t index)
|
|
{
|
|
feature.type = (df::feature_type)-1;
|
|
if (!world->world_data)
|
|
return false;
|
|
|
|
// regionX and regionY are in embark squares!
|
|
// we convert to full region tiles
|
|
// this also works in adventure mode
|
|
// region X coord - whole regions
|
|
uint32_t region_x = ( (coord.x / 3) + world->map.region_x ) / 16;
|
|
// region Y coord - whole regions
|
|
uint32_t region_y = ( (coord.y / 3) + world->map.region_y ) / 16;
|
|
|
|
uint32_t bigregion_x = region_x / 16;
|
|
uint32_t bigregion_y = region_y / 16;
|
|
|
|
uint32_t sub_x = region_x % 16;
|
|
uint32_t sub_y = region_y % 16;
|
|
// megaregions = 16x16 squares of regions = 256x256 squares of embark squares
|
|
|
|
// bigregion is 16x16 regions. for each bigregion in X dimension:
|
|
if (!world->world_data->unk_204[bigregion_x][bigregion_y].features)
|
|
return false;
|
|
|
|
vector <df::feature_init *> &features = world->world_data->unk_204[bigregion_x][bigregion_y].features->feature_init[sub_x][sub_y];
|
|
if ((index < 0) || (index >= features.size()))
|
|
return false;
|
|
df::feature_init *f = features[index];
|
|
|
|
feature.discovered = false;
|
|
feature.origin = f;
|
|
feature.type = f->getType();
|
|
f->getMaterial(&feature.main_material, &feature.sub_material);
|
|
return true;
|
|
}
|
|
|
|
bool Maps::ReadFeatures(uint32_t x, uint32_t y, uint32_t z, int32_t & local, int32_t & global)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
local = block->local_feature;
|
|
global = block->global_feature;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::WriteFeatures(uint32_t x, uint32_t y, uint32_t z, const int32_t & local, const int32_t & global)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
block->local_feature = local;
|
|
block->global_feature = global;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::ReadFeatures(uint32_t x, uint32_t y, uint32_t z, t_feature *local, t_feature *global)
|
|
{
|
|
int32_t loc, glob;
|
|
if (!ReadFeatures(x, y, z, loc, glob))
|
|
return false;
|
|
|
|
bool result = true;
|
|
if (global)
|
|
{
|
|
if (glob != -1)
|
|
result &= GetGlobalFeature(*global, glob);
|
|
else
|
|
global->type = (df::feature_type)-1;
|
|
}
|
|
if (local)
|
|
{
|
|
if (loc != -1)
|
|
{
|
|
df::coord2d coord(x,y);
|
|
result &= GetLocalFeature(*local, coord, loc);
|
|
}
|
|
else
|
|
local->type = (df::feature_type)-1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Maps::ReadFeatures(mapblock40d * block, t_feature * local, t_feature * global)
|
|
{
|
|
bool result = true;
|
|
if (global)
|
|
{
|
|
if (block->global_feature != -1)
|
|
result &= GetGlobalFeature(*global, block->global_feature);
|
|
else
|
|
global->type = (df::feature_type)-1;
|
|
}
|
|
if (local)
|
|
{
|
|
if (block->local_feature != -1)
|
|
result &= GetLocalFeature(*local, block->position, block->local_feature);
|
|
else
|
|
local->type = (df::feature_type)-1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Maps::SetBlockLocalFeature(uint32_t x, uint32_t y, uint32_t z, int32_t local)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
block->local_feature = local;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Maps::SetBlockGlobalFeature(uint32_t x, uint32_t y, uint32_t z, int32_t global)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (block)
|
|
{
|
|
block->global_feature = global;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Block events
|
|
*/
|
|
bool Maps::SortBlockEvents(uint32_t x, uint32_t y, uint32_t z,
|
|
vector <df::block_square_event_mineralst *>* veins,
|
|
vector <df::block_square_event_frozen_liquidst *>* ices,
|
|
vector <df::block_square_event_material_spatterst *> *splatter,
|
|
vector <df::block_square_event_grassst *> *grasses,
|
|
vector <df::block_square_event_world_constructionst *> *constructions)
|
|
{
|
|
if (veins)
|
|
veins->clear();
|
|
if (ices)
|
|
ices->clear();
|
|
if (splatter)
|
|
splatter->clear();
|
|
if (grasses)
|
|
grasses->clear();
|
|
if (constructions)
|
|
constructions->clear();
|
|
|
|
df::map_block * block = getBlock(x,y,z);
|
|
if (!block)
|
|
return false;
|
|
|
|
// read all events
|
|
for (size_t i = 0; i < block->block_events.size(); i++)
|
|
{
|
|
df::block_square_event *evt = block->block_events[i];
|
|
switch (evt->getType())
|
|
{
|
|
case block_square_event_type::mineral:
|
|
if (veins)
|
|
veins->push_back((df::block_square_event_mineralst *)evt);
|
|
break;
|
|
case block_square_event_type::frozen_liquid:
|
|
if (ices)
|
|
ices->push_back((df::block_square_event_frozen_liquidst *)evt);
|
|
break;
|
|
case block_square_event_type::material_spatter:
|
|
if (splatter)
|
|
splatter->push_back((df::block_square_event_material_spatterst *)evt);
|
|
break;
|
|
case block_square_event_type::grass:
|
|
if (grasses)
|
|
grasses->push_back((df::block_square_event_grassst *)evt);
|
|
break;
|
|
case block_square_event_type::world_construction:
|
|
if (constructions)
|
|
constructions->push_back((df::block_square_event_world_constructionst *)evt);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Maps::RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which)
|
|
{
|
|
df::map_block * block = getBlock(x,y,z);
|
|
if (!block)
|
|
return false;
|
|
for (size_t i = 0; i < block->block_events.size(); i++)
|
|
{
|
|
if (block->block_events[i] == which)
|
|
{
|
|
delete which;
|
|
block->block_events.erase(block->block_events.begin() + i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Layer geology
|
|
*/
|
|
bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
|
|
{
|
|
if (!world->world_data)
|
|
return false;
|
|
|
|
vector<uint16_t> v_geology[eBiomeCount];
|
|
// iterate over 8 surrounding regions + local region
|
|
for (int i = eNorthWest; i < eBiomeCount; i++)
|
|
{
|
|
// 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;
|
|
|
|
/// 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)
|
|
continue;
|
|
|
|
vector <df::world_geo_layer*> &geolayers = geo_biome->layers;
|
|
|
|
/// layer descriptor has a field that determines the type of stone/soil
|
|
v_geology[i].reserve(geolayers.size());
|
|
|
|
// finally, read the layer matgloss
|
|
for (size_t j = 0; j < geolayers.size(); j++)
|
|
v_geology[i].push_back(geolayers[j]->mat_index);
|
|
}
|
|
|
|
assign.clear();
|
|
assign.reserve(eBiomeCount);
|
|
for (int i = 0; i < eBiomeCount; i++)
|
|
assign.push_back(v_geology[i]);
|
|
return true;
|
|
}
|
|
|
|
bool Maps::ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector<df::plant *>*& plants)
|
|
{
|
|
df::map_block *block = getBlock(x,y,z);
|
|
if (!block)
|
|
return false;
|
|
|
|
plants = &block->plants;
|
|
return true;
|
|
}
|