mirror of https://github.com/OpenTTD/OpenTTD
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
parent
4edde7d6de
commit
216216d895
|
@ -944,45 +944,146 @@ static void GenerateTerrain(int type, uint flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "table/genland.h"
|
|
||||||
|
|
||||||
static void CreateDesertOrRainForest(uint desert_tropic_line)
|
static void CreateDesertOrRainForest(uint desert_tropic_line)
|
||||||
{
|
{
|
||||||
uint update_freq = Map::Size() / 4;
|
static const auto circle_offset_of_radius = [](int16_t r, int32_t r2) {
|
||||||
|
std::vector<TileIndexDiffC> circle;
|
||||||
for (const auto tile : Map::Iterate()) {
|
for (int16_t x = -r; x <= r; ++x) {
|
||||||
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
int32_t my = r2 - x * x;
|
||||||
|
for (int16_t y = 0; y * y <= my; ++y) {
|
||||||
if (!IsValidTile(tile)) continue;
|
if (abs(x) > r || abs(y) > r) continue;
|
||||||
|
circle.push_back(TileIndexDiffC(x, y));
|
||||||
auto allows_desert = [tile, desert_tropic_line](auto &offset) {
|
if (y > 0) circle.push_back(TileIndexDiffC(x, -y));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (uint i = 0; i != TILE_UPDATE_FREQUENCY; i++) {
|
return circle;
|
||||||
if ((i % 64) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
};
|
||||||
|
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()) {
|
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);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,7 +14,6 @@ add_files(
|
||||||
control_codes.h
|
control_codes.h
|
||||||
elrail_data.h
|
elrail_data.h
|
||||||
engines.h
|
engines.h
|
||||||
genland.h
|
|
||||||
heightmap_colours.h
|
heightmap_colours.h
|
||||||
industry_land.h
|
industry_land.h
|
||||||
landscape_sprite.h
|
landscape_sprite.h
|
||||||
|
|
|
@ -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
|
|
Loading…
Reference in New Issue