Fix AirportGetNearestTown for rotated airports

See: https://github.com/OpenTTD/OpenTTD/issues/11629
This commit is contained in:
Jonathan G Rennison
2023-12-26 19:37:03 +00:00
parent 43936575e6
commit 35d2842de0
2 changed files with 37 additions and 29 deletions

View File

@@ -15,7 +15,7 @@
#include "../../safeguards.h" #include "../../safeguards.h"
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 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance); extern uint8 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance);
/* static */ bool ScriptAirport::IsValidAirportType(AirportType type) /* static */ bool ScriptAirport::IsValidAirportType(AirportType type)
@@ -140,9 +140,8 @@ extern uint8 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint
if (!as->IsWithinMapBounds(0, tile)) return -1; if (!as->IsWithinMapBounds(0, tile)) return -1;
if (_settings_game.economy.station_noise_level) { if (_settings_game.economy.station_noise_level) {
AirportTileTableIterator it(as->table[0], tile);
uint dist; uint dist;
AirportGetNearestTown(as, tile, it, dist); AirportGetNearestTown(as, DIR_N, tile, AirportTileTableIterator(as->table[0], tile), dist);
return GetAirportNoiseLevelForDistance(as, dist); return GetAirportNoiseLevelForDistance(as, dist);
} }
@@ -158,7 +157,7 @@ extern uint8 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint
if (!as->IsWithinMapBounds(0, tile)) return INVALID_TOWN; if (!as->IsWithinMapBounds(0, tile)) return INVALID_TOWN;
uint dist; uint dist;
return AirportGetNearestTown(as, tile, AirportTileTableIterator(as->table[0], tile), dist)->index; return AirportGetNearestTown(as, DIR_N, tile, AirportTileTableIterator(as->table[0], tile), dist)->index;
} }
/* static */ SQInteger ScriptAirport::GetMaintenanceCostFactor(AirportType type) /* static */ SQInteger ScriptAirport::GetMaintenanceCostFactor(AirportType type)

View File

@@ -2599,28 +2599,32 @@ uint8 GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
* Finds the town nearest to given airport. Based on minimal manhattan distance to any airport's tile. * 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. * If two towns have the same distance, town with lower index is returned.
* @param as airport's description * @param as airport's description
* @param rotation airport's rotation
* @param tile origin tile (top corner of the airport) * @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 * @param[out] mindist Minimum distance to town
* @return nearest town to airport * @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); assert(Town::GetNumItems() > 0);
Town *nearest = nullptr; Town *nearest = nullptr;
int width = as->size_x;
int height = as->size_y;
if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
uint perimeter_min_x = TileX(tile); uint perimeter_min_x = TileX(tile);
uint perimeter_min_y = TileY(tile); uint perimeter_min_y = TileY(tile);
uint perimeter_max_x = perimeter_min_x + as->size_x - 1; uint perimeter_max_x = perimeter_min_x + width - 1;
uint perimeter_max_y = perimeter_min_y + as->size_y - 1; uint perimeter_max_y = perimeter_min_y + height - 1;
mindist = UINT_MAX - 1; // prevent overflow mindist = UINT_MAX - 1; // prevent overflow
std::unique_ptr<TileIterator> copy(it.Clone()); for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
for (TileIndex cur_tile = *copy; cur_tile != INVALID_TILE; cur_tile = ++*copy) { assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, as->size_x)); assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, as->size_y));
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) { 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); Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
if (t == nullptr) continue; if (t == nullptr) continue;
@@ -2637,6 +2641,18 @@ Town *AirportGetNearestTown(const AirportSpec *as, TileIndex tile, const TileIte
return nearest; 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
*/
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 */ /** Recalculate the noise generated by the airports of each town */
void UpdateAirportsNoise() void UpdateAirportsNoise()
@@ -2647,11 +2663,9 @@ void UpdateAirportsNoise()
for (const Station *st : Station::Iterate()) { for (const Station *st : Station::Iterate()) {
if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) { if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
const AirportSpec *as = st->airport.GetSpec();
AirportTileIterator it(st);
uint dist; uint dist;
Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist); Town *nearest = AirportGetNearestTown(st, dist);
nearest->noise_reached += GetAirportNoiseLevelForDistance(as, dist); nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
} }
} }
} }
@@ -2763,17 +2777,16 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
} }
/* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */ /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
AirportTileTableIterator nearest_town_iter = iter;
uint dist; uint dist;
Town *nearest = AirportGetNearestTown(as, tile, iter, dist); Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(nearest_town_iter), dist);
uint newnoise_level = nearest->noise_reached + GetAirportNoiseLevelForDistance(as, dist); uint newnoise_level = nearest->noise_reached + GetAirportNoiseLevelForDistance(as, dist);
if (action == AIRPORT_UPGRADE) { if (action == AIRPORT_UPGRADE) {
const AirportSpec *old_as = st->airport.GetSpec();
AirportTileTableIterator old_iter(old_as->table[st->airport.layout], st->airport.tile);
uint old_dist; uint old_dist;
Town *old_nearest = AirportGetNearestTown(old_as, st->airport.tile, old_iter, old_dist); Town *old_nearest = AirportGetNearestTown(st, old_dist);
if (old_nearest == nearest) { if (old_nearest == nearest) {
newnoise_level -= GetAirportNoiseLevelForDistance(old_as, old_dist); newnoise_level -= GetAirportNoiseLevelForDistance(st->airport.GetSpec(), old_dist);
} }
} }
@@ -2827,13 +2840,11 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
CloseWindowById(WC_VEHICLE_DEPOT, tile_cur); CloseWindowById(WC_VEHICLE_DEPOT, tile_cur);
} }
const AirportSpec *old_as = st->airport.GetSpec();
AirportTileTableIterator old_iter(old_as->table[st->airport.layout], st->airport.tile);
uint old_dist; uint old_dist;
Town *old_nearest = AirportGetNearestTown(old_as, st->airport.tile, old_iter, old_dist); Town *old_nearest = AirportGetNearestTown(st, old_dist);
if (old_nearest != nearest) { if (old_nearest != nearest) {
old_nearest->noise_reached -= GetAirportNoiseLevelForDistance(old_as, old_dist); old_nearest->noise_reached -= GetAirportNoiseLevelForDistance(st->airport.GetSpec(), old_dist);
if (_settings_game.economy.station_noise_level) { if (_settings_game.economy.station_noise_level) {
SetWindowDirty(WC_TOWN_VIEW, st->town->index); SetWindowDirty(WC_TOWN_VIEW, st->town->index);
} }
@@ -2919,14 +2930,12 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
} }
ZoningMarkDirtyStationCoverageArea(st); ZoningMarkDirtyStationCoverageArea(st);
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. /* 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 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
* need of recalculation */ * need of recalculation */
AirportTileIterator it(st);
uint dist; uint dist;
Town *nearest = AirportGetNearestTown(as, st->airport.tile, it, dist); Town *nearest = AirportGetNearestTown(st, dist);
nearest->noise_reached -= GetAirportNoiseLevelForDistance(as, dist); nearest->noise_reached -= GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
if (_settings_game.economy.station_noise_level) { if (_settings_game.economy.station_noise_level) {
SetWindowDirty(WC_TOWN_VIEW, nearest->index); SetWindowDirty(WC_TOWN_VIEW, nearest->index);