1
0
Fork 0

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.
pull/13144/head
SamuXarick 2021-01-19 12:08:50 +00:00
parent 4edde7d6de
commit 216216d895
3 changed files with 128 additions and 194 deletions

View File

@ -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<TileIndexDiffC> 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<TileIndex> 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<size_t>(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<TileIndex> 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<size_t>(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();
}
/**

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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