changeset 18614:010be418dc07 draft

(svn r23461) -Fix: handle a missing airport newgrf as graceful as possible by not crashing when loading such savegame or when an airport is removed
author yexo <yexo@openttd.org>
date Fri, 09 Dec 2011 19:30:30 +0000
parents 0bf095b4ce21
children 5ba394eea1f1
files src/newgrf_airport.h src/script/api/script_airport.cpp src/station_base.h src/station_cmd.cpp src/tilearea_type.h
diffstat 5 files changed, 118 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/src/newgrf_airport.h
+++ b/src/newgrf_airport.h
@@ -18,6 +18,7 @@
 #include "newgrf_class.h"
 #include "newgrf_commons.h"
 #include "gfx_type.h"
+#include "tilearea_type.h"
 
 /** Copy from station_map.h */
 typedef byte StationGfx;
@@ -28,6 +29,39 @@
 	StationGfx gfx;    ///< AirportTile to use for this tile.
 };
 
+/** Iterator to iterate over all tiles belonging to an airport spec. */
+class AirportTileTableIterator : public TileIterator {
+private:
+	const AirportTileTable *att; ///< The offsets.
+	TileIndex base_tile;         ///< The tile we base the offsets off.
+
+public:
+	/**
+	 * Construct the iterator.
+	 * @param att The TileTable we want to iterate over.
+	 * @param base_tile The basetile for all offsets.
+	 */
+	AirportTileTableIterator(const AirportTileTable *att, TileIndex base_tile) : TileIterator(base_tile + ToTileIndexDiff(att->ti)), att(att), base_tile(base_tile)
+	{
+	}
+
+	FORCEINLINE TileIterator& operator ++()
+	{
+		this->att++;
+		if (this->att->ti.x == -0x80) {
+			this->tile = INVALID_TILE;
+		} else {
+			this->tile = base_tile + ToTileIndexDiff(att->ti);
+		}
+		return *this;
+	}
+
+	virtual AirportTileTableIterator *Clone() const
+	{
+		return new AirportTileTableIterator(*this);
+	}
+};
+
 /** List of default airport classes. */
 enum AirportClassID {
 	APC_BEGIN     = 0,  ///< Lowest valid airport class id
--- a/src/script/api/script_airport.cpp
+++ b/src/script/api/script_airport.cpp
@@ -127,16 +127,17 @@
 
 /* static */ int ScriptAirport::GetNoiseLevelIncrease(TileIndex tile, AirportType type)
 {
-	extern Town *AirportGetNearestTown(const AirportSpec *as, byte layout, TileIndex airport_tile);
-	extern uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, byte layout, TileIndex town_tile, TileIndex tile);
+	extern Town *AirportGetNearestTown(const AirportSpec *as, const TileIterator &it);
+	extern uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIterator &it, TileIndex town_tile);
 
 	if (!::IsValidTile(tile)) return -1;
 	if (!IsAirportInformationAvailable(type)) return -1;
 
 	if (_settings_game.economy.station_noise_level) {
 		const AirportSpec *as = ::AirportSpec::Get(type);
-		const Town *t = AirportGetNearestTown(as, 0, tile);
-		return GetAirportNoiseLevelForTown(as, 0, t->xy, tile);
+		AirportTileTableIterator it(as->table[0], tile);
+		const Town *t = AirportGetNearestTown(as, it);
+		return GetAirportNoiseLevelForTown(as, it, t->xy);
 	}
 
 	return 1;
