From 216216d89528f24bd4c3b6bd4064fcdc51f850d3 Mon Sep 17 00:00:00 2001 From: SamuXarick <43006711+SamuXarick@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:08:50 +0000 Subject: [PATCH] Codechange: Algorithmic generation of desert and rainforest - Eliminated the dependency on arrays by implementing a circle algorithm, which mimics the previous behavior. - Optimized the desert conversion process with a faster method that eliminates the need for looping. - Note: While the new method doesn't replicate the exact tile layout of the previous version, it maintains identical terrain features except near the map borders. --- src/landscape.cpp | 155 +++++++++++++++++++++++++++++------- src/table/CMakeLists.txt | 1 - src/table/genland.h | 166 --------------------------------------- 3 files changed, 128 insertions(+), 194 deletions(-) delete mode 100644 src/table/genland.h diff --git a/src/landscape.cpp b/src/landscape.cpp index e4594fb4d3..bee7933e6f 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -944,45 +944,146 @@ static void GenerateTerrain(int type, uint flag) } -#include "table/genland.h" - static void CreateDesertOrRainForest(uint desert_tropic_line) { - uint update_freq = Map::Size() / 4; - - for (const auto tile : Map::Iterate()) { - if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); - - if (!IsValidTile(tile)) continue; - - auto allows_desert = [tile, desert_tropic_line](auto &offset) { - TileIndex t = AddTileIndexDiffCWrap(tile, offset); - return t == INVALID_TILE || (TileHeight(t) < desert_tropic_line && !IsTileType(t, MP_WATER)); - }; - if (std::all_of(std::begin(_make_desert_or_rainforest_data), std::end(_make_desert_or_rainforest_data), allows_desert)) { - SetTropicZone(tile, TROPICZONE_DESERT); + static const auto circle_offset_of_radius = [](int16_t r, int32_t r2) { + std::vector circle; + for (int16_t x = -r; x <= r; ++x) { + int32_t my = r2 - x * x; + for (int16_t y = 0; y * y <= my; ++y) { + if (abs(x) > r || abs(y) > r) continue; + circle.push_back(TileIndexDiffC(x, y)); + if (y > 0) circle.push_back(TileIndexDiffC(x, -y)); + } } - } - for (uint i = 0; i != TILE_UPDATE_FREQUENCY; i++) { - if ((i % 64) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + return circle; + }; + static const auto &make_desert_or_rainforest_data = circle_offset_of_radius(6, 51); - RunTileLoop(); - } + size_t update_freq = Map::Size() / 4; + /* Step 1: Iterate over all tiles in the map to set their tropic zones. + * Tiles at or above the desert_tropic_line height, or at sea level, are set to TROPICZONE_RAINFOREST. + * Tiles below the desert_tropic_line height are set to TROPICZONE_DESERT. */ + std::vector rainforest_tiles; for (const auto tile : Map::Iterate()) { - if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + if (tile % update_freq == 0) { + MarkWholeScreenDirty(); + IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + } - if (!IsValidTile(tile)) continue; + const TileType type = GetTileType(tile); + if (type == MP_VOID) continue; + + const uint height = TileHeight(tile); + if (height >= desert_tropic_line || type == MP_WATER) { + /* Only add tiles at the edge of the desert_tropic_line or adjacent to water to the rainforest_tiles vector. */ + if (height == desert_tropic_line && type != MP_WATER) rainforest_tiles.push_back(tile); + if (type == MP_WATER) { + for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) { + const TileIndex neighbour_tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDiagDir(dir)); + + if (neighbour_tile == INVALID_TILE) continue; + if (!IsTileType(neighbour_tile, MP_CLEAR)) continue; + + rainforest_tiles.push_back(tile); + break; + } + } - auto allows_rainforest = [tile](auto &offset) { - TileIndex t = AddTileIndexDiffCWrap(tile, offset); - return t == INVALID_TILE || !IsTileType(t, MP_CLEAR) || !IsClearGround(t, CLEAR_DESERT); - }; - if (std::all_of(std::begin(_make_desert_or_rainforest_data), std::end(_make_desert_or_rainforest_data), allows_rainforest)) { SetTropicZone(tile, TROPICZONE_RAINFOREST); + } else { + SetTropicZone(tile, TROPICZONE_DESERT); + + /* Assume maximum desert density at this step. + * Adjustments will be made in the final step if needed. */ + SetClearGroundDensity(tile, CLEAR_DESERT, 3); } } + + MarkWholeScreenDirty(); + update_freq = std::max(1, (rainforest_tiles.size() / 4) + (((rainforest_tiles.size() % 4) != 0) ? 1 : 0)); + size_t i = 0; + + /* Step 2: Starting from each TROPICZONE_RAINFOREST tile, check neighboring tiles. + * If a neighboring tile is TROPICZONE_DESERT, convert it to TROPICZONE_NORMAL + * to create a buffer zone between the rainforest and the desert. */ + std::vector normal_tiles; + while (!rainforest_tiles.empty()) { + if (i % update_freq == 0) { + IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + MarkWholeScreenDirty(); + } + i++; + + const TileIndex rainforest_tile = rainforest_tiles.back(); + rainforest_tiles.pop_back(); + + for (const auto &offset : make_desert_or_rainforest_data) { + const TileIndex neighbour_tile = AddTileIndexDiffCWrap(rainforest_tile, offset); + + if (neighbour_tile == INVALID_TILE) continue; + if (GetTropicZone(neighbour_tile) != TROPICZONE_DESERT) continue; + + SetTropicZone(neighbour_tile, TROPICZONE_NORMAL); + + /* Speed up the transition from desert to grass. */ + SetClearGroundDensity(neighbour_tile, CLEAR_GRASS, 3); + + normal_tiles.push_back(neighbour_tile); + } + } + + MarkWholeScreenDirty(); + update_freq = std::max(1, (normal_tiles.size() / 4) + (((normal_tiles.size() % 4) != 0) ? 1 : 0)); + i = 0; + + /* Step 3: Process each normal tile collected in step 2: + * Originally, these tiles were TROPICZONE_DESERT (from step 1). + * They were then converted to TROPICZONE_NORMAL (in step 2). + * Now, if none of the surrounding tiles are TROPICZONE_DESERT, + * convert the normal tile to TROPICZONE_RAINFOREST. */ + while (!normal_tiles.empty()) { + if (i % update_freq == 0) { + IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + MarkWholeScreenDirty(); + } + i++; + + const TileIndex normal_tile = normal_tiles.back(); + normal_tiles.pop_back(); + + bool allows_rainforest = true; + for (const auto &offset : make_desert_or_rainforest_data) { + const TileIndex neighbour_tile = AddTileIndexDiffCWrap(normal_tile, offset); + + if (!IsValidTile(neighbour_tile)) continue; + if (GetTropicZone(neighbour_tile) != TROPICZONE_DESERT) continue; + + allows_rainforest = false; + break; + } + + if (!allows_rainforest) { + /* If the normal tile remains as such due to adjacent desert tiles, + * correct their density in this step. */ + for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { + const TileIndex t = normal_tile + TileOffsByDiagDir(dir); + + if (!IsValidTile(t)) continue; + if (GetTropicZone(t) != TROPICZONE_DESERT) continue; + + SetClearGroundDensity(t, CLEAR_DESERT, 1); + } + + continue; + } + + SetTropicZone(normal_tile, TROPICZONE_RAINFOREST); + } + + MarkWholeScreenDirty(); } /** diff --git a/src/table/CMakeLists.txt b/src/table/CMakeLists.txt index 3a615135ef..40875e0960 100644 --- a/src/table/CMakeLists.txt +++ b/src/table/CMakeLists.txt @@ -14,7 +14,6 @@ add_files( control_codes.h elrail_data.h engines.h - genland.h heightmap_colours.h industry_land.h landscape_sprite.h diff --git a/src/table/genland.h b/src/table/genland.h deleted file mode 100644 index 7bc6294ec7..0000000000 --- a/src/table/genland.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file genland.h Table used to generate deserts and/or rain forests. */ - -#define M(x, y) {x, y} - -static const TileIndexDiffC _make_desert_or_rainforest_data[] = { - M(-5, -5), - M(-4, -5), - M(-3, -5), - M(-2, -5), - M(-1, -5), - M( 0, -5), - M( 1, -5), - M( 2, -5), - M( 3, -5), - M( 4, -5), - M( 5, -5), - M(-5, -4), - M(-4, -4), - M(-3, -4), - M(-2, -4), - M(-1, -4), - M( 0, -4), - M( 1, -4), - M( 2, -4), - M( 3, -4), - M( 4, -4), - M( 5, -4), - M(-5, -3), - M(-4, -3), - M(-3, -3), - M(-2, -3), - M(-1, -3), - M( 0, -3), - M( 1, -3), - M( 2, -3), - M( 3, -3), - M( 4, -3), - M( 5, -3), - M(-5, -2), - M(-4, -2), - M(-3, -2), - M(-2, -2), - M(-1, -2), - M( 0, -2), - M( 1, -2), - M( 2, -2), - M( 3, -2), - M( 4, -2), - M( 5, -2), - M(-5, -1), - M(-4, -1), - M(-3, -1), - M(-2, -1), - M(-1, -1), - M( 0, -1), - M( 1, -1), - M( 2, -1), - M( 3, -1), - M( 4, -1), - M( 5, -1), - M(-5, 0), - M(-4, 0), - M(-3, 0), - M(-2, 0), - M(-1, 0), - M( 0, 0), - M( 1, 0), - M( 2, 0), - M( 3, 0), - M( 4, 0), - M( 5, 0), - M(-5, 1), - M(-4, 1), - M(-3, 1), - M(-2, 1), - M(-1, 1), - M( 0, 1), - M( 1, 1), - M( 2, 1), - M( 3, 1), - M( 4, 1), - M( 5, 1), - M(-5, 2), - M(-4, 2), - M(-3, 2), - M(-2, 2), - M(-1, 2), - M( 0, 2), - M( 1, 2), - M( 2, 2), - M( 3, 2), - M( 4, 2), - M( 5, 2), - M(-5, 3), - M(-4, 3), - M(-3, 3), - M(-2, 3), - M(-1, 3), - M( 0, 3), - M( 1, 3), - M( 2, 3), - M( 3, 3), - M( 4, 3), - M( 5, 3), - M(-5, 4), - M(-4, 4), - M(-3, 4), - M(-2, 4), - M(-1, 4), - M( 0, 4), - M( 1, 4), - M( 2, 4), - M( 3, 4), - M( 4, 4), - M( 5, 4), - M(-5, 5), - M(-4, 5), - M(-3, 5), - M(-2, 5), - M(-1, 5), - M( 0, 5), - M( 1, 5), - M( 2, 5), - M( 3, 5), - M( 4, 5), - M( 5, 5), - M( 6, -3), - M(-6, -3), - M(-3, 6), - M(-3, -6), - M( 6, -2), - M(-6, -2), - M(-2, 6), - M(-2, -6), - M( 6, -1), - M(-6, -1), - M(-1, 6), - M(-1, -6), - M( 6, 0), - M(-6, 0), - M( 0, 6), - M( 0, -6), - M( 6, 1), - M(-6, 1), - M( 1, 6), - M( 1, -6), - M( 6, 2), - M(-6, 2), - M( 2, 6), - M( 2, -6), - M( 6, 3), - M(-6, 3), - M( 3, 6), - M( 3, -6) -}; - - - -#undef M