From 3b18877b87d59b781398e6aa84133efeca843a25 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 28 Dec 2023 21:43:05 +0000 Subject: [PATCH] Fix #11629: AirportGetNearestTown for rotated airports (#11631) Add rotation parameter to AirportGetNearestTown Add wrapper for existing stations Remove unnecessary iterator cloning --- src/script/api/script_airport.cpp | 5 ++-- src/station_cmd.cpp | 46 +++++++++++++++++++------------ src/station_cmd.h | 2 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/script/api/script_airport.cpp b/src/script/api/script_airport.cpp index c0b28408c7..81e24fe0f6 100644 --- a/src/script/api/script_airport.cpp +++ b/src/script/api/script_airport.cpp @@ -137,9 +137,8 @@ if (!as->IsWithinMapBounds(0, tile)) return -1; if (_settings_game.economy.station_noise_level) { - AirportTileTableIterator it(as->table[0], tile); uint dist; - AirportGetNearestTown(as, tile, it, dist); + AirportGetNearestTown(as, as->rotation[0], tile, AirportTileTableIterator(as->table[0], tile), dist); return GetAirportNoiseLevelForDistance(as, dist); } @@ -155,7 +154,7 @@ if (!as->IsWithinMapBounds(0, tile)) return INVALID_TOWN; uint dist; - return AirportGetNearestTown(as, tile, AirportTileTableIterator(as->table[0], tile), dist)->index; + return AirportGetNearestTown(as, as->rotation[0], tile, AirportTileTableIterator(as->table[0], tile), dist)->index; } /* static */ SQInteger ScriptAirport::GetMaintenanceCostFactor(AirportType type) diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 619bf02aea..230e7dac3c 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2303,28 +2303,32 @@ uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance) * Finds the town nearest to given airport. Based on minimal manhattan distance to any airport's tile. * If two towns have the same distance, town with lower index is returned. * @param as airport's description + * @param rotation airport's rotation * @param tile origin tile (top corner of the airport) - * @param it An iterator over all airport tiles + * @param it An iterator over all airport tiles (consumed) * @param[out] mindist Minimum distance to town * @return nearest town to airport */ -Town *AirportGetNearestTown(const AirportSpec *as, TileIndex tile, const TileIterator &it, uint &mindist) +Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist) { assert(Town::GetNumItems() > 0); Town *nearest = nullptr; + auto width = as->size_x; + auto height = as->size_y; + if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height); + uint perimeter_min_x = TileX(tile); uint perimeter_min_y = TileY(tile); - uint perimeter_max_x = perimeter_min_x + as->size_x - 1; - uint perimeter_max_y = perimeter_min_y + as->size_y - 1; + uint perimeter_max_x = perimeter_min_x + width - 1; + uint perimeter_max_y = perimeter_min_y + height - 1; mindist = UINT_MAX - 1; // prevent overflow - std::unique_ptr copy(it.Clone()); - for (TileIndex cur_tile = *copy; cur_tile != INVALID_TILE; cur_tile = ++*copy) { - assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, as->size_x)); - assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, as->size_y)); + for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) { + assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width)); + assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height)); if (TileX(cur_tile) == perimeter_min_x || TileX(cur_tile) == perimeter_max_x || TileY(cur_tile) == perimeter_min_y || TileY(cur_tile) == perimeter_max_y) { Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1); if (t == nullptr) continue; @@ -2341,6 +2345,18 @@ Town *AirportGetNearestTown(const AirportSpec *as, TileIndex tile, const TileIte return nearest; } +/** + * Finds the town nearest to given existing airport. Based on minimal manhattan distance to any airport's tile. + * If two towns have the same distance, town with lower index is returned. + * @param station existing station with airport + * @param[out] mindist Minimum distance to town + * @return nearest town to airport + */ +static Town *AirportGetNearestTown(const Station *st, uint &mindist) +{ + return AirportGetNearestTown(st->airport.GetSpec(), st->airport.rotation, st->airport.tile, AirportTileIterator(st), mindist); +} + /** Recalculate the noise generated by the airports of each town */ void UpdateAirportsNoise() @@ -2349,11 +2365,9 @@ void UpdateAirportsNoise() for (const Station *st : Station::Iterate()) { if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) { - const AirportSpec *as = st->airport.GetSpec(); - AirportTileIterator it(st); uint dist; - Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist); - nearest->noise_reached += GetAirportNoiseLevelForDistance(as, dist); + Town *nearest = AirportGetNearestTown(st, dist); + nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist); } } } @@ -2402,7 +2416,7 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */ uint dist; - Town *nearest = AirportGetNearestTown(as, tile, tile_iter, dist); + Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist); uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist); /* Check if local auth would allow a new airport */ @@ -2524,14 +2538,12 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) CloseWindowById(WC_VEHICLE_DEPOT, tile_cur); } - const AirportSpec *as = st->airport.GetSpec(); /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. * And as for construction, always remove it, even if the setting is not set, in order to avoid the * need of recalculation */ - AirportTileIterator it(st); uint dist; - Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist); - nearest->noise_reached -= GetAirportNoiseLevelForDistance(as, dist); + Town *nearest = AirportGetNearestTown(st, dist); + nearest->noise_reached -= GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist); if (_settings_game.economy.station_noise_level) { SetWindowDirty(WC_TOWN_VIEW, nearest->index); diff --git a/src/station_cmd.h b/src/station_cmd.h index 38f393fd00..ed3a117653 100644 --- a/src/station_cmd.h +++ b/src/station_cmd.h @@ -16,7 +16,7 @@ enum StationClassID : byte; enum RoadStopClassID : byte; -extern Town *AirportGetNearestTown(const struct AirportSpec *as, TileIndex tile, const TileIterator &it, uint &mindist); +extern Town *AirportGetNearestTown(const struct AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist); extern uint8_t GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance); CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_type, byte layout, StationID station_to_join, bool allow_adjacent);