@@ -144,12 +145,13 @@
 
 /* static */ TownID ScriptAirport::GetNearestTown(TileIndex tile, AirportType type)
 {
-	extern Town *AirportGetNearestTown(const AirportSpec *as, byte layout, TileIndex airport_tile);
+	extern Town *AirportGetNearestTown(const AirportSpec *as, const TileIterator &it);
 
 	if (!::IsValidTile(tile)) return INVALID_TOWN;
 	if (!IsAirportInformationAvailable(type)) return INVALID_TOWN;
 
-	return AirportGetNearestTown(AirportSpec::Get(type), 0, tile)->index;
+	const AirportSpec *as = AirportSpec::Get(type);
+	return AirportGetNearestTown(as, AirportTileTableIterator(as->table[0], tile))->index;
 }
 
 /* static */ uint16 ScriptAirport::GetMaintenanceCostFactor(AirportType type)
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -17,6 +17,7 @@
 #include "cargopacket.h"
 #include "industry_type.h"
 #include "newgrf_storage.h"
+#include "town.h"
 
 typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
 extern StationPool _station_pool;
@@ -261,4 +262,34 @@
 
 #define FOR_ALL_STATIONS(var) FOR_ALL_BASE_STATIONS_OF_TYPE(Station, var)
 
+/** Iterator to iterate over all tiles belonging to an airport. */
+class AirportTileIterator : public OrthogonalTileIterator {
+private:
+	const Station *st; ///< The station the airport is a part of.
+
+public:
+	/**
+	 * Construct the iterator.
+	 * @param ta Area, i.e. begin point and width/height of to-be-iterated area.
+	 */
+	AirportTileIterator(const Station *st) : OrthogonalTileIterator(st->airport), st(st)
+	{
+		if (!st->TileBelongsToAirport(this->tile)) ++(*this);
+	}
+
+	FORCEINLINE TileIterator& operator ++()
+	{
+		(*this).OrthogonalTileIterator::operator++();
+		while (this->tile != INVALID_TILE && !st->TileBelongsToAirport(this->tile)) {
+			(*this).OrthogonalTileIterator::operator++();
+		}
+		return *this;
+	}
+
+	virtual TileIterator *Clone() const
+	{
+		return new AirportTileIterator(*this);
+	}
+};
+
 #endif /* STATION_BASE_H */
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2019,24 +2019,17 @@
 
 /**
  * Computes the minimal distance from town's xy to any airport's tile.
- * @param as airport's description
+ * @param it An iterator over all airport tiles.
  * @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 AirportSpec *as, byte layout, TileIndex town_tile, TileIndex airport_tile)
+static uint GetMinimalAirportDistanceToTile(TileIterator &it, TileIndex town_tile)
 {
 	uint mindist = UINT_MAX;
 
-	const AirportTileTable *it = as->table[layout];
-	do {
-		TileIndex cur_tile = airport_tile + ToTileIndexDiff(it->ti);
-
-		uint dist = DistanceManhattan(town_tile, cur_tile);
-		if (dist < mindist) {
-			mindist = dist;
-		}
-	} while ((++it)->ti.x != -0x80);
+	for (TileIndex cur_tile = it; cur_tile != INVALID_TILE; cur_tile = ++it) {
+		mindist = min(mindist, DistanceManhattan(town_tile, cur_tile));
+	}
 
 	return mindist;
 }
@@ -2046,18 +2039,17 @@
  * 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 as airport information
+ * @param it An iterator over all airport tiles.
  * @param town_tile TileIndex of town's center, the one who will receive the airport's candidature
- * @param tile TileIndex of northern tile of an airport (present or to-be-built), NOT the station tile
  * @return the noise that will be generated, according to distance
  */
-uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, byte layout, TileIndex town_tile, TileIndex tile)
+uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIterator &it, TileIndex town_tile)
 {
 	/* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
 	 * So no need to go any further*/
 	if (as->noise_level < 2) return as->noise_level;
 
-	assert(layout < as->num_table);
-	uint distance = GetMinimalAirportDistanceToTile(as, layout, town_tile, tile);
+	uint distance = GetMinimalAirportDistanceToTile(it, town_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.
@@ -2078,19 +2070,19 @@
  * 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 as airport's description
- * @param airport_tile st->airport.tile
+ * @param it An iterator over all airport tiles
  * @return nearest town to airport
  */
-Town *AirportGetNearestTown(const AirportSpec *as, byte layout, TileIndex airport_tile)
+Town *AirportGetNearestTown(const AirportSpec *as, const TileIterator &it)
 {
-	assert(layout < as->num_table);
-
 	Town *t, *nearest = NULL;
 	uint add = as->size_x + as->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(as, layout, t->xy, airport_tile);
+		if (DistanceManhattan(t->xy, it) < mindist + add) { // avoid calling GetMinimalAirportDistanceToTile too often
+			TileIterator *copy = it.Clone();
+			uint dist = GetMinimalAirportDistanceToTile(*copy, t->xy);
+			delete copy;
 			if (dist < mindist) {
 				nearest = t;
 				mindist = dist;
@@ -2113,8 +2105,9 @@
 	FOR_ALL_STATIONS(st) {
 		if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
 			const AirportSpec *as = st->airport.GetSpec();
-			Town *nearest = AirportGetNearestTown(as, st->airport.layout, st->airport.tile);
-			nearest->noise_reached += GetAirportNoiseLevelForTown(as, st->airport.layout, nearest->xy, st->airport.tile);
+			AirportTileIterator it(st);
+			Town *nearest = AirportGetNearestTown(as, it);
+			nearest->noise_reached += GetAirportNoiseLevelForTown(as, it, nearest->xy);
 		}
 	}
 }
@@ -2166,8 +2159,9 @@
 	if (cost.Failed()) return cost;
 
 	/* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
-	Town *nearest = AirportGetNearestTown(as, layout, tile);
-	uint newnoise_level = GetAirportNoiseLevelForTown(as, layout, nearest->xy, tile);
+	AirportTileTableIterator iter(as->table[layout], tile);
+	Town *nearest = AirportGetNearestTown(as, iter);
+	uint newnoise_level = GetAirportNoiseLevelForTown(as, iter, nearest->xy);
 
 	/* Check if local auth would allow a new airport */
 	StringID authority_refuse_message = STR_NULL;
@@ -2309,6 +2303,16 @@
 		if (a->targetairport == st->index && a->state != FLYING) return CMD_ERROR;
 	}
 
+	if (flags & DC_EXEC) {
+		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.
+		 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
+		 * need of recalculation */
+		AirportTileIterator it(st);
+		Town *nearest = AirportGetNearestTown(as, it);
+		nearest->noise_reached -= GetAirportNoiseLevelForTown(as, it, nearest->xy);
+	}
+
 	TILE_AREA_LOOP(tile_cur, st->airport) {
 		if (!st->TileBelongsToAirport(tile_cur)) continue;
 
@@ -2336,12 +2340,6 @@
 			);
 		}
 
-		/* 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
-		 * need of recalculation */
-		Town *nearest = AirportGetNearestTown(as, st->airport.layout, tile);
-		nearest->noise_reached -= GetAirportNoiseLevelForTown(as, st->airport.layout, nearest->xy, tile);
-
 		st->rect.AfterRemoveRect(st, st->airport);
 
 		st->airport.Clear();
--- a/src/tilearea_type.h
+++ b/src/tilearea_type.h
@@ -94,6 +94,11 @@
 	 * Move ourselves to the next tile in the rectange on the map.
 	 */
 	virtual TileIterator& operator ++() = 0;
+
+	/**
+	 * Allocate a new iterator that is a copy of this one.
+	 */
+	virtual TileIterator *Clone() const = 0;
 };
 
 /** Iterator to iterate over a tile area (rectangle) of the map. */
@@ -129,6 +134,11 @@
 		}
 		return *this;
 	}
+
+	virtual TileIterator *Clone() const
+	{
+		return new OrthogonalTileIterator(*this);
+	}
 };
 
 /** Iterator to iterate over a diagonal area of the map. */
@@ -145,6 +155,11 @@
 	DiagonalTileIterator(TileIndex begin, TileIndex end);
 
 	TileIterator& operator ++();
+
+	virtual TileIterator *Clone() const
+	{
+		return new DiagonalTileIterator(*this);
+	}
 };
 
 /**