|
|
|
@ -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 <class T, StringID error_message>
|
|
|
|
|
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
|
|
|
|
|
template <class T>
|
|
|
|
|
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<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
|
|
|
|
|
return FindJoiningBaseStation<Station>(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<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
|
|
|
|
|
return FindJoiningBaseStation<Waypoint>(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<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st);
|
|
|
|
|
return FindJoiningBaseStation<Station>(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;
|
|
|
|
|
|
|
|
|
|