From a45438d17273988fd00cc79896578fc55fbb5b82 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 5 Sep 2014 15:11:13 +0400 Subject: [PATCH] Fix 3dveins crashing because of trees and malformed biome data. The sky in the latest versions has uninitialized biome data, which made the plugin crash on invalid vector index access. Also, trees on ground may still have wrong geolayer inside them like obsidian. --- plugins/3dveins.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index c9e15aae5..9802ff5eb 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -661,6 +661,55 @@ GeoLayer *VeinGenerator::mapLayer(Block *pb, df::coord2d tile) return biome->layers[lidx]; } +static bool isTransientMaterial(df::tiletype tile) +{ + using namespace df::enums::tiletype_material; + + switch (tileMaterial(tile)) + { + case AIR: + case LAVA_STONE: + case PLANT: + case ROOT: + case TREE: + case MUSHROOM: + return true; + + default: + return false; + } +} + +static bool isSkyBlock(Block *b) +{ + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + df::coord2d tile(x,y); + auto dsgn = b->DesignationAt(tile); + auto ttype = b->baseTiletypeAt(tile); + + if (dsgn.bits.subterranean || !dsgn.bits.light || !isTransientMaterial(ttype)) + return false; + } + } + + return true; +} + +static int findTopBlock(MapCache &map, int x, int y) +{ + for (int z = map.maxZ(); z >= 0; z--) + { + Block *b = map.BlockAt(df::coord(x,y,z)); + if (b && b->is_valid() && !isSkyBlock(b)) + return z; + } + + return -1; +} + bool VeinGenerator::scan_tiles() { for (int x = 0; x < size.x; x++) @@ -669,8 +718,10 @@ bool VeinGenerator::scan_tiles() { df::coord2d column(x,y); + int top = findTopBlock(map, x, y); + // First find where layers start and end - for (int z = map.maxZ(); z >= 0; z--) + for (int z = top; z >= 0; z--) { Block *b = map.BlockAt(df::coord(x,y,z)); if (!b || !b->is_valid()) @@ -684,7 +735,7 @@ bool VeinGenerator::scan_tiles() return false; // Collect tile data - for (int z = map.maxZ(); z >= 0; z--) + for (int z = top; z >= 0; z--) { Block *b = map.BlockAt(df::coord(x,y,z)); if (!b || !b->is_valid()) @@ -725,7 +776,7 @@ bool VeinGenerator::scan_layer_depth(Block *b, df::coord2d column, int z) auto &bottom = col_info.bottom_layer[x][y]; auto ttype = b->baseTiletypeAt(tile); - bool obsidian = (tileMaterial(ttype) == tiletype_material::LAVA_STONE); + bool obsidian = isTransientMaterial(ttype); if (top_solid < 0 && !obsidian && isWallTerrain(ttype)) top_solid = z; @@ -974,7 +1025,9 @@ void VeinGenerator::write_tiles() { df::coord2d column(x,y); - for (int z = map.maxZ(); z >= 0; z--) + int top = findTopBlock(map, x, y); + + for (int z = top; z >= 0; z--) { Block *b = map.BlockAt(df::coord(x,y,z)); if (!b || !b->is_valid())