diff --git a/src/landscape.cpp b/src/landscape.cpp index 041359a632..13e6479700 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1072,6 +1072,15 @@ static bool FindSpring(TileIndex tile, void *user_data) return true; } +struct MakeLakeData { + TileIndex centre; ///< Lake centre tile + uint height; ///< Lake height + int max_distance; ///< Max radius + int secondary_axis_scale; ///< Multiplier for ellipse narrow axis, 16 bit fixed point + int sin_fp; ///< sin of ellipse rotation angle, 16 bit fixed point + int cos_fp; ///< cos of ellipse rotation angle, 16 bit fixed point +}; + /** * Make a connected lake; fill all tiles in the circular tile search that are connected. * @param tile The tile to consider for lake making. @@ -1080,10 +1089,29 @@ static bool FindSpring(TileIndex tile, void *user_data) */ static bool MakeLake(TileIndex tile, void *user_data) { - uint height = *(uint*)user_data; - if (!IsValidTile(tile) || TileHeight(tile) != height || !IsTileFlat(tile)) return false; + const MakeLakeData *data = (const MakeLakeData *)user_data; + if (!IsValidTile(tile) || TileHeight(tile) != data->height || !IsTileFlat(tile)) return false; if (_settings_game.game_creation.landscape == LT_TROPIC && GetTropicZone(tile) == TROPICZONE_DESERT) return false; + /* Offset from centre tile */ + const int64 x_delta = (int)TileX(tile) - (int)TileX(data->centre); + const int64 y_delta = (int)TileY(tile) - (int)TileY(data->centre); + + /* Rotate to new coordinate system */ + const int64 a_delta = (x_delta * data->cos_fp + y_delta * data->sin_fp) >> 8; + const int64 b_delta = (-x_delta * data->sin_fp + y_delta * data->cos_fp) >> 8; + + int max_distance = data->max_distance; + if (max_distance >= 6) { + /* Vary radius a bit for larger lakes */ + uint coord = (std::abs(x_delta) > std::abs(y_delta)) ? TileY(tile) : TileX(tile); + static const int8 offset_fuzz[4] = { 0, 1, 0, -1 }; + max_distance += offset_fuzz[(coord / 3) & 3]; + } + + /* Check if inside ellipse */ + if ((a_delta * a_delta) + ((data->secondary_axis_scale * b_delta * b_delta) >> 16) > ((int64)(max_distance * max_distance) << 16)) return false; + for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { TileIndex t2 = tile + TileOffsByDiagDir(d); if (IsWaterTile(t2)) { @@ -1280,10 +1308,25 @@ static bool FlowRiver(TileIndex spring, TileIndex begin) CircularTileSearch(&lakeCenter, _settings_game.game_creation.river_tropics_width, RiverModifyDesertZone, nullptr); lakeCenter = end; uint range = RandomRange(_settings_game.game_creation.lake_size) + 3; - CircularTileSearch(&lakeCenter, range, MakeLake, &height); + + MakeLakeData data; + data.centre = lakeCenter; + data.height = height; + data.max_distance = range / 2; + + /* Square of ratio of ellipse dimensions: 1 to 5 (16 bit fixed point) */ + data.secondary_axis_scale = (1 << 16) + RandomRange(1 << 18); + + /* Range from -1 to 1 (16 bit fixed point) */ + data.sin_fp = RandomRange(1 << 17) - (1 << 16); + + /* sin^2 + cos^2 = 1 */ + data.cos_fp = IntSqrt64(((int64)1 << 32) - ((int64)data.sin_fp * (int64)data.sin_fp)); + + CircularTileSearch(&lakeCenter, range, MakeLake, &data); /* Call the search a second time so artefacts from going circular in one direction get (mostly) hidden. */ lakeCenter = end; - CircularTileSearch(&lakeCenter, range, MakeLake, &height); + CircularTileSearch(&lakeCenter, range, MakeLake, &data); found = true; } }