From c9be5d50dac64fe9fefe1a1e85d4eeaf8cc2470c Mon Sep 17 00:00:00 2001 From: yexo Date: Fri, 9 Dec 2011 19:30:30 +0000 Subject: [PATCH] (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 --- src/newgrf_airport.h | 34 +++++++++++++++++ src/script/api/script_airport.cpp | 14 ++++--- src/station_base.h | 31 ++++++++++++++++ src/station_cmd.cpp | 62 +++++++++++++++---------------- src/tilearea_type.h | 15 ++++++++ 5 files changed, 118 insertions(+), 38 deletions(-) diff --git a/src/newgrf_airport.h b/src/newgrf_airport.h index b00c3667c2..ca2ce15767 100644 --- 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 @@ struct AirportTileTable { 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 diff --git a/src/script/api/script_airport.cpp b/src/script/api/script_airport.cpp index d040d30940..43f8090575 100644 --- 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) diff --git a/src/station_base.h b/src/station_base.h index 2a7fedbf76..5ec337f563 100644 --- 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 StationPool; extern StationPool _station_pool; @@ -261,4 +262,34 @@ public: #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 */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 49711f2e64..27b0c0934b 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2019,24 +2019,17 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui /** * 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 @@ static uint GetMinimalAirportDistanceToTile(const AirportSpec *as, byte layout, * 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 @@ uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, byte layout, TileIndex * 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 @@ void UpdateAirportsNoise() 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 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint 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 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) 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 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) ); } - /* 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(); diff --git a/src/tilearea_type.h b/src/tilearea_type.h index 5826ce116f..1b3832a843 100644 --- a/src/tilearea_type.h +++ b/src/tilearea_type.h @@ -94,6 +94,11 @@ public: * 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 @@ public: } return *this; } + + virtual TileIterator *Clone() const + { + return new OrthogonalTileIterator(*this); + } }; /** Iterator to iterate over a diagonal area of the map. */ @@ -145,6 +155,11 @@ public: DiagonalTileIterator(TileIndex begin, TileIndex end); TileIterator& operator ++(); + + virtual TileIterator *Clone() const + { + return new DiagonalTileIterator(*this); + } }; /**