diff options
Diffstat (limited to 'src/station_cmd.cpp')
-rw-r--r-- | src/station_cmd.cpp | 122 |
1 files changed, 117 insertions, 5 deletions
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 2cc60b4ef..0fe4bebf2 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1610,6 +1610,88 @@ static const byte * const _airport_sections[] = { _airport_sections_helistation // Helistation }; +/** Recalculate the noise generated by the airports of each town */ +void UpdateAirportsNoise() +{ + Town *t; + const Station *st; + + FOR_ALL_TOWNS(t) t->noise_reached = 0; + + FOR_ALL_STATIONS(st) { + if (IsAirport(st->xy)) { + st->town->noise_reached += GetAirportNoiseLevelForTown(GetAirport(st->airport_type), st->town->xy, st->xy); + } + } +} + +/** Get a possible noise reduction factor based on distance from town center. + * The further you get, the less noise you generate. + * So all those folks at city council can now happily slee... work in their offices + * @param afc AirportFTAClass pointer of the class being proposed + * @param town_tile TileIndex of town's center, the one who will receive the airport's candidature + * @param tile TileIndex where the new airport might be built + * @return the noise that will be generated, according to distance + */ +uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile) +{ + struct TileIndexDistance { + TileIndex index; + uint distance; + }; + + 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; + + /* 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; + } + + /* 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. + * Basically, it says that the less tolerant a town is, the bigger the distance before + * an actual decrease can be granted */ + uint8 town_tolerance_distance = 8 + (_opt.diff.town_council_tolerance * 4); + + /* The airport is in the "inner" distance where there is no noise reduction */ + if (distance < town_tolerance_distance) return afc->noise_level; + + /* now, we want to have the distance segmented using the distance judged bareable by town + * This will give us the coefficient of reduction the distance provides. */ + uint noise_reduction = min(afc->noise_level, distance / town_tolerance_distance); + + /* If the noise reduction equals the airport noise itself, don't give it for free. Use it all minus 1. + * Otherwise, simply reduce the airport's level. */ + return max(1U, noise_reduction == afc->noise_level ? afc->noise_level - 1 : afc->noise_level - noise_reduction); +} + /** Place an Airport. * @param tile tile where airport will be built * @param flags operation to perform @@ -1641,12 +1723,25 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) CommandCost cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL); if (CmdFailed(cost)) return cost; - /* Check if local auth refuses a new airport */ - uint num = 0; - FOR_ALL_STATIONS(st) { - if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++; + /* 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); + + /* Check if local auth would allow a new airport */ + bool autority_refused; + + if (_patches.station_noise_level) { + /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */ + autority_refused = (t->noise_reached + newnoise_level) > t->MaxTownNoise(); + } else { + uint num = 0; + const Station *st; + FOR_ALL_STATIONS(st) { + if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++; + } + autority_refused = (num >= 2); } - if (num >= 2) { + + if (autority_refused) { SetDParam(0, t->index); return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES); } @@ -1693,6 +1788,9 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) cost.AddCost(_price.build_airport * w * h); 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; + st->airport_tile = tile; st->AddFacility(FACIL_AIRPORT, tile); st->airport_type = (byte)p1; @@ -1722,6 +1820,10 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) UpdateStationAcceptance(st, false); InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES); + + if (_patches.station_noise_level) { + InvalidateWindow(WC_TOWN_VIEW, st->town->index); + } } return cost; @@ -1764,12 +1866,22 @@ 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); + st->rect.AfterRemoveRect(st, tile, w, h); st->airport_tile = 0; st->facilities &= ~FACIL_AIRPORT; InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES); + + if (_patches.station_noise_level) { + InvalidateWindow(WC_TOWN_VIEW, st->town->index); + } + UpdateStationVirtCoordDirty(st); DeleteStationIfEmpty(st); } |