diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 3495f111..694b9e44 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -937,6 +937,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); + 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 @@ -1016,16 +1057,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; @@ -1068,11 +1109,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); } /** @@ -1086,7 +1128,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); } /** @@ -1668,7 +1710,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); } /** @@ -2080,6 +2122,44 @@ 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]); + + if (flags & DC_EXEC) { + if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); + DeleteAnimatedTile(tile_cur); + DoClearSquare(tile_cur); + DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); + } + } + + return cost; +} + + /** * Place an Airport. * @param tile tile where airport will be built @@ -2123,22 +2203,49 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT); } - CommandCost cost = CheckFlatLand(TileArea(tile, w, h), flags); + StationID est = INVALID_STATION; + CommandCost cost = CheckFlatLandAirport(TileArea(tile, w, h), flags, &est); if (cost.Failed()) return cost; + Station *st = NULL; + ret = FindJoiningStation(est, station_to_join, HasBit(p2, 0), TileArea(tile, w, h), &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); + + /* Find a deleted station close to us */ + if (st == NULL && reuse) st = GetClosestDeletedStation(tile); + + /* 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; + /* 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, tile); - uint newnoise_level = GetAirportNoiseLevelForTown(as, nearest->xy, tile); + uint newnoise_level = nearest->noise_reached + GetAirportNoiseLevelForTown(as, nearest->xy, tile); + + if (action == AIRPORT_UPGRADE) { + Town *old_nearest = AirportGetNearestTown(st->airport.GetSpec(), st->airport.tile); + if (old_nearest == nearest) { + newnoise_level -= GetAirportNoiseLevelForTown(st->airport.GetSpec(), nearest->xy, st->airport.tile); + } + } /* Check if local auth would allow a new airport */ StringID authority_refuse_message = STR_NULL; 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; } - } else { + } else if (action != AIRPORT_UPGRADE) { uint num = 0; const Station *st; FOR_ALL_STATIONS(st) { @@ -2154,17 +2261,7 @@ 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), TileArea(tile, w, h), &st); - if (ret.Failed()) return ret; - - /* Distant join */ - if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); - - /* Find a deleted station close to us */ - if (st == NULL && reuse) st = GetClosestDeletedStation(tile); - - if (st != NULL) { + if (action != AIRPORT_NEW) { if (st->owner != _current_company) { return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION); } @@ -2172,8 +2269,15 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint CommandCost ret = st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST); if (ret.Failed()) return ret; - if (st->airport.tile != INVALID_TILE) { - return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT); + if (action != AIRPORT_UPGRADE) { + if (st->airport.tile != INVALID_TILE) { + return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT); + } + } else { + /* check that the old airport can be removed */ + CommandCost r = CanRemoveAirport(st, flags); + if (r.Failed()) return r; + cost.AddCost(r); } } else { /* allocate and initialize new station */ @@ -2197,8 +2301,28 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint } while ((++it)->ti.x != -0x80); if (flags & DC_EXEC) { + if (action == AIRPORT_UPGRADE) { + /* delete old airport if upgrading */ + TileIndex otile = st->airport.tile; + const AirportSpec *oas = st->airport.GetSpec(); + + 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(); + + Town *old_nearest = AirportGetNearestTown(oas, otile); + if (old_nearest != nearest) { + old_nearest->noise_reached -= GetAirportNoiseLevelForTown(oas, nearest->xy, otile); + } + } + /* 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; @@ -2225,9 +2349,14 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint AirportTileAnimationTrigger(st, cur_tile, AAT_BUILT); } while ((++it)->ti.x != -0x80); - UpdateAirplanesOnNewStation(st); + if (action != AIRPORT_NEW) UpdateAirplanesOnNewStation(st); + + if (action == AIRPORT_UPGRADE) { + UpdateStationSignCoord(st); + } else { + st->UpdateVirtCoord(); + } - st->UpdateVirtCoord(); UpdateStationAcceptance(st, false); st->RecomputeIndustriesNear(); InvalidateWindowData(WC_SELECT_STATION, 0, 0); @@ -2257,36 +2386,14 @@ 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; - } - - 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) { - if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); - DeleteAnimatedTile(tile_cur); - DoClearSquare(tile_cur); - DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); - } - } + CommandCost cost = CanRemoveAirport(st, flags); + if (cost.Failed()) return cost; if (flags & DC_EXEC) { /* Clear the persistent storage. */ delete st->airport.psa; + TileIndex tile = st->airport.tile; const AirportSpec *as = st->airport.GetSpec(); for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { DeleteWindowById(