src/station_cmd.cpp
branchnoai
changeset 10715 6bdf79ffb022
parent 10645 8cbdb511a674
child 10776 07203fc29812
--- a/src/station_cmd.cpp	Mon May 26 11:36:42 2008 +0000
+++ b/src/station_cmd.cpp	Mon May 26 13:52:59 2008 +0000
@@ -296,7 +296,7 @@
 				CountMapSquareAround(tile, CMSATree) >= 8 ||
 				CountMapSquareAround(tile, CMSAForest) >= 2)
 			) {
-		return _opt.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
+		return _settings.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
 	}
 
 	/* check elevation compared to town */
@@ -563,7 +563,7 @@
 			TileXY(rect.left, rect.bottom),
 			rect.right - rect.left   + 1,
 			rect.top   - rect.bottom + 1,
-			_patches.modified_catchment ? FindCatchmentRadius(st) : (uint)CA_UNMODIFIED
+			_settings.station.modified_catchment ? FindCatchmentRadius(st) : (uint)CA_UNMODIFIED
 		);
 	} else {
 		memset(accepts, 0, sizeof(accepts));
@@ -692,7 +692,7 @@
 		 *     b) the build_on_slopes switch is disabled
 		 */
 		if (IsSteepSlope(tileh) ||
-				((!_patches.build_on_slopes) && tileh != SLOPE_FLAT)) {
+				((!_settings.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
 			return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 		}
 
@@ -750,7 +750,7 @@
 	uint w = fin[1];
 	uint h = fin[2];
 
-	if (_patches.nonuniform_stations) {
+	if (_settings.station.nonuniform_stations) {
 		/* determine new size of train station region.. */
 		int x = min(TileX(st->train_tile), TileX(tile));
 		int y = min(TileY(st->train_tile), TileY(tile));
@@ -794,7 +794,7 @@
 		}
 	}
 	/* make sure the final size is not too big. */
-	if (curw > _patches.station_spread || curh > _patches.station_spread) {
+	if (curw > _settings.station.station_spread || curh > _settings.station.station_spread) {
 		_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 		return false;
 	}
@@ -883,7 +883,7 @@
 		w_org = numtracks;
 	}
 
-	if (h_org > _patches.station_spread || w_org > _patches.station_spread) return CMD_ERROR;
+	if (h_org > _settings.station.station_spread || w_org > _settings.station.station_spread) return CMD_ERROR;
 
 	/* these values are those that will be stored in train_tile and station_platforms */
 	uint finalvalues[3];
@@ -896,14 +896,14 @@
 	/* If DC_EXEC is in flag, do not want to pass it to CheckFlatLandBelow, because of a nice bug
 	 * for detail info, see:
 	 * https://sourceforge.net/tracker/index.php?func=detail&aid=1029064&group_id=103924&atid=636365 */
-	CommandCost ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
+	CommandCost ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _settings.station.nonuniform_stations ? &est : NULL);
 	if (CmdFailed(ret)) return ret;
 	CommandCost cost(EXPENSES_CONSTRUCTION, ret.GetCost() + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len);
 
 	Station *st = NULL;
 	bool check_surrounding = true;
 
-	if (_patches.adjacent_stations) {
+	if (_settings.station.adjacent_stations) {
 		if (est != INVALID_STATION) {
 			if (HasBit(p1, 24)) {
 				/* You can't build an adjacent station over the top of one that
@@ -938,7 +938,7 @@
 
 		if (st->train_tile != 0) {
 			/* check if we want to expanding an already existing station? */
-			if (!_patches.join_stations)
+			if (!_settings.station.join_stations)
 				return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
 			if (!CanExpandRailroadStation(st, finalvalues, axis))
 				return CMD_ERROR;
@@ -993,7 +993,7 @@
 		/* Now really clear the land below the station
 		 * It should never return CMD_ERROR.. but you never know ;)
 		 * (a bit strange function name for it, but it really does clear the land, when DC_EXEC is in flags) */
-		ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
+		ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _settings.station.nonuniform_stations ? &est : NULL);
 		if (CmdFailed(ret)) return ret;
 
 		st->train_tile = finalvalues[0];
@@ -1162,7 +1162,7 @@
 		/* Do not allow removing from stations if non-uniform stations are not enabled
 		 * The check must be here to give correct error message
 		 */
-		if (!_patches.nonuniform_stations) return_cmd_error(STR_NONUNIFORM_STATIONS_DISALLOWED);
+		if (!_settings.station.nonuniform_stations) return_cmd_error(STR_NONUNIFORM_STATIONS_DISALLOWED);
 
 		/* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
 		quantity++;
@@ -1207,7 +1207,7 @@
 static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, uint32 flags)
 {
 	/* if there is flooding and non-uniform stations are enabled, remove platforms tile by tile */
-	if (_current_player == OWNER_WATER && _patches.nonuniform_stations) {
+	if (_current_player == OWNER_WATER && _settings.station.nonuniform_stations) {
 		return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
 	}
 
@@ -1300,7 +1300,7 @@
 	bool type = HasBit(p2, 0);
 	bool is_drive_through = HasBit(p2, 1);
 	bool build_over_road  = is_drive_through && IsNormalRoadTile(tile);
-	bool town_owned_road  = build_over_road && IsTileOwner(tile, OWNER_TOWN);
+	bool town_owned_road  = false;
 	RoadTypes rts = (RoadTypes)GB(p2, 2, 3);
 
 	if (!AreValidRoadTypes(rts) || !HasRoadTypesAvail(_current_player, rts)) return CMD_ERROR;
@@ -1319,14 +1319,17 @@
 
 	/* Not allowed to build over this road */
 	if (build_over_road) {
-		if (IsTileOwner(tile, OWNER_TOWN) && !_patches.road_stop_on_town_road) return_cmd_error(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD);
-
 		RoadTypes cur_rts = GetRoadTypes(tile);
 
 		/* there is a road, check if we can build road+tram stop over it */
 		if (HasBit(cur_rts, ROADTYPE_ROAD)) {
 			Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
-			if (road_owner != OWNER_TOWN && road_owner != OWNER_NONE && !CheckOwnership(road_owner)) return CMD_ERROR;
+			if (road_owner == OWNER_TOWN) {
+				town_owned_road = true;
+				if (!_settings.construction.road_stop_on_town_road) return_cmd_error(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD);
+			} else {
+				if (road_owner != OWNER_NONE && !CheckOwnership(road_owner)) return CMD_ERROR;
+			}
 		}
 
 		/* there is a tram, check if we can build road+tram stop over it */
@@ -1347,7 +1350,7 @@
 
 	Station *st = NULL;
 
-	if (!_patches.adjacent_stations || !HasBit(p2, 5)) {
+	if (!_settings.station.adjacent_stations || !HasBit(p2, 5)) {
 		st = GetStationAround(tile, 1, 1, INVALID_STATION);
 		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 	}
@@ -1610,6 +1613,88 @@
 	_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 + (_settings.difficulty.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
@@ -1633,7 +1718,7 @@
 	int h = afc->size_y;
 	Station *st = NULL;
 
-	if (w > _patches.station_spread || h > _patches.station_spread) {
+	if (w > _settings.station.station_spread || h > _settings.station.station_spread) {
 		_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 		return CMD_ERROR;
 	}
@@ -1641,17 +1726,30 @@
 	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 authority_refused;
+
+	if (_settings.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();
+	} 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++;
+		}
+		authority_refused = (num >= 2);
 	}
-	if (num >= 2) {
+
+	if (authority_refused) {
 		SetDParam(0, t->index);
 		return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES);
 	}
 
-	if (!_patches.adjacent_stations || !HasBit(p2, 0)) {
+	if (!_settings.station.adjacent_stations || !HasBit(p2, 0)) {
 		st = GetStationAround(tile, w, h, INVALID_STATION);
 		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 	} else {
@@ -1693,6 +1791,9 @@
 	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 +1823,10 @@
 		UpdateStationAcceptance(st, false);
 		InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
 		InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
+
+		if (_settings.economy.station_noise_level) {
+			InvalidateWindow(WC_TOWN_VIEW, st->town->index);
+		}
 	}
 
 	return cost;
@@ -1764,12 +1869,22 @@
 			);
 		}
 
+		/* 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 (_settings.economy.station_noise_level) {
+			InvalidateWindow(WC_TOWN_VIEW, st->town->index);
+		}
+
 		UpdateStationVirtCoordDirty(st);
 		DeleteStationIfEmpty(st);
 	}
@@ -1929,7 +2044,7 @@
 	/* middle */
 	Station *st = NULL;
 
-	if (!_patches.adjacent_stations || !HasBit(p1, 0)) {
+	if (!_settings.station.adjacent_stations || !HasBit(p1, 0)) {
 		st = GetStationAround(
 				tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
 				_dock_w_chk[direction], _dock_h_chk[direction], INVALID_STATION);
@@ -2188,7 +2303,12 @@
 
 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
 {
-	td->owner = GetTileOwner(tile);
+	td->owner[0] = GetTileOwner(tile);
+	if (IsDriveThroughStopTile(tile) && HasTileRoadType(tile, ROADTYPE_ROAD) && GetStopBuiltOnTownRoad(tile)) {
+		/* Display a second owner */
+		td->owner_type[1] = STR_ROAD_OWNER;
+		td->owner[1] = OWNER_TOWN;
+	}
 	td->build_date = GetStationByTile(tile)->build_date;
 
 	StringID str;
@@ -2654,7 +2774,7 @@
 	int w_prod; // width and height of the "producer" of the cargo
 	int h_prod;
 	int max_rad;
-	if (_patches.modified_catchment) {
+	if (_settings.station.modified_catchment) {
 		w_prod = w;
 		h_prod = h;
 		w += 2 * MAX_CATCHMENT;
@@ -2677,7 +2797,7 @@
 		if (st->IsBuoy()) continue; // bouys don't accept cargo
 
 
-		if (_patches.modified_catchment) {
+		if (_settings.station.modified_catchment) {
 			/* min and max coordinates of the producer relative */
 			const int x_min_prod = max_rad + 1;
 			const int x_max_prod = max_rad + w_prod;
@@ -2731,7 +2851,7 @@
 
 		if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore
 
-		if (_patches.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
+		if (_settings.order.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
 
 		if (IsCargoInClass(type, CC_PASSENGERS)) {
 			if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
@@ -2875,16 +2995,15 @@
  * Road stops built on town-owned roads check the conditions
  * that would allow clearing of the original road.
  * @param tile road stop tile to check
+ * @param flags command flags
  * @return true if the road can be cleared
  */
-static bool CanRemoveRoadWithStop(TileIndex tile)
+static bool CanRemoveRoadWithStop(TileIndex tile, uint32 flags)
 {
 	/* The road can always be cleared if it was not a town-owned road */
 	if (!GetStopBuiltOnTownRoad(tile)) return true;
 
-	bool edge_road;
-	return CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, &edge_road, ROADTYPE_ROAD) &&
-				CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_TRAM), OWNER_TOWN, &edge_road, ROADTYPE_TRAM);
+	return CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags);
 }
 
 static CommandCost ClearTile_Station(TileIndex tile, byte flags)
@@ -2909,11 +3028,11 @@
 		case STATION_RAIL:    return RemoveRailroadStation(st, tile, flags);
 		case STATION_AIRPORT: return RemoveAirport(st, flags);
 		case STATION_TRUCK:
-			if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile))
+			if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
 				return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
 			return RemoveRoadStop(st, flags, tile);
 		case STATION_BUS:
-			if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile))
+			if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
 				return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
 			return RemoveRoadStop(st, flags, tile);
 		case STATION_BUOY:    return RemoveBuoy(st, flags);
@@ -2957,7 +3076,7 @@
 
 static CommandCost TerraformTile_Station(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new)
 {
-	if (_patches.build_on_slopes && AutoslopeEnabled()) {
+	if (_settings.construction.build_on_slopes && AutoslopeEnabled()) {
 		/* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
 		 *       TTDP does not call it.
 		 */