/**
* Find a suitable spot for an airport, walking all towns hoping to find one.
* When a town is used, it is marked as such and not re-used.
*/
function WrightAI::FindSuitableAirportSpot(airportTypes, airport1_tile, large_aircraft, small_aircraft, helicopter)
{
local town_list = AITownList();
/* Remove all the towns we already used */
town_list.RemoveList(this.towns_used);
/* Remove towns we have tried recently */
town_list.RemoveList(this.triedTowns);
if (AIController.GetSetting("cities_only")) {
town_list.Valuate(AITown.IsCity);
town_list.KeepValue(1);
}
town_list.Valuate(AITown.GetLastMonthProduction, this.cargoId);
town_list.KeepAboveValue(LuDiAIAfterFix.cargoClass == AICargo.CC_PASSENGERS ? 70 : 18);
if (AIController.GetSetting("pick_random")) {
town_list.Valuate(AIBase.RandItem);
} else {
town_list.Sort(AIList.SORT_BY_VALUE, false);
}
/* Keep the best 10, if we can't find a station in there, just leave it anyway */
town_list.KeepTop(10);
if (town_list.Count() <= 1) {
this.triedTowns.Clear();
}
/* Now find a suitable town */
for (local town = town_list.Begin(); town_list.HasNext(); town = town_list.Next()) {
/* Don't make this a CPU hog */
AIController.Sleep(1);
local town_tile = AITown.GetLocation(town);
// AILog.Info("Checking town " + AITown.GetName(town));
for (local i = airportTypes.Begin(); airportTypes.HasNext(); i = airportTypes.Next()) {
local a = airportTypes.GetValue(i);
local airport_x = AIAirport.GetAirportWidth(a);
local airport_y = AIAirport.GetAirportHeight(a);
local airport_rad = AIAirport.GetAirportCoverageRadius(a);
// AILog.Info("Checking airport of type " + a);
if (!AIAirport.IsValidAirportType(a)) continue;
if (large_aircraft && a != AIAirport.AT_INTERCON && a != AIAirport.AT_INTERNATIONAL && a != AIAirport.AT_METROPOLITAN && a != AIAirport.AT_LARGE) {
continue;
}
if (small_aircraft && a != AIAirport.AT_INTERCON && a != AIAirport.AT_INTERNATIONAL && a != AIAirport.AT_METROPOLITAN && a != AIAirport.AT_LARGE && a != AIAirport.AT_SMALL && a != AIAirport.AT_COMMUTER) {
continue;
}
if (helicopter && a != AIAirport.AT_HELISTATION && a != AIAirport.AT_HELIDEPOT && a != AIAirport.AT_HELIPORT) {
if (AIAirport.IsValidAirportType(AIAirport.AT_HELISTATION) || AIAirport.IsValidAirportType(AIAirport.AT_HELIDEPOT)) {
continue;
}
}
local engine_list = null;
if (large_aircraft) {
engine_list = this.GetBestAirportEngine(AIAirport.AT_LARGE, true);
} else if (small_aircraft) {
engine_list = this.GetBestAirportEngine(AIAirport.AT_SMALL, true);
} else if (helicopter) {
engine_list = this.GetBestAirportEngine(AIAirport.AT_HELIPORT, true);
} else {
engine_list = this.GetBestAirportEngine(a, true);
}
if (engine_list == null) continue;
local engine = WrightAI.GetBestEngineIncome(engine_list);
if (engine[0] == null) continue;
local fakedist = engine[1];
/* Best engine is unprofitable enough */
if (fakedist == 0) continue;
local rectangleCoordinates = this.TownAirportRadRect(a, town);
local tileList = AITileList();
tileList.AddRectangle(rectangleCoordinates[0], rectangleCoordinates[1]);
tileList.Valuate(AITile.GetClosestTown);
tileList.KeepValue(town);
if (tileList.Count() == 0) continue;
if (airport1_tile != 0) {
/* If we have the tile of the first airport, we don't want the second airport to be as close or as further */
tileList.Valuate(AITile.GetDistanceSquareToTile, airport1_tile);
local max_order_dist = WrightAI.GetMaximumOrderDistance(engine[0]);
local max_dist = max_order_dist > AIMap.GetMapSize() ? AIMap.GetMapSize() : max_order_dist;
local min_order_dist = (fakedist / 2) * (fakedist / 2);
local min_dist = min_order_dist > max_dist * 3 / 4 ? max_dist * 3 / 4 > AIMap.GetMapSize() / 8 ? AIMap.GetMapSize() / 8 : max_dist * 3 / 4 : min_order_dist;
tileList.KeepBetweenValue(min_dist, max_dist);
// AILog.Info("KeepBetweenValue " + min_dist + " and " + max_dist);
tileList.Valuate(WrightAI.DistanceRealFake, airport1_tile);
tileList.KeepBelowValue(fakedist * 11 / 10);
if (tileList.Count() == 0) continue;
}
tileList.Valuate(AITile.IsBuildableRectangle, airport_x, airport_y);
tileList.KeepValue(1)
if (tileList.Count() == 0) continue;
/* Sort on acceptance, remove places that don't have acceptance */
tileList.Valuate(AITile.GetCargoAcceptance, this.cargoId, airport_x, airport_y, airport_rad);
tileList.RemoveBelowValue(10);
if (tileList.Count() == 0) continue;
tileList.Valuate(AITile.GetCargoProduction, this.cargoId, airport_x, airport_y, airport_rad);
tileList.RemoveBelowValue(LuDiAIAfterFix.cargoClass == AICargo.CC_PASSENGERS ? 70 : 18);
/* Couldn't find a suitable place for this town, skip to the next */
if (tileList.Count() == 0) continue;
tileList.Sort(AIList.SORT_BY_VALUE, false);
/* Walk all the tiles and see if we can build the airport at all */
local good_tile = 0;
for (local tile = tileList.Begin(); tileList.HasNext(); tile = tileList.Next()) {
local adjacentStationId = checkAdjacentStation(tile, a);
local nearest_town;
if (adjacentStationId == AIStation.STATION_NEW) {
nearest_town = AITile.GetClosestTown(tile);
} else {
nearest_town = AIStation.GetNearestTown(adjacentStationId);
if (nearest_town != town) {
adjacentStationId = AIStation.STATION_NEW;
nearest_town = AITile.GetClosestTown(tile);
}
}
local noise = AIAirport.GetNoiseLevelIncrease(tile, a);
local allowed_noise = AITown.GetAllowedNoise(AIAirport.GetNearestTown(tile, a));
if (noise > allowed_noise) continue;
/* Don't build airport if there is any competitor station in the vicinity, or an airport of mine */
local airportcoverage = this.TownAirportRadRect(a, tile, false);
local tileList2 = AITileList();
tileList2.AddRectangle(airportcoverage[0], airportcoverage[1]);
local nearby_station = false;
for (local t = tileList2.Begin(); tileList2.HasNext(); t = tileList2.Next()) {
if (AITile.IsStationTile(t) && (AIAirport.IsAirportTile(t) || AITile.GetOwner(t) != AICompany.ResolveCompanyID(AICompany.COMPANY_SELF) && AIController.GetSetting("is_friendly"))) {
nearby_station = true;
break;
}
}
if (nearby_station) continue;
// AISign.BuildSign(tile, ("" + noise + " <= " + allowed_noise + ""));
AIController.Sleep(1);
if (AITestMode() && !AIAirport.BuildAirport(tile, a, adjacentStationId)) continue;
good_tile = tile;
AILog.Info("Found a good spot for an airport near " + AITown.GetName(nearest_town) + " at tile " + good_tile);
/* Mark the town as used, so we don't use it again */
assert(!towns_used.HasItem(nearest_town) && !triedTowns.HasItem(nearest_town) && nearest_town == town);
this.triedTowns.AddItem(nearest_town, good_tile);
if (a == AIAirport.AT_INTERCON || a == AIAirport.AT_INTERNATIONAL || a == AIAirport.AT_METROPOLITAN || a == AIAirport.AT_LARGE) {
large_aircraft = true;
}
if (a == AIAirport.AT_SMALL || a == AIAirport.AT_COMMUTER) {
small_aircraft = true;
}
if (a == AIAirport.AT_HELISTATION || a == AIAirport.AT_HELIDEPOT || a == AIAirport.AT_HELIPORT) {
helicopter = true;
}
return [good_tile, a, large_aircraft, small_aircraft, helicopter, adjacentStationId];
}
}
/* All airport types were tried on this town and no suitable location was found */
assert(!triedTowns.HasItem(town));
this.triedTowns.AddItem(town, town_tile);
}
/* We haven't found a suitable location for any airport type in any town */
return [-1, AIAirport.AT_INVALID, large_aircraft, small_aircraft, helicopter, -1];
}