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; }