summaryrefslogtreecommitdiff
path: root/src/station_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/station_cmd.cpp')
-rw-r--r--src/station_cmd.cpp122
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);
}