diff --git a/src/landscape.cpp b/src/landscape.cpp index 13e6479700..d6d6ec0b02 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -93,6 +93,15 @@ extern const byte _slope_to_sprite_offset[32] = { */ static SnowLine *_snow_line = nullptr; +/** The current spring during river generation */ +static TileIndex _current_spring = INVALID_TILE; + +/** The current estuary during river generation when one river flows into another */ +static TileIndex _current_estuary = INVALID_TILE; + +/** Whether the current river is a big river that others flow into */ +static bool _is_main_river = false; + byte _cached_snowline = 0; /** @@ -1183,16 +1192,49 @@ static void River_GetNeighbours(AyStar *aystar, OpenListNode *current) } } +/** Callback to widen a river tile. */ +static bool RiverMakeWider(TileIndex tile, void *data) +{ + if (IsValidTile(tile) && !IsWaterTile(tile) && GetTileSlope(tile) == GetTileSlope(*(TileIndex *)data)) { + MakeRiver(tile, Random()); + /* Remove desert directly around the river tile. */ + TileIndex cur_tile = tile; + + MarkTileDirtyByTile(cur_tile); + CircularTileSearch(&cur_tile, _settings_game.game_creation.river_tropics_width, RiverModifyDesertZone, nullptr); + } + return false; +} + /* AyStar callback when an route has been found. */ static void River_FoundEndNode(AyStar *aystar, OpenListNode *current) { + /* Count river length. */ + uint length = 0; + for (PathNode *path = ¤t->path; path != nullptr; path = path->parent) { + length++; + } + + uint cur_pos = 0; + for (PathNode *path = ¤t->path; path != nullptr; path = path->parent, cur_pos++) { TileIndex tile = path->node.tile; if (!IsWaterTile(tile)) { MakeRiver(tile, Random()); + + // Widen river depending on how far we are away from the source. + const uint current_river_length = DistanceManhattan(_current_spring, path->node.tile); + const uint long_river_length = _settings_game.game_creation.min_river_length * 4; + const uint radius = std::min(3u, (current_river_length / (long_river_length / 3u)) + 1u); + MarkTileDirtyByTile(tile); - /* Remove desert directly around the river tile. */ - CircularTileSearch(&tile, _settings_game.game_creation.river_tropics_width, RiverModifyDesertZone, nullptr); + + if (_is_main_river && (radius > 1)) { + CircularTileSearch(&tile, radius + RandomRange(1), RiverMakeWider, (void *)&path->node.tile); + } else { + /* Remove desert directly around the river tile. */ + CircularTileSearch(&tile, _settings_game.game_creation.river_tropics_width, RiverModifyDesertZone, nullptr); + } } } } @@ -1239,15 +1281,24 @@ static void BuildRiver(TileIndex begin, TileIndex end) * Try to flow the river down from a given begin. * @param spring The springing point of the river. * @param begin The begin point we are looking from; somewhere down hill from the spring. + * @param min_river_length The minimum length for the river. * @return True iff a river could/has been built, otherwise false. */ -static bool FlowRiver(TileIndex spring, TileIndex begin) +static bool FlowRiver(TileIndex spring, TileIndex begin, uint min_river_length) { # define SET_MARK(x) marks.insert(x) # define IS_MARKED(x) (marks.find(x) != marks.end()) uint height = TileHeight(begin); - if (IsWaterTile(begin)) return DistanceManhattan(spring, begin) > _settings_game.game_creation.min_river_length; + if (IsWaterTile(begin)) + { + if (GetTileZ(begin) == 0) { + _current_estuary = begin; + _is_main_river = true; + } + + return DistanceManhattan(spring, begin) > min_river_length; + } btree::btree_set marks; SET_MARK(begin); @@ -1281,7 +1332,7 @@ static bool FlowRiver(TileIndex spring, TileIndex begin) if (found) { /* Flow further down hill. */ - found = FlowRiver(spring, end); + found = FlowRiver(spring, end, min_river_length); } else if (count > 32) { /* Maybe we can make a lake. Find the Nth of the considered tiles. */ TileIndex lakeCenter = 0; @@ -1300,7 +1351,7 @@ static bool FlowRiver(TileIndex spring, TileIndex begin) /* We don't want lakes in the desert. */ (_settings_game.game_creation.landscape != LT_TROPIC || _settings_game.game_creation.lakes_allowed_in_deserts || GetTropicZone(lakeCenter) != TROPICZONE_DESERT) && /* We only want a lake if the river is long enough. */ - DistanceManhattan(spring, lakeCenter) > _settings_game.game_creation.min_river_length) { + DistanceManhattan(spring, lakeCenter) > min_river_length) { end = lakeCenter; MakeRiver(lakeCenter, Random()); MarkTileDirtyByTile(lakeCenter); @@ -1345,14 +1396,28 @@ static void CreateRivers() if (amount == 0) return; uint wells = ScaleByMapSize(4 << _settings_game.game_creation.amount_of_rivers); + const uint num_short_rivers = wells - std::max(1u, wells / 10); SetGeneratingWorldProgress(GWP_RIVER, wells + 256 / 64); // Include the tile loop calls below. + for (; wells > num_short_rivers; wells--) { + IncreaseGeneratingWorldProgress(GWP_RIVER); + for (int tries = 0; tries < 512; tries++) { + TileIndex t = RandomTile(); + if (!CircularTileSearch(&t, 8, FindSpring, nullptr)) continue; + _current_spring = t; + _is_main_river = false; + if (FlowRiver(t, t, _settings_game.game_creation.min_river_length * 4)) break; + } + } + for (; wells != 0; wells--) { IncreaseGeneratingWorldProgress(GWP_RIVER); for (int tries = 0; tries < 128; tries++) { TileIndex t = RandomTile(); if (!CircularTileSearch(&t, 8, FindSpring, nullptr)) continue; - if (FlowRiver(t, t)) break; + _current_spring = t; + _is_main_river = false; + if (FlowRiver(t, t, _settings_game.game_creation.min_river_length)) break; } }