|
|
|
|
@@ -29,6 +29,7 @@
|
|
|
|
|
#include "animated_tile_func.h"
|
|
|
|
|
#include "core/random_func.hpp"
|
|
|
|
|
#include "object_base.h"
|
|
|
|
|
#include "tree_cmd.h"
|
|
|
|
|
#include "company_func.h"
|
|
|
|
|
#include "company_gui.h"
|
|
|
|
|
#include "saveload/saveload.h"
|
|
|
|
|
@@ -1045,24 +1046,117 @@ static bool FindSpring(TileIndex tile)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Make a connected lake; fill all tiles in the circular tile search that are connected.
|
|
|
|
|
* @param tile The tile to consider for lake making.
|
|
|
|
|
* Is this a valid tile for the water feature at the end of a river?
|
|
|
|
|
* @param tile The tile to check.
|
|
|
|
|
* @param height The height of the rest of the water feature, which must match.
|
|
|
|
|
* @return True iff this is a valid tile to be part of the river terminus.
|
|
|
|
|
*/
|
|
|
|
|
static bool IsValidRiverTerminusTile(TileIndex tile, uint height)
|
|
|
|
|
{
|
|
|
|
|
if (!IsValidTile(tile) || TileHeight(tile) != height || !IsTileFlat(tile)) return false;
|
|
|
|
|
if (_settings_game.game_creation.landscape == LandscapeType::Tropic && GetTropicZone(tile) == TROPICZONE_DESERT) return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Make a lake centred on the given tile, of a random diameter.
|
|
|
|
|
* @param lake_centre The middle tile of the lake.
|
|
|
|
|
* @param height_lake The height of the lake.
|
|
|
|
|
*/
|
|
|
|
|
static void MakeLake(TileIndex tile, uint height_lake)
|
|
|
|
|
static void MakeLake(TileIndex lake_centre, uint height_lake)
|
|
|
|
|
{
|
|
|
|
|
if (!IsValidTile(tile) || TileHeight(tile) != height_lake || !IsTileFlat(tile)) return;
|
|
|
|
|
if (_settings_game.game_creation.landscape == LandscapeType::Tropic && GetTropicZone(tile) == TROPICZONE_DESERT) return;
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(lake_centre);
|
|
|
|
|
uint diameter = RandomRange(8) + 3;
|
|
|
|
|
|
|
|
|
|
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
|
|
|
|
|
TileIndex t = tile + TileOffsByDiagDir(d);
|
|
|
|
|
if (IsWaterTile(t)) {
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(tile);
|
|
|
|
|
return;
|
|
|
|
|
/* Run the loop twice, so artefacts from going circular in one direction get (mostly) hidden. */
|
|
|
|
|
for (uint loops = 0; loops < 2; ++loops) {
|
|
|
|
|
for (TileIndex tile : SpiralTileSequence(lake_centre, diameter)) {
|
|
|
|
|
if (!IsValidRiverTerminusTile(tile, height_lake)) continue;
|
|
|
|
|
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
|
|
|
|
|
TileIndex t = tile + TileOffsByDiagDir(d);
|
|
|
|
|
if (IsWaterTile(t)) {
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(tile);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Make wetlands around the given tile.
|
|
|
|
|
* @param centre The starting tile.
|
|
|
|
|
* @param height The height of the wetlands.
|
|
|
|
|
* @param river_length The length of the river.
|
|
|
|
|
*/
|
|
|
|
|
static void MakeWetlands(TileIndex centre, uint height, uint river_length)
|
|
|
|
|
{
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(centre);
|
|
|
|
|
|
|
|
|
|
uint diameter = std::max((river_length), 16u);
|
|
|
|
|
|
|
|
|
|
/* Some wetlands have trees planted among the water tiles. */
|
|
|
|
|
bool has_trees = Chance16(1, 2);
|
|
|
|
|
|
|
|
|
|
/* Create the main wetland area. */
|
|
|
|
|
for (TileIndex tile : SpiralTileSequence(centre, diameter)) {
|
|
|
|
|
if (!IsValidRiverTerminusTile(tile, height)) continue;
|
|
|
|
|
|
|
|
|
|
/* Don't make a perfect square, but a circle with a noisy border. */
|
|
|
|
|
uint radius = diameter / 2;
|
|
|
|
|
if ((DistanceSquare(tile, centre) > radius * radius) && Chance16(3, 4)) continue;
|
|
|
|
|
|
|
|
|
|
if (Chance16(1, 3)) {
|
|
|
|
|
/* This tile is water. */
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(tile);
|
|
|
|
|
} else if (IsTileType(tile, MP_CLEAR)) {
|
|
|
|
|
/* This tile is ground, which we always make rough. */
|
|
|
|
|
SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
|
|
|
|
|
/* Maybe place trees? */
|
|
|
|
|
if (has_trees && _settings_game.game_creation.tree_placer != TP_NONE) {
|
|
|
|
|
PlaceTree(tile, Random(), true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Try to end a river at a tile which is not the sea.
|
|
|
|
|
* @param tile The tile to try ending the river at.
|
|
|
|
|
* @param begin The starting tile of the river.
|
|
|
|
|
* @return Whether we succesfully ended the river on the given tile.
|
|
|
|
|
*/
|
|
|
|
|
static bool TryMakeRiverTerminus(TileIndex tile, TileIndex begin)
|
|
|
|
|
{
|
|
|
|
|
if (!IsValidTile(tile)) return false;
|
|
|
|
|
|
|
|
|
|
/* We don't want to end the river at the entry of the valley. */
|
|
|
|
|
if (tile == begin) return false;
|
|
|
|
|
|
|
|
|
|
/* We don't want the river to end in the desert. */
|
|
|
|
|
if (_settings_game.game_creation.landscape == LandscapeType::Tropic && GetTropicZone(tile) == TROPICZONE_DESERT) return false;
|
|
|
|
|
|
|
|
|
|
/* Only end on flat slopes. */
|
|
|
|
|
int height_lake;
|
|
|
|
|
if (!IsTileFlat(tile, &height_lake)) return false;
|
|
|
|
|
|
|
|
|
|
/* Only build at the height of the river. */
|
|
|
|
|
int height_begin = TileHeight(begin);
|
|
|
|
|
if (height_lake != height_begin) return false;
|
|
|
|
|
|
|
|
|
|
/* Checks successful, time to build.
|
|
|
|
|
* Chance of water feature is split evenly between a lake, a wetland with trees, and a wetland with grass. */
|
|
|
|
|
if (Chance16(1, 3)) {
|
|
|
|
|
MakeLake(tile, height_lake);
|
|
|
|
|
} else {
|
|
|
|
|
MakeWetlands(tile, height_lake, DistanceManhattan(tile, begin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is the new end of the river. */
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Widen a river by expanding into adjacent tiles via circular tile search.
|
|
|
|
|
* @param tile The tile to try expanding the river into.
|
|
|
|
|
@@ -1284,31 +1378,12 @@ static std::tuple<bool, bool> FlowRiver(TileIndex spring, TileIndex begin, uint
|
|
|
|
|
} else if (queue.size() > 32) {
|
|
|
|
|
/* Maybe we can make a lake. Find the Nth of the considered tiles. */
|
|
|
|
|
TileIndex lake_centre = queue[RandomRange(static_cast<uint32_t>(queue.size()))];
|
|
|
|
|
int height_lake;
|
|
|
|
|
|
|
|
|
|
if (IsValidTile(lake_centre) &&
|
|
|
|
|
/* We don't want the lake at the entry of the valley. */
|
|
|
|
|
lake_centre != begin &&
|
|
|
|
|
/* We don't want lakes in the desert. */
|
|
|
|
|
(_settings_game.game_creation.landscape != LandscapeType::Tropic || GetTropicZone(lake_centre) != TROPICZONE_DESERT) &&
|
|
|
|
|
/* A river, or lake, can only be built on flat slopes. */
|
|
|
|
|
IsTileFlat(lake_centre, &height_lake) &&
|
|
|
|
|
/* We want the lake to be built at the height of the river. */
|
|
|
|
|
height_lake == height_begin &&
|
|
|
|
|
/* We only want a lake if the river is long enough. */
|
|
|
|
|
DistanceManhattan(spring, lake_centre) > min_river_length) {
|
|
|
|
|
end = lake_centre;
|
|
|
|
|
MakeRiverAndModifyDesertZoneAround(lake_centre);
|
|
|
|
|
uint diameter = RandomRange(8) + 3;
|
|
|
|
|
|
|
|
|
|
/* Run the loop twice, so artefacts from going circular in one direction get (mostly) hidden. */
|
|
|
|
|
for (uint loops = 0; loops < 2; ++loops) {
|
|
|
|
|
for (auto tile : SpiralTileSequence(lake_centre, diameter)) {
|
|
|
|
|
MakeLake(tile, height_lake);
|
|
|
|
|
}
|
|
|
|
|
if (DistanceManhattan(spring, lake_centre) > min_river_length) {
|
|
|
|
|
if (TryMakeRiverTerminus(lake_centre, begin)) {
|
|
|
|
|
/* If successful, this becomes the new end of the river. */
|
|
|
|
|
end = lake_centre;
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|