From b982e97159076a7f69c96639d14764dd7006b1c2 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Sat, 27 Jun 2026 22:35:38 +0200 Subject: [PATCH 1/3] GameDataLoader kind --- CMap.cpp | 327 ++++++++++++++------------------------------------- CSurface.cpp | 31 +++-- 2 files changed, 112 insertions(+), 246 deletions(-) diff --git a/CMap.cpp b/CMap.cpp index 75f50d7..d6b0708 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -1281,7 +1281,6 @@ void CMap::render() static const TerrainDesc* getTerrainDesc(const bobMAP& map, Uint8 rawTextureId) { - // Mask out harbour bit const Uint8 s2Id = rawTextureId & ~0x40; if(s2Id < map.s2IdToTerrain.size()) { @@ -1292,6 +1291,24 @@ static const TerrainDesc* getTerrainDesc(const bobMAP& map, Uint8 rawTextureId) return nullptr; } +static bool nodeIsMountain(const bobMAP& map, const MapNode& node, bool checkBoth = true) +{ + const auto* rsu = getTerrainDesc(map, node.rsuTexture); + const auto* usd = getTerrainDesc(map, node.usdTexture); + if(checkBoth) + return rsu && usd && rsu->kind == TerrainKind::Mountain && usd->kind == TerrainKind::Mountain; + return (rsu && rsu->kind == TerrainKind::Mountain) || (usd && usd->kind == TerrainKind::Mountain); +} + +static bool nodeHasTerrainFlag(const bobMAP& map, const MapNode& node, ETerrain flag, bool checkBoth = true) +{ + const auto* rsu = getTerrainDesc(map, node.rsuTexture); + const auto* usd = getTerrainDesc(map, node.usdTexture); + if(checkBoth) + return rsu && usd && rsu->Is(flag) && usd->Is(flag); + return (rsu && rsu->Is(flag)) || (usd && usd->Is(flag)); +} + static void getTriangleColor(const bobMAP& map, Uint8 rawTextureId, Sint16& r, Sint16& g, Sint16& b) { const auto* desc = getTerrainDesc(map, rawTextureId); @@ -1722,13 +1739,9 @@ void CMap::modifyHeightMakeBigHouse(Position pos) } // remove harbour if there is one - if(middleVertex.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || middleVertex.rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) + if((middleVertex.rsuTexture & 0x40) && getTerrainDesc(*map, middleVertex.rsuTexture) + && getTerrainDesc(*map, middleVertex.rsuTexture)->kind == TerrainKind::Land + && getTerrainDesc(*map, middleVertex.rsuTexture)->Is(ETerrain::Buildable)) { middleVertex.rsuTexture &= ~0x40; } @@ -1822,10 +1835,8 @@ void CMap::modifyTexture(Position pos, bool rsu, bool usd) void CMap::modifyTextureMakeHarbour(Position pos) { MapNode& vertex = map->getVertex(pos.x, pos.y); - if(vertex.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 || vertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW1 - || vertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW2 || vertex.rsuTexture == TRIANGLE_TEXTURE_MEADOW3 - || vertex.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 || vertex.rsuTexture == TRIANGLE_TEXTURE_FLOWER - || vertex.rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW) + const auto* desc = getTerrainDesc(*map, vertex.rsuTexture); + if(desc && desc->kind == TerrainKind::Land && desc->Is(ETerrain::Buildable)) { vertex.rsuTexture |= 0x40; } @@ -2029,8 +2040,8 @@ void CMap::modifyBuild(Position pos) // calculate the building using the height of the vertices // this building is a mine - if(curVertex.rsuTexture == TRIANGLE_TEXTURE_MINING1 || curVertex.rsuTexture == TRIANGLE_TEXTURE_MINING2 - || curVertex.rsuTexture == TRIANGLE_TEXTURE_MINING3 || curVertex.rsuTexture == TRIANGLE_TEXTURE_MINING4) + if(getTerrainDesc(*map, curVertex.rsuTexture) + && getTerrainDesc(*map, curVertex.rsuTexture)->kind == TerrainKind::Mountain) { building = 0x05; // test vertex lower right @@ -2113,13 +2124,16 @@ void CMap::modifyBuild(Position pos) // test if there is snow or lava at the vertex or around the vertex and touching the vertex (first section) if(building > 0x00) { - if(mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_LAVA || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_LAVA || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_LAVA) + auto isSnowOrLava = [&](const MapNode& node) { + return (getTerrainDesc(*map, node.rsuTexture) + && (getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Snow + || getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Lava)) + || (getTerrainDesc(*map, node.usdTexture) + && (getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Snow + || getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Lava)); + }; + if(isSnowOrLava(*mapVertices[0]) || isSnowOrLava(*mapVertices[1]) || isSnowOrLava(*mapVertices[2]) + || isSnowOrLava(*mapVertices[3])) { building = 0x00; } @@ -2128,11 +2142,15 @@ void CMap::modifyBuild(Position pos) // test if there is snow or lava on the right side (RSU), in lower left (USD) or in lower right (first section) if(building > 0x01) { - if(mapVertices[4]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[4]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[5]->usdTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[5]->usdTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[6]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[6]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[6]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[6]->usdTexture == TRIANGLE_TEXTURE_LAVA) + auto isSnowOrLava2 = [&](const MapNode& node) { + return (getTerrainDesc(*map, node.rsuTexture) + && (getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Snow + || getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Lava)) + || (getTerrainDesc(*map, node.usdTexture) + && (getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Snow + || getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Lava)); + }; + if(isSnowOrLava2(*mapVertices[4]) || isSnowOrLava2(*mapVertices[5]) || isSnowOrLava2(*mapVertices[6])) { building = 0x01; } @@ -2141,46 +2159,29 @@ void CMap::modifyBuild(Position pos) // test if vertex is surrounded by water or swamp if(building > 0x00) { - if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SWAMP)) + bool v0Shippable = nodeHasTerrainFlag(*map, *mapVertices[0], ETerrain::Shippable, true); + bool v1Shippable = nodeHasTerrainFlag(*map, *mapVertices[1], ETerrain::Shippable, true); + bool v2Shippable = nodeHasTerrainFlag(*map, *mapVertices[2], ETerrain::Shippable, true); + bool v3Shippable = nodeHasTerrainFlag(*map, *mapVertices[3], ETerrain::Shippable, true); + if(v0Shippable && v1Shippable && v2Shippable && v3Shippable) { building = 0x00; - } else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SWAMP)) + } else if(v0Shippable || v1Shippable || v2Shippable || v3Shippable) { building = 0x01; } } - // test if there is steppe at the vertex or touching the vertex + // test if there is non-buildable land at the vertex or touching the vertex if(building > 0x01) { - if(mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE) + auto isNonBuildable = [&](const MapNode& node) { + const auto* rsu = getTerrainDesc(*map, node.rsuTexture); + const auto* usd = getTerrainDesc(*map, node.usdTexture); + return (rsu && !rsu->Is(ETerrain::Buildable)) || (usd && !usd->Is(ETerrain::Buildable)); + }; + if(isNonBuildable(*mapVertices[0]) || isNonBuildable(*mapVertices[1]) || isNonBuildable(*mapVertices[2]) + || isNonBuildable(*mapVertices[3])) { building = 0x01; } @@ -2189,56 +2190,14 @@ void CMap::modifyBuild(Position pos) // test if vertex is surrounded by mining-textures if(building > 0x01) { - if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + bool v0Mtn = nodeIsMountain(*map, *mapVertices[0], true); + bool v1Mtn = nodeIsMountain(*map, *mapVertices[1], true); + bool v2Mtn = nodeIsMountain(*map, *mapVertices[2], true); + bool v3Mtn = nodeIsMountain(*map, *mapVertices[3], true); + if(v0Mtn && v1Mtn && v2Mtn && v3Mtn) { building = 0x05; - } else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + } else if(v0Mtn || v1Mtn || v2Mtn || v3Mtn) { building = 0x01; } @@ -2296,147 +2255,41 @@ void CMap::modifyResource(Position pos) // SPECIAL CASE: test if we should set water only // test if vertex is surrounded by meadow and meadow-like textures - if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR)) + auto isBuildableLand = [&](const MapNode& node) { + const auto* rsu = getTerrainDesc(*map, node.rsuTexture); + const auto* usd = getTerrainDesc(*map, node.usdTexture); + return rsu && usd && rsu->kind == TerrainKind::Land && rsu->Is(ETerrain::Buildable) + && usd->kind == TerrainKind::Land && usd->Is(ETerrain::Buildable); + }; + if(isBuildableLand(*mapVertices[0]) && isBuildableLand(*mapVertices[1]) && isBuildableLand(*mapVertices[2]) + && isBuildableLand(*mapVertices[3])) { curVertex.resource = 0x21; } // SPECIAL CASE: test if we should set fishes only // test if vertex is surrounded by water (first section) and at least one non-water texture in the second section - else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[2]->usdTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[3]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[4]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[4]->usdTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[5]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[5]->usdTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[6]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[6]->usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[7]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[7]).usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[8]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[8]).usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[9]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[10]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[10]).usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[11]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[12]).usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[14]).usdTexture != TRIANGLE_TEXTURE_WATER)) + else if(nodeHasTerrainFlag(*map, *mapVertices[0], ETerrain::Shippable, true) + && nodeHasTerrainFlag(*map, *mapVertices[1], ETerrain::Shippable, true) + && nodeHasTerrainFlag(*map, *mapVertices[2], ETerrain::Shippable, true) + && nodeHasTerrainFlag(*map, *mapVertices[3], ETerrain::Shippable, true) + && (!nodeHasTerrainFlag(*map, *mapVertices[2], ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, *mapVertices[3], ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, *mapVertices[4], ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, *mapVertices[5], ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, *mapVertices[6], ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[7]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[8]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[9]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[10]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[11]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[12]), ETerrain::Shippable, false) + || !nodeHasTerrainFlag(*map, map->getVertex(tempVertices[14]), ETerrain::Shippable, false))) { curVertex.resource = 0x87; } // test if vertex is surrounded by mining textures - else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + else if(nodeIsMountain(*map, *mapVertices[0], true) && nodeIsMountain(*map, *mapVertices[1], true) + && nodeIsMountain(*map, *mapVertices[2], true) && nodeIsMountain(*map, *mapVertices[3], true)) { // check which resource to set if(mode == EDITOR_MODE_RESOURCE_RAISE) diff --git a/CSurface.cpp b/CSurface.cpp index 18cc1ce..acd13af 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -21,6 +21,20 @@ // locking. Was originally called in CGame::Init(). static bool sgeLockOff = (sge_Lock_OFF(), true); +namespace { +const TerrainDesc* getTerrainDesc(const bobMAP& map, Uint8 rawTextureId) +{ + const Uint8 s2Id = rawTextureId & ~0x40; + if(s2Id < map.s2IdToTerrain.size()) + { + const auto idx = map.s2IdToTerrain[s2Id]; + if(idx) + return &global::worldDesc.get(idx); + } + return nullptr; +} +} // namespace + namespace { SDL_Rect rect2SDL_Rect(const Rect& rect) { @@ -853,13 +867,16 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa // draw the triangle // do not shade water and lava - if(texture == TRIANGLE_TEXTURE_WATER || texture == TRIANGLE_TEXTURE_LAVA) + if(getTerrainDesc(myMap, texture) + && (getTerrainDesc(myMap, texture)->kind == TerrainKind::Water + || getTerrainDesc(myMap, texture)->kind == TerrainKind::Lava)) sge_TexturedTrigon(display, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, Surf_Tileset, upper.x, upper.y, left.x, left.y, right.x, right.y); else { // draw special winterland textures with moving water (ice floe textures) - if(type == MAP_WINTERLAND && (texture == TRIANGLE_TEXTURE_SNOW || texture == TRIANGLE_TEXTURE_SWAMP)) + if(type == MAP_WINTERLAND && getTerrainDesc(myMap, texture) + && (getTerrainDesc(myMap, texture)->s2Id == 2 || getTerrainDesc(myMap, texture)->s2Id == 3)) { sge_TexturedTrigon(display, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, Surf_Tileset, upper2.x, upper2.y, left2.x, left2.y, right2.x, right2.y); @@ -1211,13 +1228,9 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa (int)(p2.y - global::bmpArray[MAPPIC_HOUSE_MIDDLE].ny)); break; case 0x04: - if(P2.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || P2.rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) + if((P2.rsuTexture & 0x40) && getTerrainDesc(myMap, P2.rsuTexture) + && getTerrainDesc(myMap, P2.rsuTexture)->kind == TerrainKind::Land + && getTerrainDesc(myMap, P2.rsuTexture)->Is(ETerrain::Buildable)) Draw(display, global::bmpArray[MAPPIC_HOUSE_HARBOUR].surface, (int)(p2.x - global::bmpArray[MAPPIC_HOUSE_HARBOUR].nx), (int)(p2.y - global::bmpArray[MAPPIC_HOUSE_HARBOUR].ny)); From 9fb57d3bfd2743a9d6e80c225335861d81dba3f0 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 19:11:58 +0200 Subject: [PATCH 2/3] Address review feedback --- CMap.cpp | 28 +++++++++++----------------- CSurface.cpp | 12 ++++-------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/CMap.cpp b/CMap.cpp index d6b0708..6af342f 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -1739,9 +1739,7 @@ void CMap::modifyHeightMakeBigHouse(Position pos) } // remove harbour if there is one - if((middleVertex.rsuTexture & 0x40) && getTerrainDesc(*map, middleVertex.rsuTexture) - && getTerrainDesc(*map, middleVertex.rsuTexture)->kind == TerrainKind::Land - && getTerrainDesc(*map, middleVertex.rsuTexture)->Is(ETerrain::Buildable)) + if(middleVertex.rsuTexture & 0x40) { middleVertex.rsuTexture &= ~0x40; } @@ -2040,8 +2038,8 @@ void CMap::modifyBuild(Position pos) // calculate the building using the height of the vertices // this building is a mine - if(getTerrainDesc(*map, curVertex.rsuTexture) - && getTerrainDesc(*map, curVertex.rsuTexture)->kind == TerrainKind::Mountain) + if(const auto* mineDesc = getTerrainDesc(*map, curVertex.rsuTexture); + mineDesc && mineDesc->kind == TerrainKind::Mountain) { building = 0x05; // test vertex lower right @@ -2125,12 +2123,10 @@ void CMap::modifyBuild(Position pos) if(building > 0x00) { auto isSnowOrLava = [&](const MapNode& node) { - return (getTerrainDesc(*map, node.rsuTexture) - && (getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Snow - || getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Lava)) - || (getTerrainDesc(*map, node.usdTexture) - && (getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Snow - || getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Lava)); + const auto* rsu = getTerrainDesc(*map, node.rsuTexture); + const auto* usd = getTerrainDesc(*map, node.usdTexture); + return (rsu && (rsu->kind == TerrainKind::Snow || rsu->kind == TerrainKind::Lava)) + || (usd && (usd->kind == TerrainKind::Snow || usd->kind == TerrainKind::Lava)); }; if(isSnowOrLava(*mapVertices[0]) || isSnowOrLava(*mapVertices[1]) || isSnowOrLava(*mapVertices[2]) || isSnowOrLava(*mapVertices[3])) @@ -2143,12 +2139,10 @@ void CMap::modifyBuild(Position pos) if(building > 0x01) { auto isSnowOrLava2 = [&](const MapNode& node) { - return (getTerrainDesc(*map, node.rsuTexture) - && (getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Snow - || getTerrainDesc(*map, node.rsuTexture)->kind == TerrainKind::Lava)) - || (getTerrainDesc(*map, node.usdTexture) - && (getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Snow - || getTerrainDesc(*map, node.usdTexture)->kind == TerrainKind::Lava)); + const auto* rsu = getTerrainDesc(*map, node.rsuTexture); + const auto* usd = getTerrainDesc(*map, node.usdTexture); + return (rsu && (rsu->kind == TerrainKind::Snow || rsu->kind == TerrainKind::Lava)) + || (usd && (usd->kind == TerrainKind::Snow || usd->kind == TerrainKind::Lava)); }; if(isSnowOrLava2(*mapVertices[4]) || isSnowOrLava2(*mapVertices[5]) || isSnowOrLava2(*mapVertices[6])) { diff --git a/CSurface.cpp b/CSurface.cpp index acd13af..2602c9f 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -867,16 +867,14 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa // draw the triangle // do not shade water and lava - if(getTerrainDesc(myMap, texture) - && (getTerrainDesc(myMap, texture)->kind == TerrainKind::Water - || getTerrainDesc(myMap, texture)->kind == TerrainKind::Lava)) + if(const auto* terrainDesc = getTerrainDesc(myMap, texture); + terrainDesc && (terrainDesc->kind == TerrainKind::Water || terrainDesc->kind == TerrainKind::Lava)) sge_TexturedTrigon(display, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, Surf_Tileset, upper.x, upper.y, left.x, left.y, right.x, right.y); else { // draw special winterland textures with moving water (ice floe textures) - if(type == MAP_WINTERLAND && getTerrainDesc(myMap, texture) - && (getTerrainDesc(myMap, texture)->s2Id == 2 || getTerrainDesc(myMap, texture)->s2Id == 3)) + if(type == MAP_WINTERLAND && (texture == TRIANGLE_TEXTURE_SNOW || texture == TRIANGLE_TEXTURE_SWAMP)) { sge_TexturedTrigon(display, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, Surf_Tileset, upper2.x, upper2.y, left2.x, left2.y, right2.x, right2.y); @@ -1228,9 +1226,7 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa (int)(p2.y - global::bmpArray[MAPPIC_HOUSE_MIDDLE].ny)); break; case 0x04: - if((P2.rsuTexture & 0x40) && getTerrainDesc(myMap, P2.rsuTexture) - && getTerrainDesc(myMap, P2.rsuTexture)->kind == TerrainKind::Land - && getTerrainDesc(myMap, P2.rsuTexture)->Is(ETerrain::Buildable)) + if(P2.rsuTexture & 0x40) Draw(display, global::bmpArray[MAPPIC_HOUSE_HARBOUR].surface, (int)(p2.x - global::bmpArray[MAPPIC_HOUSE_HARBOUR].nx), (int)(p2.y - global::bmpArray[MAPPIC_HOUSE_HARBOUR].ny)); From 46a11a46224cfd07f65586b2a654073f6a9a2422 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 19:30:04 +0200 Subject: [PATCH 3/3] Fix building help overlay --- CMap.cpp | 220 +++++++++++++++++++++++-------------------------------- 1 file changed, 91 insertions(+), 129 deletions(-) diff --git a/CMap.cpp b/CMap.cpp index 6af342f..dd63715 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -105,6 +105,29 @@ void CMap::constructMap(const boost::filesystem::path& filepath, int width, int // load the right MAP0x.LST for all pictures loadMapPics(); + // Populate s2IdToTerrain mapping before building calculation (needed by getTerrainDesc) + { + DescIdx lt(0); + for(DescIdx i(0); i.value < global::worldDesc.landscapes.size(); i.value++) + { + if(global::worldDesc.get(i).s2Id == map->type) + { + lt = i; + break; + } + } + for(DescIdx i(0); i.value < global::worldDesc.terrain.size(); i.value++) + { + const TerrainDesc& t = global::worldDesc.get(i); + if(t.landscape == lt) + { + if(map->s2IdToTerrain.size() <= t.s2Id) + map->s2IdToTerrain.resize(t.s2Id + 1); + map->s2IdToTerrain[t.s2Id] = i; + } + } + } + CSurface::get_nodeVectors(*map); // for safety recalculate build and shadow data and test if fishes and water is correct @@ -197,26 +220,6 @@ void CMap::constructMap(const boost::filesystem::path& filepath, int width, int HorizontalMovementLocked = false; VerticalMovementLocked = false; - - DescIdx lt(0); - for(DescIdx i(0); i.value < global::worldDesc.landscapes.size(); i.value++) - { - if(global::worldDesc.get(i).s2Id == map->type) - { - lt = i; - break; - } - } - for(DescIdx i(0); i.value < global::worldDesc.terrain.size(); i.value++) - { - const TerrainDesc& t = global::worldDesc.get(i); - if(t.landscape == lt) - { - if(map->s2IdToTerrain.size() <= t.s2Id) - map->s2IdToTerrain.resize(t.s2Id + 1); - map->s2IdToTerrain[t.s2Id] = i; - } - } } void CMap::destructMap() { @@ -2036,45 +2039,82 @@ void CMap::modifyBuild(Position pos) for(unsigned i = 0; i < mapVertices.size(); i++) mapVertices[i] = &map->getVertex(tempVertices[i]); - // calculate the building using the height of the vertices - // this building is a mine - if(const auto* mineDesc = getTerrainDesc(*map, curVertex.rsuTexture); - mineDesc && mineDesc->kind == TerrainKind::Mountain) - { - building = 0x05; - // test vertex lower right - const auto tmpHeight = mapVertices[6]->h; - if(tmpHeight - height >= 0x04) - building = 0x01; - } - // not a mine - else - { - building = 0x04; - // test the whole section - for(int i = 0; i < 6; i++) + // Determine building quality from terrain following s25client's BQCalculator: + // Check the 6 terrain triangles around the point (same layout as World::GetTerrainsAround): + // {nwNode.t1, nwNode.t2, neNode.t1, curNode.t2, curNode.t1, wNode.t2} + // where t1 = rsuTexture, t2 = usdTexture + int buildingHits = 0, mineHits = 0, flagHits = 0; + bool danger = false; + + auto checkTerrainBQ = [&](const TerrainDesc* desc) { + if(!desc) + return; + switch(desc->GetBQ()) { - auto tmpHeight = mapVertices[i]->h; - if(height - tmpHeight >= 0x04 || tmpHeight - height >= 0x04) - building = 0x01; + case TerrainBQ::Castle: ++buildingHits; break; + case TerrainBQ::Mine: ++mineHits; break; + case TerrainBQ::Flag: ++flagHits; break; + case TerrainBQ::Danger: danger = true; break; + default: break; // Nothing } + }; - // test vertex lower right - auto tmpHeight = mapVertices[6]->h; - if(height - tmpHeight >= 0x04 || tmpHeight - height >= 0x02) - building = 0x01; + checkTerrainBQ(getTerrainDesc(*map, mapVertices[1]->rsuTexture)); // nwNode.t1 + checkTerrainBQ(getTerrainDesc(*map, mapVertices[1]->usdTexture)); // nwNode.t2 + checkTerrainBQ(getTerrainDesc(*map, mapVertices[2]->rsuTexture)); // neNode.t1 + checkTerrainBQ(getTerrainDesc(*map, mapVertices[0]->usdTexture)); // curNode.t2 + checkTerrainBQ(getTerrainDesc(*map, mapVertices[0]->rsuTexture)); // curNode.t1 + checkTerrainBQ(getTerrainDesc(*map, mapVertices[3]->usdTexture)); // wNode.t2 - // now test the second section around the vertex - if(building > 0x02) + if(danger) + building = 0x00; + else if(mineHits == 6) + building = 0x05; + else if(buildingHits == 6) + building = 0x04; + else if(buildingHits || mineHits || flagHits) + building = 0x01; + else + building = 0x00; + + // Now reduce BQ based on altitude (matching s25client altitude checks) + if(building == 0x04) // Castle + { + // flag point (SE neighbour) more than 1 higher? -> Flag + if(mapVertices[6]->h > height + 1) + building = 0x01; + else { - // test the whole section - for(int i = 7; i < 19; i++) + // Direct neighbours: Flag for altitude difference > 3 + for(int i = 1; i < 7; i++) { - tmpHeight = map->getVertex(tempVertices[i]).h; - if(height - tmpHeight >= 0x03 || tmpHeight - height >= 0x03) - building = 0x02; + const auto tmpHeight = mapVertices[i]->h; + if(height > tmpHeight + 3 || tmpHeight > height + 3) + { + building = 0x01; + break; + } + } + + if(building == 0x04) + { + // Radius-2 neighbours: Hut (small house) for altitude difference > 2 + for(int i = 7; i < 19; i++) + { + const auto tmpHeight = map->getVertex(tempVertices[i]).h; + if(height > tmpHeight + 2 || tmpHeight > height + 2) + { + building = 0x02; + break; + } + } } } + } else if(building == 0x05) // Mine + { + // Mines only possible till altitude diff of 3 to SE neighbour + if(mapVertices[6]->h > height + 3) + building = 0x01; } // test if there is an object AROUND the vertex (trees or granite) @@ -2119,84 +2159,6 @@ void CMap::modifyBuild(Position pos) } } - // test if there is snow or lava at the vertex or around the vertex and touching the vertex (first section) - if(building > 0x00) - { - auto isSnowOrLava = [&](const MapNode& node) { - const auto* rsu = getTerrainDesc(*map, node.rsuTexture); - const auto* usd = getTerrainDesc(*map, node.usdTexture); - return (rsu && (rsu->kind == TerrainKind::Snow || rsu->kind == TerrainKind::Lava)) - || (usd && (usd->kind == TerrainKind::Snow || usd->kind == TerrainKind::Lava)); - }; - if(isSnowOrLava(*mapVertices[0]) || isSnowOrLava(*mapVertices[1]) || isSnowOrLava(*mapVertices[2]) - || isSnowOrLava(*mapVertices[3])) - { - building = 0x00; - } - } - - // test if there is snow or lava on the right side (RSU), in lower left (USD) or in lower right (first section) - if(building > 0x01) - { - auto isSnowOrLava2 = [&](const MapNode& node) { - const auto* rsu = getTerrainDesc(*map, node.rsuTexture); - const auto* usd = getTerrainDesc(*map, node.usdTexture); - return (rsu && (rsu->kind == TerrainKind::Snow || rsu->kind == TerrainKind::Lava)) - || (usd && (usd->kind == TerrainKind::Snow || usd->kind == TerrainKind::Lava)); - }; - if(isSnowOrLava2(*mapVertices[4]) || isSnowOrLava2(*mapVertices[5]) || isSnowOrLava2(*mapVertices[6])) - { - building = 0x01; - } - } - - // test if vertex is surrounded by water or swamp - if(building > 0x00) - { - bool v0Shippable = nodeHasTerrainFlag(*map, *mapVertices[0], ETerrain::Shippable, true); - bool v1Shippable = nodeHasTerrainFlag(*map, *mapVertices[1], ETerrain::Shippable, true); - bool v2Shippable = nodeHasTerrainFlag(*map, *mapVertices[2], ETerrain::Shippable, true); - bool v3Shippable = nodeHasTerrainFlag(*map, *mapVertices[3], ETerrain::Shippable, true); - if(v0Shippable && v1Shippable && v2Shippable && v3Shippable) - { - building = 0x00; - } else if(v0Shippable || v1Shippable || v2Shippable || v3Shippable) - { - building = 0x01; - } - } - - // test if there is non-buildable land at the vertex or touching the vertex - if(building > 0x01) - { - auto isNonBuildable = [&](const MapNode& node) { - const auto* rsu = getTerrainDesc(*map, node.rsuTexture); - const auto* usd = getTerrainDesc(*map, node.usdTexture); - return (rsu && !rsu->Is(ETerrain::Buildable)) || (usd && !usd->Is(ETerrain::Buildable)); - }; - if(isNonBuildable(*mapVertices[0]) || isNonBuildable(*mapVertices[1]) || isNonBuildable(*mapVertices[2]) - || isNonBuildable(*mapVertices[3])) - { - building = 0x01; - } - } - - // test if vertex is surrounded by mining-textures - if(building > 0x01) - { - bool v0Mtn = nodeIsMountain(*map, *mapVertices[0], true); - bool v1Mtn = nodeIsMountain(*map, *mapVertices[1], true); - bool v2Mtn = nodeIsMountain(*map, *mapVertices[2], true); - bool v3Mtn = nodeIsMountain(*map, *mapVertices[3], true); - if(v0Mtn && v1Mtn && v2Mtn && v3Mtn) - { - building = 0x05; - } else if(v0Mtn || v1Mtn || v2Mtn || v3Mtn) - { - building = 0x01; - } - } - // test for headquarters around the point // NOTE: In EDITORMODE don't test AT the point, cause in Original game we need a big house AT the point, otherwise // the game wouldn't set a player there