function buildTownStation(town, cargoClass, stationTile, otherTown) {
local stationId = (stationTile == null) ? AIStation.STATION_NEW : AIStation.GetStationID(stationTile);
local vehicleType = (cargoClass == AICargo.CC_MAIL) ? AIRoad.ROADVEHTYPE_TRUCK : AIRoad.ROADVEHTYPE_BUS;
// local max_spread = AIController.GetSetting("station_spread") && AIGameSettings.GetValue("distant_join_stations");
local cargoType = Utils.getCargoId(cargoClass);
local tileList = AITileList();
if(stationTile == null) {
//stationTile = AIStation.STATION_NEW;
//build square around @town and find suitable tiles for truck stops
local rectangleCoordinates = estimateTownRectangle(town);
tileList.AddRectangle(rectangleCoordinates[0], rectangleCoordinates[1]);
for(local tile = tileList.Begin(); tileList.HasNext(); tile = tileList.Next()) {
if (Utils.AreOtherStationsNearby(tile, cargoClass, stationId)) {
tileList.RemoveTile(tile);
}
}
//valuate and sort by the number of cargo tiles the station covers
tileList.Valuate(Utils.valuateTruckStopTile, cargoClass, stationId, cargoType);
tileList.RemoveValue(0);
tileList.Sort(AIList.SORT_BY_VALUE, false); //starts from corners if without sort
}
else {
//expanding existing station
if(!AIStation.IsValidStation(stationId)) {
return null;
}
local squareSize = AIGameSettings.GetValue("station_spread");
AILog.Info("AddRectangle");
tileList.AddRectangle(Utils.getValidOffsetTile(stationTile, -1 * squareSize, -1 * squareSize),
Utils.getValidOffsetTile(stationTile, squareSize, squareSize));
AILog.Info("AreOtherStationsNearby");
for(local tile = tileList.Begin(); tileList.HasNext(); tile = tileList.Next()) {
if (Utils.AreOtherStationsNearby(tile, cargoClass, stationId)) {
tileList.RemoveTile(tile);
}
}
AILog.Info("Valuate tiles");
tileList.Valuate(Utils.valuateTruckStopTile, cargoClass, stationId, cargoType);
tileList.RemoveValue(0);
}
for (local tile = tileList.Begin(); tileList.HasNext(); tile = tileList.Next()) {
AILog.Info("cycling tileList");
//get adjacent tiles
local adjTileList = Utils.getAdjacentTiles(tile);
local adjRoadTiles = AITileList();
adjRoadTiles.AddList(adjTileList);
adjRoadTiles.Valuate(AIRoad.IsRoadTile);
adjRoadTiles.KeepValue(1);
AIRoad.SetCurrentRoadType(AIRoad.ROADTYPE_ROAD);
local adjRoadCount = adjRoadTiles.Count();
AILog.Info("computing adjacentAirport");
local adjacentAirport = Utils.checkAdjacentAirport(tile, cargoClass, stationId);
AILog.Info("switching cases");
switch (adjRoadCount) {
//case where station tile has no adjacent road tiles
case 0:
/* expanding via this case can be too slow */
if (stationTile != null) {
continue;
}
local closestAdjTile = null;
for (local adjTile = adjTileList.Begin(); adjTileList.HasNext(); adjTile = adjTileList.Next()) {
if (!AITile.IsBuildable(adjTile) || AITile.GetSlope(adjTile) != AITile.SLOPE_FLAT) {
continue;
}
if (closestAdjTile == null) {
closestAdjTile = adjTile;
}
if (AITown.GetDistanceManhattanToTile(otherTown, adjTile) < closestAdjTile) {
closestAdjTile = adjTile;
}
}
if (closestAdjTile == null) {
continue;
}
local counter = 0;
do {
if (!TestBuildRoadStation().TryBuild(tile, closestAdjTile, vehicleType, adjacentAirport)) {
//if((AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") && (AIError.GetLastErrorString() != "ERR_PRECONDITION_FAILED")) {
//AILog.Warning("Couldnt build station! " + AIError.GetLastErrorString());
//}
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < 1);
if(counter == 1) {
continue;
}
if (stationTile != null) {
if (buildRoad(tile, AITown.GetLocation(otherTown), true) == null) {
local counter = 0;
do {
if (!TestRemoveRoadStation().TryRemove(tile)) {
++counter;
}
else {
// AILog.Warning("case" + adjRoadCount + "; Removed road station tile at " + tile);
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if (counter == (stationTile == null ? 500 : 1)) {
AILog.Error("Failed to remove road station tile at " + tile + " - " + AIError.GetLastErrorString());
continue; // TODO: station should have been removed by now, but it wasn't!
} else {
continue;
}
}
}
AILog.Info("Station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
//AISign.BuildSign(tile, "" + adjRoadCount);
return tile;
break;
case 1:
local adjTile = adjRoadTiles.Begin();
if (AIRoad.IsDriveThroughRoadStationTile(adjTile)) {
continue;
}
local heighDifference = abs(AITile.GetMaxHeight(tile) - AITile.GetMaxHeight(adjTile));
if(heighDifference != 0) {
continue;
}
local counter = 0;
do {
/* Try to build a road station or a drive through road station */
if (!TestBuildRoadStation().TryBuild(tile, adjTile, vehicleType, adjacentAirport) || AIRoad.IsRoadTile(tile)
&& !TestBuildDriveThroughRoadStation().TryBuild(tile, adjTile, vehicleType, adjacentAirport)) {
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < 1);
if (counter == 1) {
/* Failed to build station, try the next location */
continue;
}
else {
/* With the road station built, try to connect it to the road */
local drivethrough = AIRoad.IsDriveThroughRoadStationTile(tile);
local counter = 0;
do {
if(!TestBuildRoad().TryBuild(tile, adjTile) && AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") {
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if(counter == (stationTile == null ? 500 : 1)) {
/* Failed to connect road to the station. Try to remove the station we had built then */
local counter = 0;
do {
if (!TestRemoveRoadStation().TryRemove(tile)) {
++counter;
}
else {
if (drivethrough) {
// AILog.Warning("case" + adjRoadCount + "; Removed drive through station tile at " + tile);
} else {
// AILog.Warning("case" + adjRoadCount + "; Removed road station tile at " + tile);
}
break;
}
AIController.Sleep(1);
} while (counter < (stationTile == null ? 500 : 1));
if (counter == (stationTile == null ? 500 : 1)) {
if (drivethrough) {
AILog.Error("Failed to remove drive through station tile at " + tile + " - " + AIError.GetLastErrorString());
} else {
AILog.Error("Failed to remove road station tile at " + tile + " - " + AIError.GetLastErrorString());
}
continue; // TODO: station should have been removed by now, but it wasn't!
} else {
/* The station was successfully removed after failing to connect it to the road. Try it all over again in the next location */
continue;
}
}
else {
/* The road was successfully connected to the station */
if (!AIRoad.IsDriveThroughRoadStationTile(tile)) {
AILog.Info("Station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
} else {
AILog.Info("Drivethrough station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
}
//AISign.BuildSign(tile, "" + adjRoadCount);
return tile;
}
}
break;
case 2:
local adjTile = adjRoadTiles.Begin();
local nextAdjTile = adjRoadTiles.Next();
//dont build drivethrough station next to regular station
adjTileList.RemoveItem(adjTile);
adjTileList.RemoveItem(nextAdjTile);
local found = false;
for(local t = adjTileList.Begin(); adjTileList.HasNext(); t = adjTileList.Next()) {
if (AIRoad.IsRoadStationTile(t) && AIRoad.GetRoadStationFrontTile(t) == tile ||
AIRoad.IsRoadDepotTile(t) && AIRoad.GetRoadDepotFrontTile(t) == tile) {
found = true;
break;
}
}
if(found) {
continue;
}
local heighDifference = abs(AITile.GetMaxHeight(nextAdjTile) - AITile.GetMaxHeight(adjTile));
if(heighDifference != 0) {
continue;
}
//check whether adjacent tiles are opposite
local opposite = false;
if((AIMap.GetTileX(adjTile) == AIMap.GetTileX(nextAdjTile))
|| (AIMap.GetTileY(adjTile) == AIMap.GetTileY(nextAdjTile))) {
opposite = true;
}
if(AIRoad.IsRoadTile(tile)) {
if(!opposite) {
continue;
}
}
if(opposite) {
local counter = 0;
do {
if (!TestBuildDriveThroughRoadStation().TryBuild(tile, adjTile, vehicleType, adjacentAirport)) {
if((AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") && (AIError.GetLastErrorString() != "ERR_PRECONDITION_FAILED")) {
if(AIError.GetLastErrorString() != "ERR_LOCAL_AUTHORITY_REFUSES") {
//AILog.Warning("Couldnt build station! " + AIError.GetLastErrorString());
}
}
++counter;
}
else {
break;
}
AIController.Sleep(1)
} while(counter < 1);
if (counter == 1) {
continue;
}
else {
local counter = 0;
do {
if(!TestBuildRoad().TryBuild(adjTile, nextAdjTile) && AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") {
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if(counter == (stationTile == null ? 500 : 1)) {
local counter = 0;
do {
if (!TestRemoveRoadStation().TryRemove(tile)) {
++counter;
}
else {
// AILog.Warning("case" + adjRoadCount + "; Removed drive through station tile at " + tile);
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if (counter == (stationTile == null ? 500 : 1)) {
AILog.Error("Failed to remove drive through station tile at " + tile + " - " + AIError.GetLastErrorString());
continue; // TODO: station should have been removed by now, but it wasn't!
} else {
continue;
}
}
else {
AILog.Info("Drivethrough station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
//AISign.BuildSign(tile, "" + adjRoadCount);
return tile;
}
}
}
//similar to case 1 if adjacent tiles are not opposite
else {
local counter = 0;
do {
if (!TestBuildRoadStation().TryBuild(tile, adjTile, vehicleType, adjacentAirport)) {
if((AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") && (AIError.GetLastErrorString() != "ERR_PRECONDITION_FAILED")) {
if(AIError.GetLastErrorString() != "ERR_LOCAL_AUTHORITY_REFUSES") {
//AILog.Warning("Couldnt build station! " + AIError.GetLastErrorString());
}
}
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < 1);
if (counter == 1) {
continue;
}
else {
local counter = 0;
do {
if(!TestBuildRoad().TryBuild(tile, adjTile) && AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") {
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if(counter == (stationTile == null ? 500 : 1)) {
local counter = 0;
do {
if (!TestRemoveRoadStation().TryRemove(tile)) {
++counter;
}
else {
// AILog.Warning("case" + adjRoadCount + "; Removed road station tile at " + tile);
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if (counter == (stationTile == null ? 500 : 1)) {
AILog.Error("Failed to remove road station tile at " + tile + " - " + AIError.GetLastErrorString());
continue; // TODO: station should have been removed by now, but it wasn't!
} else {
continue;
}
}
else {
AILog.Info("Station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
//AISign.BuildSign(tile, "" + adjRoadCount);
return tile;
}
}
}
break;
case 3:
case 4:
//similar to case 2 but always builds drivethrough station
AIRoad.SetCurrentRoadType(AIRoad.ROADTYPE_ROAD);
if(AIRoad.IsRoadTile(tile)) {
continue;
}
//check whether adjacent tiles are opposite
local adjTile = Utils.getOffsetTile(tile, -1, 0);
local nextAdjTile = Utils.getOffsetTile(tile, 1, 0);
if (!(adjRoadTiles.HasItem(adjTile) && adjRoadTiles.HasItem(nextAdjTile))) {
adjTile = Utils.getOffsetTile(tile, 0, -1);
nextAdjTile = Utils.getOffsetTile(tile, 0, 1);
}
local heighDifference = abs(AITile.GetMaxHeight(nextAdjTile) - AITile.GetMaxHeight(adjTile));
if(heighDifference != 0) {
continue;
}
local counter = 0;
do {
if (!TestBuildDriveThroughRoadStation().TryBuild(tile, adjTile, vehicleType, adjacentAirport)) {
if((AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") && (AIError.GetLastErrorString() != "ERR_PRECONDITION_FAILED")) {
if(AIError.GetLastErrorString() != "ERR_LOCAL_AUTHORITY_REFUSES") {
//AILog.Warning("Couldnt build station! " + AIError.GetLastErrorString());
}
}
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < 1);
if (counter == 1) {
continue;
}
else {
local counter = 0;
do {
if(!TestBuildRoad().TryBuild(adjTile, nextAdjTile) && AIError.GetLastErrorString() != "ERR_ALREADY_BUILT") {
++counter;
}
else {
break;
}
AIController.Sleep(1);
} while(counter < (stationTile == null ? 500 : 1));
if(counter == (stationTile == null ? 500 : 1)) {
local counter = 0;
do {
if (!TestRemoveRoadStation().TryRemove(tile)) {
++counter;
}
else {
// AILog.Warning("case" + adjRoadCount + "; Removed drive through station tile at " + tile);
break;
}
AIController.Sleep(1);
} while (counter < stationTile == null ? 500 : 1);
if (counter == (stationTile == null ? 500 : 1)) {
AILog.Error("Failed to remove drive through station tile at " + tile + " - " + AIError.GetLastErrorString());
continue; // TODO: station should have been removed by now, but it wasn't!
} else {
continue;
}
}
else {
AILog.Info("Drivethrough station built in " + AITown.GetName(town) + " at tile " + tile + "! case" + adjRoadCount);
//AISign.BuildSign(tile, "" + adjRoadCount);
return tile;
}
}
break;
default:
break;
}
}
return null;
}