diff options
-rw-r--r-- | src/ai/api/ai_airport.cpp | 3 | ||||
-rw-r--r-- | src/station_cmd.cpp | 100 |
2 files changed, 63 insertions, 40 deletions
diff --git a/src/ai/api/ai_airport.cpp b/src/ai/api/ai_airport.cpp index cb8693255..2b0c3155b 100644 --- a/src/ai/api/ai_airport.cpp +++ b/src/ai/api/ai_airport.cpp @@ -115,6 +115,7 @@ /* static */ int AIAirport::GetNoiseLevelIncrease(TileIndex tile, AirportType type) { + extern Town *AirportGetNearestTown(const AirportFTAClass *afc, TileIndex airport_tile); extern uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile); if (!::IsValidTile(tile)) return -1; @@ -122,7 +123,7 @@ if (_settings_game.economy.station_noise_level) { const AirportFTAClass *afc = ::GetAirport(type); - const Town *t = ::ClosestTownFromTile(tile, UINT_MAX); + const Town *t = AirportGetNearestTown(afc, tile); return GetAirportNoiseLevelForTown(afc, t->xy, tile); } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 8085af255..4f1c5363c 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1751,6 +1751,32 @@ static const byte * const _airport_sections[] = { _airport_sections_helistation // Helistation }; +/** + * Computes the minimal distance from town's xy to any airport's tile. + * @param afc airport's description + * @param town_tile town's tile (t->xy) + * @param airport_tile st->airport_tile + * @return minimal manhattan distance from town_tile to any airport's tile + */ +static uint GetMinimalAirportDistanceToTile(const AirportFTAClass *afc, TileIndex town_tile, TileIndex airport_tile) +{ + uint ttx = TileX(town_tile); // X, Y of town + uint tty = TileY(town_tile); + + uint atx = TileX(airport_tile); // X, Y of northern airport corner + uint aty = TileY(airport_tile); + + uint btx = TileX(airport_tile) + afc->size_x - 1; // X, Y of southern corner + uint bty = TileY(airport_tile) + afc->size_y - 1; + + /* if ttx < atx, dx = atx - ttx + * if atx <= ttx <= btx, dx = 0 + * else, dx = ttx - btx (similiar for dy) */ + uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx); + uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty); + + return dx + dy; +} /** Get a possible noise reduction factor based on distance from town center. * The further you get, the less noise you generate. @@ -1762,44 +1788,11 @@ static const byte * const _airport_sections[] = { */ uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile) { - struct TileIndexDistance { - TileIndex index; - uint distance; - }; - /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town. * So no need to go any further*/ if (afc->noise_level < 2) return afc->noise_level; - uint distance; - - /* Find the airport-to-be's closest corner to the town */ - if (afc->size_x == 1 && afc->size_y == 1) { - distance = DistanceManhattan(town_tile, tile); // ont tile, one corner, it's THE corner - } else { - /* calculate manhattan distance to town of each side of the airport */ - TileIndexDistance dist[4]; ///< Array that will contain all 4 corners in TileIndex and distance - uint min_tile = UINT_MAX; ///< Sentinel value - uint min_indice = 4; - - /* Based on the size of the airport, establish location of each corner*/ - dist[0].index = tile; // north tile - dist[1].index = TileAddWrap(tile, afc->size_x - 1, afc->size_y - 1); // south tile - dist[2].index = TileAddWrap(tile, afc->size_x - 1, 0); // west tile - dist[3].index = TileAddWrap(tile, 0, afc->size_y - 1); //east tile - - /* now, go and find the smallest one, thus the closest to town */ - for (uint i = 0; i < 4; i++) { - dist[i].distance = DistanceManhattan(town_tile, dist[i].index); - - if (dist[i].distance < min_tile) { - min_tile = dist[i].distance; // here's a new candidate - min_indice = i; - } - } - - distance = dist[min_indice].distance; - } + uint distance = GetMinimalAirportDistanceToTile(afc, town_tile, tile); /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance. @@ -1816,6 +1809,31 @@ uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_til return noise_reduction >= afc->noise_level ? 1 : afc->noise_level - noise_reduction; } +/** + * 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 afc airport's description + * @param airport_tile st->airport_tile + * @return nearest town to airport + */ +Town *AirportGetNearestTown(const AirportFTAClass *afc, TileIndex airport_tile) +{ + Town *t, *nearest = NULL; + uint add = afc->size_x + afc->size_y - 2; // GetMinimalAirportDistanceToTile can differ from DistanceManhattan by this much + uint mindist = UINT_MAX - add; // prevent overflow + FOR_ALL_TOWNS(t) { + if (DistanceManhattan(t->xy, airport_tile) < mindist + add) { // avoid calling GetMinimalAirportDistanceToTile too often + uint dist = GetMinimalAirportDistanceToTile(afc, t->xy, airport_tile); + if (dist < mindist) { + nearest = t; + mindist = dist; + } + } + } + + return nearest; +} + /** Recalculate the noise generated by the airports of each town */ void UpdateAirportsNoise() @@ -1827,7 +1845,9 @@ void UpdateAirportsNoise() FOR_ALL_STATIONS(st) { if (st->airport_tile != INVALID_TILE) { - st->town->noise_reached += GetAirportNoiseLevelForTown(GetAirport(st->airport_type), st->town->xy, st->airport_tile); + const AirportFTAClass *afc = GetAirport(st->airport_type); + Town *nearest = AirportGetNearestTown(afc, st->airport_tile); + nearest->noise_reached += GetAirportNoiseLevelForTown(afc, nearest->xy, st->airport_tile); } } } @@ -1871,14 +1891,15 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, if (CmdFailed(cost)) return cost; /* Go get the final noise level, that is base noise minus factor from distance to town center */ - uint newnoise_level = GetAirportNoiseLevelForTown(afc, t->xy, tile); + Town *nearest = AirportGetNearestTown(afc, tile); + uint newnoise_level = GetAirportNoiseLevelForTown(afc, nearest->xy, tile); /* Check if local auth would allow a new airport */ bool authority_refused; if (_settings_game.economy.station_noise_level) { /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */ - authority_refused = (t->noise_reached + newnoise_level) > t->MaxTownNoise(); + authority_refused = (nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise(); } else { uint num = 0; const Station *st; @@ -1939,7 +1960,7 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, if (flags & DC_EXEC) { /* Always add the noise, so there will be no need to recalculate when option toggles */ - st->town->noise_reached += newnoise_level; + nearest->noise_reached += newnoise_level; st->airport_tile = tile; st->AddFacility(FACIL_AIRPORT, tile); @@ -2020,7 +2041,8 @@ static CommandCost RemoveAirport(Station *st, uint32 flags) /* Go get the final noise level, that is base noise minus factor from distance to town center. * And as for construction, always remove it, even if the patch is not set, in order to avoid the * need of recalculation */ - st->town->noise_reached -= GetAirportNoiseLevelForTown(afc, st->town->xy, tile); + Town *nearest = AirportGetNearestTown(afc, tile); + nearest->noise_reached -= GetAirportNoiseLevelForTown(afc, nearest->xy, tile); st->rect.AfterRemoveRect(st, tile, w, h); |