diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index eb90c29ab5..7ee22b16cf 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -990,6 +990,47 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags return cost; } +/** Checks if an airport can be built at the given area. + * @param tile_area Area to check. + * @param flags Operation to perform. + * @param station StationID of airport allowed in search area. + * @return The cost in case of success, or an error code if it failed. + */ +static CommandCost CheckFlatLandAirport(TileArea tile_area, DoCommandFlag flags, StationID *station) +{ + CommandCost cost(EXPENSES_CONSTRUCTION); + int allowed_z = -1; + + TILE_AREA_LOOP(tile_cur, tile_area) { + CommandCost ret = CheckBuildableTile(tile_cur, 0, allowed_z, true); + if (ret.Failed()) return ret; + cost.AddCost(ret); + + /* if station is set, then allow building on top of an already + * existing airport, either the one in *station if it is not + * INVALID_STATION, or anyone otherwise and store which one + * in *station */ + if (station != NULL && IsTileType(tile_cur, MP_STATION)) { + if (!IsAirport(tile_cur)) { + return ClearTile_Station(tile_cur, DC_AUTO); // get error message + } else { + StationID st = GetStationIndex(tile_cur); + if (*station == INVALID_STATION) { + *station = st; + } else if (*station != st) { + return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING); + } + } + } else { + ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret.Failed()) return ret; + cost.AddCost(ret); + } + } + + return cost; +} + /** * Check whether we can expand the rail part of the given station. * @param st the station to expand @@ -1069,16 +1110,16 @@ void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSp /** * Find a nearby station that joins this station. * @tparam T the class to find a station for - * @tparam error_message the error message when building a station on top of others * @param existing_station an existing station we build over * @param station_to_join the station to join to * @param adjacent whether adjacent stations are allowed * @param ta the area of the newly build station * @param st 'return' pointer for the found station + * @param error_message the error message when building a station on top of others * @return command cost with the error or 'okay' */ -template -CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st) +template +CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, StringID error_message) { assert(*st == NULL); bool check_surrounding = true; @@ -1121,11 +1162,12 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station * @param adjacent whether adjacent stations are allowed * @param ta the area of the newly build station * @param st 'return' pointer for the found station + * @param error_message the error message when building a station on top of others * @return command cost with the error or 'okay' */ -static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st) +static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st, StringID error_message = STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST) { - return FindJoiningBaseStation(existing_station, station_to_join, adjacent, ta, st); + return FindJoiningBaseStation(existing_station, station_to_join, adjacent, ta, st, error_message); } /** @@ -1139,7 +1181,7 @@ static CommandCost FindJoiningStation(StationID existing_station, StationID stat */ CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp) { - return FindJoiningBaseStation(existing_waypoint, waypoint_to_join, adjacent, ta, wp); + return FindJoiningBaseStation(existing_waypoint, waypoint_to_join, adjacent, ta, wp, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST); } /** @@ -1731,7 +1773,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags); */ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st) { - return FindJoiningBaseStation(existing_stop, station_to_join, adjacent, ta, st); + return FindJoiningBaseStation(existing_stop, station_to_join, adjacent, ta, st, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST); } /** @@ -2169,6 +2211,37 @@ void UpdateAirportsNoise() } } + +/** + * Checks if an airport can be removed (no aircraft on it or landing) + * @param st Station whose airport is to be removed + * @param flags Operation to perform + * @return Cost or failure of operation + */ +static CommandCost CanRemoveAirport(Station *st, DoCommandFlag flags) +{ + const Aircraft *a; + FOR_ALL_AIRCRAFT(a) { + if (!a->IsNormalAircraft()) continue; + if (a->targetairport == st->index && a->state != FLYING) + return_cmd_error(STR_ERROR_AIRCRAFT_IN_THE_WAY); + } + + CommandCost cost(EXPENSES_CONSTRUCTION); + + TILE_AREA_LOOP(tile_cur, st->airport) { + if (!st->TileBelongsToAirport(tile_cur)) continue; + + CommandCost ret = EnsureNoVehicleOnGround(tile_cur); + if (ret.Failed()) return ret; + + cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); + } + + return cost; +} + + /** * Place an Airport. * @param tile tile where airport will be built @@ -2212,13 +2285,46 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT); } - CommandCost cost = CheckFlatLand(airport_area, flags); + StationID est = INVALID_STATION; + CommandCost cost = CheckFlatLandAirport(airport_area, flags, &est); if (cost.Failed()) return cost; + Station *st = NULL; + ret = FindJoiningStation(est, station_to_join, HasBit(p2, 0), airport_area, &st, STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST); + if (ret.Failed()) return ret; + + /* Distant join */ + if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); + + ret = BuildStationPart(&st, flags, reuse, airport_area, (GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT); + if (ret.Failed()) return ret; + + /* action to be performed */ + enum { + AIRPORT_NEW, // airport is a new station + AIRPORT_ADD, // add an airport to an existing station + AIRPORT_UPGRADE, // upgrade the airport in a station + } action = + (est != INVALID_STATION) ? AIRPORT_UPGRADE : + (st != NULL) ? AIRPORT_ADD : AIRPORT_NEW; + + if (action == AIRPORT_ADD && st->airport.tile != INVALID_TILE) { + return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT); + } + /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */ AirportTileTableIterator iter(as->table[layout], tile); Town *nearest = AirportGetNearestTown(as, iter); - uint newnoise_level = GetAirportNoiseLevelForTown(as, iter, nearest->xy); + uint newnoise_level = nearest->noise_reached + GetAirportNoiseLevelForTown(as, iter, nearest->xy); + + if (action == AIRPORT_UPGRADE) { + const AirportSpec *old_as = st->airport.GetSpec(); + AirportTileTableIterator old_iter(old_as->table[st->airport.layout], st->airport.tile); + Town *old_nearest = AirportGetNearestTown(old_as, old_iter); + if (old_nearest == nearest) { + newnoise_level -= GetAirportNoiseLevelForTown(old_as, old_iter, nearest->xy); + } + } /* Check if local auth would allow a new airport */ StringID authority_refuse_message = STR_NULL; @@ -2226,11 +2332,11 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint if (_settings_game.economy.station_noise_level) { /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */ - if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) { + if (newnoise_level > nearest->MaxTownNoise()) { authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE; authority_refuse_town = nearest; } - } else { + } else if (action != AIRPORT_UPGRADE) { Town *t = ClosestTownFromTile(tile, UINT_MAX); uint num = 0; const Station *st; @@ -2248,18 +2354,11 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint return_cmd_error(authority_refuse_message); } - Station *st = NULL; - ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), airport_area, &st); - if (ret.Failed()) return ret; - - /* Distant join */ - if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); - - ret = BuildStationPart(&st, flags, reuse, airport_area, (GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT); - if (ret.Failed()) return ret; - - if (st != NULL && st->airport.tile != INVALID_TILE) { - return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT); + if (action == AIRPORT_UPGRADE) { + /* check that the old airport can be removed */ + CommandCost r = CanRemoveAirport(st, flags); + if (r.Failed()) return r; + cost.AddCost(r); } for (AirportTileTableIterator iter(as->table[layout], tile); iter != INVALID_TILE; ++iter) { @@ -2267,8 +2366,38 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint } if (flags & DC_EXEC) { + if (action == AIRPORT_UPGRADE) { + /* delete old airport if upgrading */ + const AirportSpec *old_as = st->airport.GetSpec(); + AirportTileTableIterator old_iter(old_as->table[st->airport.layout], st->airport.tile); + Town *old_nearest = AirportGetNearestTown(old_as, old_iter); + + if (old_nearest != nearest) { + old_nearest->noise_reached -= GetAirportNoiseLevelForTown(old_as, old_iter, old_nearest->xy); + if (_settings_game.economy.station_noise_level) { + SetWindowDirty(WC_TOWN_VIEW, st->town->index); + } + } + + TILE_AREA_LOOP(tile_cur, st->airport) { + if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); + DeleteAnimatedTile(tile_cur); + DoClearSquare(tile_cur); + DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); + } + + for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { + DeleteWindowById( + WC_VEHICLE_DEPOT, st->airport.GetHangarTile(i) + ); + } + + st->rect.AfterRemoveRect(st, st->airport); + st->airport.Clear(); + } + /* Always add the noise, so there will be no need to recalculate when option toggles */ - nearest->noise_reached += newnoise_level; + nearest->noise_reached = newnoise_level; st->AddFacility(FACIL_AIRPORT, tile); st->airport.type = airport_type; @@ -2291,12 +2420,16 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint AirportTileAnimationTrigger(st, iter, AAT_BUILT); } - UpdateAirplanesOnNewStation(st); + if (action != AIRPORT_NEW) UpdateAirplanesOnNewStation(st); - Company::Get(st->owner)->infrastructure.airport++; - DirtyCompanyInfrastructureWindows(st->owner); + if (action == AIRPORT_UPGRADE) { + UpdateStationSignCoord(st); + } else { + Company::Get(st->owner)->infrastructure.airport++; + DirtyCompanyInfrastructureWindows(st->owner); + st->UpdateVirtCoord(); + } - st->UpdateVirtCoord(); UpdateStationAcceptance(st, false); st->RecomputeIndustriesNear(); InvalidateWindowData(WC_SELECT_STATION, 0, 0); @@ -2326,15 +2459,8 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; } - tile = st->airport.tile; - - CommandCost cost(EXPENSES_CONSTRUCTION); - - const Aircraft *a; - FOR_ALL_AIRCRAFT(a) { - if (!a->IsNormalAircraft()) continue; - if (a->targetairport == st->index && a->state != FLYING) return CMD_ERROR; - } + CommandCost cost = CanRemoveAirport(st, flags); + if (cost.Failed()) return cost; if (flags & DC_EXEC) { const AirportSpec *as = st->airport.GetSpec(); @@ -2344,25 +2470,14 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) 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; - - CommandCost ret = EnsureNoVehicleOnGround(tile_cur); - if (ret.Failed()) return ret; - - cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); - - if (flags & DC_EXEC) { + TILE_AREA_LOOP(tile_cur, st->airport) { if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); DeleteAnimatedTile(tile_cur); DoClearSquare(tile_cur); DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); } - } - if (flags & DC_EXEC) { /* Clear the persistent storage. */ delete st->airport.psa;