Loading

RoadPathfinder.nut

  1. require("aystar.nut");
  2.  
  3. /**
  4.  * A Road Pathfinder.
  5.  *  This road pathfinder tries to find a buildable / existing route for
  6.  *  road vehicles. You can changes the costs below using for example
  7.  *  roadpf.cost.turn = 30. Note that it's not allowed to change the cost
  8.  *  between consecutive calls to FindPath. You can change the cost before
  9.  *  the first call to FindPath and after FindPath has returned an actual
  10.  *  route. To use only existing roads, set cost.no_existing_road to
  11.  *  cost.max_cost.
  12.  */
  13. class Road
  14. {
  15.     _aystar_class = AyStar;
  16.     _max_cost = null;              ///< The maximum cost for a route.
  17.     _cost_tile = null;             ///< The cost for a single road tile, bridge tile or tunnel tile.
  18.     _cost_no_existing_road = null; ///< The cost that is added to _cost_tile if no road connection exists between two tiles. Cost is doubled when the tile to enter has no road, no bridge and no tunnel.
  19.     _cost_turn = null;             ///< The cost that is added to _cost_tile if the direction changes.
  20.     _cost_slope = null;            ///< The extra cost if a road tile or bridge head is sloped.
  21.     _cost_bridge_per_tile = null;  ///< The extra cost per tile for a bridge.
  22.     _cost_tunnel_per_tile = null;  ///< The extra cost per tile for a tunnel.
  23.     _cost_coast = null;            ///< The extra cost if a new road tile or new bridge head is on a coast tile with water.
  24.     _cost_drive_through = null;    ///< The extra cost if a road tile is part of a drive through road station.
  25.     _max_bridge_length = null;     ///< The maximum length of a bridge that will be built. Length includes bridge heads.
  26.     _max_tunnel_length = null;     ///< The maximum length of a tunnel that will be built. Length includes entrance and exit.
  27.     _pathfinder = null;            ///< A reference to the used AyStar object.
  28.  
  29.     cost = null;                   ///< Used to change the costs.
  30.     _running = null;
  31.     _map_size_x = AIMap.GetMapSizeX();
  32.  
  33.     constructor()
  34.     {
  35.         this._max_cost = 10000000;
  36.         this._cost_tile = 100;
  37.         this._cost_no_existing_road = 40;
  38.         this._cost_turn = 100;
  39.         this._cost_slope = 200;
  40.         this._cost_bridge_per_tile = 150;
  41.         this._cost_tunnel_per_tile = 120;
  42.         this._cost_coast = 20;
  43.         this._cost_drive_through = 800;
  44.         this._max_bridge_length = 10;
  45.         this._max_tunnel_length = 20;
  46.         this._pathfinder = this._aystar_class(this, this._Cost, this._Estimate, this._Neighbours, this._CheckDirection);
  47.  
  48.         this.cost = this.Cost(this);
  49.         this._running = false;
  50.     }
  51.  
  52.     /**
  53.      * Initialize a path search between sources and goals.
  54.      * @param sources The source tiles.
  55.      * @param goals The target tiles.
  56.      * @see AyStar::InitializePath()
  57.      */
  58.     function InitializePath(sources, goals) {
  59.         local nsources = [];
  60.  
  61.         foreach (node in sources) {
  62.             nsources.push([node, 0xFF]);
  63.         }
  64.         this._pathfinder.InitializePath(nsources, goals);
  65.     }
  66.  
  67.     /**
  68.      * Try to find the path as indicated with InitializePath with the lowest cost.
  69.      * @param iterations After how many iterations it should abort for a moment.
  70.      *  This value should either be -1 for infinite, or > 0. Any other value
  71.      *  aborts immediatly and will never find a path.
  72.      * @return A route if one was found, or false if the amount of iterations was
  73.      *  reached, or null if no path was found.
  74.      *  You can call this function over and over as long as it returns false,
  75.      *  which is an indication it is not yet done looking for a route.
  76.      * @see AyStar::FindPath()
  77.      */
  78.     function FindPath(iterations);
  79. };
  80.  
  81. class Road.Cost
  82. {
  83.     _main = null;
  84.  
  85.     function _set(idx, val)
  86.     {
  87.         if (this._main._running) throw("You are not allowed to change parameters of a running pathfinder.");
  88.  
  89.         switch (idx) {
  90.             case "max_cost":          this._main._max_cost = val; break;
  91.             case "tile":              this._main._cost_tile = val; break;
  92.             case "no_existing_road":  this._main._cost_no_existing_road = val; break;
  93.             case "turn":              this._main._cost_turn = val; break;
  94.             case "slope":             this._main._cost_slope = val; break;
  95.             case "bridge_per_tile":   this._main._cost_bridge_per_tile = val; break;
  96.             case "tunnel_per_tile":   this._main._cost_tunnel_per_tile = val; break;
  97.             case "coast":             this._main._cost_coast = val; break;
  98.             case "drive_through":     this._main._cost_drive_through = val; break;
  99.             case "max_bridge_length": this._main._max_bridge_length = val; break;
  100.             case "max_tunnel_length": this._main._max_tunnel_length = val; break;
  101.             default: throw("the index '" + idx + "' does not exist");
  102.         }
  103.  
  104.         return val;
  105.     }
  106.  
  107.     function _get(idx)
  108.     {
  109.         switch (idx) {
  110.             case "max_cost":          return this._main._max_cost;
  111.             case "tile":              return this._main._cost_tile;
  112.             case "no_existing_road":  return this._main._cost_no_existing_road;
  113.             case "turn":              return this._main._cost_turn;
  114.             case "slope":             return this._main._cost_slope;
  115.             case "bridge_per_tile":   return this._main._cost_bridge_per_tile;
  116.             case "tunnel_per_tile":   return this._main._cost_tunnel_per_tile;
  117.             case "coast":             return this._main._cost_coast;
  118.             case "drive_through":     return this._main._cost_drive_through;
  119.             case "max_bridge_length": return this._main._max_bridge_length;
  120.             case "max_tunnel_length": return this._main._max_tunnel_length;
  121.             default: throw("the index '" + idx + "' does not exist");
  122.         }
  123.     }
  124.  
  125.     constructor(main)
  126.     {
  127.         this._main = main;
  128.     }
  129. };
  130.  
  131. function Road::FindPath(iterations)
  132. {
  133.     local test_mode = AITestMode();
  134.     local ret = this._pathfinder.FindPath(iterations);
  135.     this._running = (ret == false) ? true : false;
  136.     return ret;
  137. }
  138.  
  139. function Road::_GetBridgeNumSlopesEfficient(end_a, end_b,
  140.         map_size_x = Road._map_size_x, _AITile = AITile)
  141. {
  142. //  AILog.Info("_GetBridgeNumSlopesEfficient in");
  143. //  local before = AIController.GetOpsTillSuspend();
  144.     local slopes = 0;
  145.     local direction = (end_b - end_a) / AIMap.DistanceManhattan(end_a, end_b);
  146.     local slope = _AITile.GetSlope(end_a);
  147.     if (!((slope == _AITile.SLOPE_NE && direction == 1) || (slope == _AITile.SLOPE_SE && direction == -map_size_x) ||
  148.             (slope == _AITile.SLOPE_SW && direction == -1) || (slope == _AITile.SLOPE_NW && direction == map_size_x) ||
  149.             slope == _AITile.SLOPE_N || slope == _AITile.SLOPE_E || slope == _AITile.SLOPE_S || slope == _AITile.SLOPE_W)) {
  150.         slopes++;
  151.     }
  152.  
  153.     slope = _AITile.GetSlope(end_b);
  154.     direction = -direction;
  155.     if (!((slope == _AITile.SLOPE_NE && direction == 1) || (slope == _AITile.SLOPE_SE && direction == -map_size_x) ||
  156.             (slope == _AITile.SLOPE_SW && direction == -1) || (slope == _AITile.SLOPE_NW && direction == map_size_x) ||
  157.             slope == _AITile.SLOPE_N || slope == _AITile.SLOPE_E || slope == _AITile.SLOPE_S || slope == _AITile.SLOPE_W)) {
  158.         slopes++;
  159.     }
  160. //  AILog.Info("Measured ops _GetBridgeNumSlopesEfficient: " + (before - AIController.GetOpsTillSuspend()) + " ; end_a = " + end_a + " ; end_b = " + end_b);
  161. //  AILog.Info("_GetBridgeNumSlopesEfficient out");
  162.     return slopes;
  163. }
  164.  
  165. function Road::_GetBridgeNumSlopes(end_a, end_b)
  166. {
  167. //  AILog.Info("_GetBridgeNumSlopes in");
  168. //  local before = AIController.GetOpsTillSuspend();
  169.     local slopes = 0;
  170.     local direction = (end_b - end_a) / AIMap.DistanceManhattan(end_a, end_b);
  171.     local slope = AITile.GetSlope(end_a);
  172.     if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
  173.             (slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
  174.             slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
  175.         slopes++;
  176.     }
  177.  
  178.     local slope = AITile.GetSlope(end_b);
  179.     direction = -direction;
  180.     if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
  181.             (slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
  182.             slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
  183.         slopes++;
  184.     }
  185. //  AILog.Info("Measured ops _GetBridgeNumSlopes: " + (before - AIController.GetOpsTillSuspend()) + " ; end_a = " + end_a + " ; end_b = " + end_b);
  186. //  AILog.Info("_GetBridgeNumSlopes out");
  187.     return slopes;
  188. }
  189.  
  190. function Road::_CostHelperEfficient(self, path, new_tile, coast_cost_only = null,
  191.         _AIBridge = AIBridge, _AITunnel = AITunnel, _AIRoad = AIRoad, _AITile = AITile, _AICompany = AICompany)
  192. {
  193. //  AILog.Info("_CostHelperEfficient in");
  194. //  local before = AIController.GetOpsTillSuspend();
  195.     local prev_tile = path.GetTile();
  196.     local cost = 0;
  197.  
  198.     if (coast_cost_only != true) {
  199.         cost += self._cost_tile;
  200.  
  201.         local dist = 0;
  202.         local par_tile = 0;
  203.         if (path.GetParent() != null) {
  204.             dist = AIMap.DistanceManhattan(path.GetParent().GetTile(), prev_tile);
  205.             par_tile = path.GetParent().GetTile();
  206.         }
  207.  
  208.         if (dist == 1) {
  209.             /* Check for a turn. We do this by substracting the TileID of the current node from
  210.              * the TileID of the previous node and comparing that to the difference between the
  211.              * previous node and the node before that. */
  212.             if (prev_tile - par_tile != new_tile - prev_tile) {
  213.                 cost += self._cost_turn;
  214.             }
  215.  
  216.             /* Check if the last tile was sloped. */
  217.             if (!_AIBridge.IsBridgeTile(prev_tile) && !_AITunnel.IsTunnelTile(prev_tile) && self._IsSlopedRoadEfficient(par_tile, prev_tile, new_tile)) {
  218.                 cost += self._cost_slope;
  219.             }
  220.         }
  221.  
  222.         if (!_AIRoad.AreRoadTilesConnected(prev_tile, new_tile)) {
  223.             cost += self._cost_no_existing_road * 2;
  224.             if (_AIRoad.IsRoadTile(new_tile) || _AIBridge.IsBridgeTile(new_tile) || _AITunnel.IsTunnelTile(new_tile) ||
  225.                     (_AIRoad.IsRoadStationTile(new_tile) || _AIRoad.IsRoadDepotTile(new_tile)) && _AITile.GetOwner(new_tile) == _AICompany.ResolveCompanyID(_AICompany.COMPANY_SELF)) {
  226.                 cost -= self._cost_no_existing_road;
  227.             }
  228.         }
  229.        
  230.         if (_AIRoad.IsDriveThroughRoadStationTile(new_tile)) {
  231.             cost += self._cost_drive_through;
  232.         }
  233.     }
  234.  
  235.     if (coast_cost_only != null) {
  236.         /* Check if the new tile is a coast tile with water. */
  237.         if (_AITile.IsCoastTile(new_tile) && _AITile.HasTransportType(new_tile, AITile.TRANSPORT_WATER)) {
  238.             cost += self._cost_coast;
  239.         }
  240.     }
  241.  
  242. //  AILog.Info("Measured ops _CostHelperEfficient: " + (before - AIController.GetOpsTillSuspend()) + " ; coast_cost_only = " + (coast_cost_only == null ? "null" : coast_cost_only) + " ; par_tile = " + (path.GetParent() != null ? path.GetParent().GetTile() : "null") + " ; prev_tile = " + prev_tile + " ; new_tile = " + new_tile);
  243. //  AILog.Info("_CostHelperEfficient out");
  244.     return cost;
  245. }
  246.  
  247. function Road::_CostHelper(self, path, new_tile, coast_cost_only = null)
  248. {
  249. //  AILog.Info("_CostHelper in");
  250. //  local before = AIController.GetOpsTillSuspend();
  251.     local prev_tile = path.GetTile();
  252.     local cost = 0;
  253.  
  254.     if (coast_cost_only != true) {
  255.         cost += self._cost_tile;
  256.         local dist = 0;
  257.         local par_tile = 0;
  258.         if (path.GetParent() != null) {
  259.             dist = AIMap.DistanceManhattan(path.GetParent().GetTile(), prev_tile);
  260.             par_tile = path.GetParent().GetTile();
  261.         }
  262.  
  263.         if (dist == 1) {
  264.             /* Check for a turn. We do this by substracting the TileID of the current node from
  265.              * the TileID of the previous node and comparing that to the difference between the
  266.              * previous node and the node before that. */
  267.             if ((prev_tile - par_tile) != (new_tile - prev_tile)) {
  268.                 cost += self._cost_turn;
  269.             }
  270.  
  271.             /* Check if the last tile was sloped. */
  272.             if (!AIBridge.IsBridgeTile(prev_tile) && !AITunnel.IsTunnelTile(prev_tile) && self._IsSlopedRoad(par_tile, prev_tile, new_tile)) {
  273.                 cost += self._cost_slope;
  274.             }
  275.         }
  276.  
  277.         if (!AIRoad.AreRoadTilesConnected(prev_tile, new_tile)) {
  278.             cost += self._cost_no_existing_road * 2;
  279.             if (AIRoad.IsRoadTile(new_tile) || AIBridge.IsBridgeTile(new_tile) || AITunnel.IsTunnelTile(new_tile) ||
  280.                     (AIRoad.IsRoadStationTile(new_tile) || AIRoad.IsRoadDepotTile(new_tile)) && AITile.GetOwner(new_tile) == AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)) {
  281.                 cost -= self._cost_no_existing_road;
  282.             }
  283.         }
  284.        
  285.         if (AIRoad.IsDriveThroughRoadStationTile(new_tile)) {
  286.             cost += self._cost_drive_through;
  287.         }
  288.     }
  289.  
  290.     if (coast_cost_only != null) {
  291.         /* Check if the new tile is a coast tile with water. */
  292.         if (AITile.IsCoastTile(new_tile) && AITile.HasTransportType(new_tile, AITile.TRANSPORT_WATER)) {
  293.             cost += self._cost_coast;
  294.         }
  295.     }
  296.  
  297. //  AILog.Info("Measured ops _CostHelper: " + (before - AIController.GetOpsTillSuspend()) + " ; coast_cost_only = " + (coast_cost_only == null ? "null" : coast_cost_only) + " ; par_tile = " + (path.GetParent() != null ? path.GetParent().GetTile() : "null") + " ; prev_tile = " + prev_tile + " ; new_tile = " + new_tile);
  298. //  AILog.Info("_CostHelper out");
  299.     return cost;
  300. }
  301.  
  302. function Road::_Cost(self, path, new_tile, new_direction,
  303.         _AIBridge = AIBridge, _AITunnel = AITunnel)
  304. {
  305.     /* path == null means this is the first node of a path, so the cost is 0. */
  306.     if (path == null) return 0;
  307.  
  308.     local prev_tile = path.GetTile();
  309.     local dist = AIMap.DistanceManhattan(new_tile, prev_tile);
  310.  
  311.     /* If the new tile is a bridge / tunnel tile, check whether we came from the other
  312.      * end of the bridge / tunnel or if we just entered the bridge / tunnel. */
  313.     if (_AIBridge.IsBridgeTile(new_tile)) {
  314.         if (_AIBridge.GetOtherBridgeEnd(new_tile) != prev_tile) {
  315.             return path.GetCost() + self._CostHelperEfficient(self, path, new_tile);
  316.         }
  317.         return path.GetCost() + (dist + 1) * self._cost_bridge_per_tile + dist * self._cost_tile + self._GetBridgeNumSlopesEfficient(new_tile, prev_tile) * self._cost_slope;
  318.     }
  319.     if (_AITunnel.IsTunnelTile(new_tile)) {
  320.         if (_AITunnel.GetOtherTunnelEnd(new_tile) != prev_tile) {
  321.             return path.GetCost() + self._CostHelperEfficient(self, path, new_tile);
  322.         }
  323.         return path.GetCost() + (dist + 1) * self._cost_tunnel_per_tile + dist * self._cost_tile;
  324.     }
  325.  
  326.     /* If the two tiles are more than 1 tile apart, the pathfinder wants a bridge or tunnel
  327.      * to be built. It isn't an existing bridge / tunnel, as that case is already handled. */
  328.     if (dist > 1) {
  329.         /* Check if we should build a bridge or a tunnel. */
  330.         if (_AITunnel.GetOtherTunnelEnd(new_tile) == prev_tile) {
  331.             return path.GetCost() + (dist + 1) * self._cost_tunnel_per_tile + dist * (self._cost_tile + self._cost_no_existing_road * 2);
  332.         } else {
  333.             return path.GetCost() + (dist + 1) * self._cost_bridge_per_tile + dist * (self._cost_tile + self._cost_no_existing_road * 2) + self._GetBridgeNumSlopesEfficient(new_tile, prev_tile) * self._cost_slope + self._CostHelperEfficient(self, path, new_tile, true);
  334.         }
  335.     }
  336.  
  337.     return path.GetCost() + self._CostHelperEfficient(self, path, new_tile, false);
  338. }
  339.  
  340. function Road::_CostOriginal(self, path, new_tile, new_direction)
  341. {
  342.     /* path == null means this is the first node of a path, so the cost is 0. */
  343.     if (path == null) return 0;
  344.  
  345.     local prev_tile = path.GetTile();
  346.     local dist = AIMap.DistanceManhattan(new_tile, prev_tile);
  347.  
  348.     /* If the new tile is a bridge / tunnel tile, check whether we came from the other
  349.      * end of the bridge / tunnel or if we just entered the bridge / tunnel. */
  350.     if (AIBridge.IsBridgeTile(new_tile)) {
  351.         if (AIBridge.GetOtherBridgeEnd(new_tile) != prev_tile) {
  352.             return path.GetCost() + self._CostHelper(self, path, new_tile);
  353.         }
  354.         return path.GetCost() + (dist + 1) * self._cost_bridge_per_tile + dist * self._cost_tile + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope;
  355.     }
  356.     if (AITunnel.IsTunnelTile(new_tile)) {
  357.         if (AITunnel.GetOtherTunnelEnd(new_tile) != prev_tile) {
  358.             return path.GetCost() + self._CostHelper(self, path, new_tile);
  359.         }
  360.         return path.GetCost() + (dist + 1) * self._cost_tunnel_per_tile + dist * self._cost_tile;
  361.     }
  362.  
  363.     /* If the two tiles are more than 1 tile apart, the pathfinder wants a bridge or tunnel
  364.      * to be built. It isn't an existing bridge / tunnel, as that case is already handled. */
  365.     if (dist > 1) {
  366.         /* Check if we should build a bridge or a tunnel. */
  367.         if (AITunnel.GetOtherTunnelEnd(new_tile) == prev_tile) {
  368.             return path.GetCost() + (dist + 1) * self._cost_tunnel_per_tile + dist * (self._cost_tile + self._cost_no_existing_road * 2);
  369.         } else {
  370.             return path.GetCost() + (dist + 1) * self._cost_bridge_per_tile + dist * (self._cost_tile + self._cost_no_existing_road * 2) + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope + self._CostHelper(self, path, new_tile, true);
  371.         }
  372.     }
  373.  
  374.     return path.GetCost() + self._CostHelper(self, path, new_tile, false);
  375. }
  376.  
  377. function Road::_Estimate(self, cur_tile, cur_direction, goal_tiles,
  378.         _AIMap = AIMap)
  379. {
  380.     local min_cost = self._max_cost;
  381.     /* As estimate we multiply the lowest possible cost for a single tile
  382.      * with the minimum number of tiles we need to traverse. */
  383.     foreach (tile in goal_tiles) {
  384.         min_cost = min(_AIMap.DistanceManhattan(cur_tile, tile) * self._cost_tile, min_cost);
  385.     }
  386.     return min_cost;
  387. }
  388.  
  389. function Road::_EstimateOriginal(self, cur_tile, cur_direction, goal_tiles)
  390. {
  391.     local min_cost = self._max_cost;
  392.     /* As estimate we multiply the lowest possible cost for a single tile
  393.      * with the minimum number of tiles we need to traverse. */
  394.     foreach (tile in goal_tiles) {
  395.         min_cost = min(AIMap.DistanceManhattan(cur_tile, tile) * self._cost_tile, min_cost);
  396.     }
  397.     return min_cost;
  398. }
  399.  
  400. function Road::_Neighbours(self, path, cur_node,
  401.         _AIBridge = AIBridge, _AITunnel = AITunnel, _AITile = AITile, _AIMap = AIMap, _AIRoad = AIRoad, _AIRail = AIRail, _AICompany = AICompany, _AIVehicle = AIVehicle)
  402. {
  403.     /* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
  404.     if (path.GetCost() >= self._max_cost) return [];
  405. //  AILog.Info("_Neighbours (efficient) in");
  406. //  local before = AIController.GetOpsTillSuspend();
  407.    
  408.     local par = path.GetParent() != null;
  409.     local last_node = par ? path.GetParent().GetTile() : 0;
  410.  
  411.     local tiles = [];
  412.  
  413.     /* Check if the current tile is part of a bridge or tunnel. */
  414.     local other_end = 0;
  415.     if (_AIBridge.IsBridgeTile(cur_node)) {
  416.         other_end = _AIBridge.GetOtherBridgeEnd(cur_node);
  417.     } else if (_AITunnel.IsTunnelTile(cur_node)) {
  418.         other_end = _AITunnel.GetOtherTunnelEnd(cur_node);
  419.     }
  420.     if (other_end && _AITile.HasTransportType(cur_node, _AITile.TRANSPORT_ROAD)) {
  421. //      local other_end = _AIBridge.IsBridgeTile(cur_node) ? _AIBridge.GetOtherBridgeEnd(cur_node) : _AITunnel.GetOtherTunnelEnd(cur_node);
  422.         local next_tile = cur_node + (cur_node - other_end) / _AIMap.DistanceManhattan(cur_node, other_end);
  423.         if ((_AIRoad.AreRoadTilesConnected(cur_node, next_tile) || _AIRoad.IsRoadTile(next_tile)) && !_AIRail.IsLevelCrossingTile(next_tile) &&
  424.                 (!_AIRoad.IsDriveThroughRoadStationTile(next_tile) || _AIRoad.GetRoadStationFrontTile(next_tile) == cur_node || _AIRoad.GetDriveThroughBackTile(next_tile) == cur_node) ||
  425.                 _AITile.IsBuildable(next_tile)) {
  426.             tiles.push([next_tile, self._GetDirectionEfficient(cur_node, next_tile, false)]);
  427.         }
  428.         /* The other end of the bridge / tunnel is a neighbour. */
  429.         tiles.push([other_end, self._GetDirectionEfficient(next_tile, cur_node, true) << 4]);
  430.     } else if (last_node && _AIMap.DistanceManhattan(cur_node, last_node) > 1) {
  431.         local next_tile = cur_node + (cur_node - last_node) / _AIMap.DistanceManhattan(cur_node, last_node);
  432.         if (_AIRoad.AreRoadTilesConnected(cur_node, next_tile) && !_AIRail.IsLevelCrossingTile(next_tile) ||
  433.                 _AIRoad.BuildRoad(cur_node, next_tile) && !_AIRail.IsRailTile(next_tile)) {
  434.             tiles.push([next_tile, self._GetDirectionEfficient(cur_node, next_tile, false)]);
  435.         }
  436.     } else {
  437.         local offsets = [_AIMap.GetTileIndex(0, 1), _AIMap.GetTileIndex(0, -1), _AIMap.GetTileIndex(1, 0), _AIMap.GetTileIndex(-1, 0)];
  438.         /* Check all tiles adjacent to the current tile. */
  439.         foreach (offset in offsets) {
  440.             local next_tile = cur_node + offset;
  441.             /* We add them to the to the neighbours-list if one of the following applies:
  442.              * 1) There already is a connection between the current tile and the next tile, and it's not a level crossing.
  443.              * 2) We can build a road to the next tile, except when it's a level crossing.
  444.              *    We can connect to a regular road station or a road depot owned by us.
  445.              * 3) The next tile is the entrance of a tunnel / bridge in the correct direction. */
  446.             if (_AIRoad.AreRoadTilesConnected(cur_node, next_tile) && !_AIRail.IsLevelCrossingTile(next_tile)) {
  447.                 tiles.push([next_tile, self._GetDirectionEfficient(cur_node, next_tile, false)]);
  448.             } else if ((_AITile.IsBuildable(next_tile) || _AIRoad.IsRoadTile(next_tile) && !_AIRail.IsLevelCrossingTile(next_tile) ||
  449.                     (_AIRoad.IsRoadStationTile(next_tile) || _AIRoad.IsRoadDepotTile(next_tile)) && _AITile.GetOwner(next_tile) == _AICompany.ResolveCompanyID(_AICompany.COMPANY_SELF)) &&
  450.                     (!par || _AIRoad.CanBuildConnectedRoadPartsHere(cur_node, last_node, next_tile)) &&
  451.                     _AIRoad.BuildRoad(cur_node, next_tile)) {
  452.                 tiles.push([next_tile, self._GetDirectionEfficient(cur_node, next_tile, false)]);
  453.             } else if (self._CheckTunnelBridgeEfficient(cur_node, next_tile) &&
  454.                     (!par || _AIRoad.CanBuildConnectedRoadPartsHere(cur_node, last_node, next_tile)) && _AIRoad.BuildRoad(cur_node, next_tile)) {
  455.                 tiles.push([next_tile, self._GetDirectionEfficient(cur_node, next_tile, false)]);
  456.             }
  457.         }
  458.         if (par) {
  459. //          local bridges = self._GetTunnelsBridgesEfficient(path.GetParent().GetTile(), cur_node, self._GetDirectionEfficient(path.GetParent().GetTile(), cur_node, true) << 4);
  460. //          foreach (tile in bridges) {
  461. //              tiles.push(tile);
  462. //          }          
  463.             /*
  464.              * Get a list of all bridges and tunnels that can be built from the
  465.              * current tile. Tunnels will only be built if no terraforming
  466.              * is needed on both ends. */
  467.             local bridge_dir = self._GetDirectionEfficient(last_node, cur_node, true) << 4;
  468.             for (local i = 2; i < self._max_bridge_length;) {
  469.                 local target = cur_node + i * (cur_node - last_node);
  470.                 local bridge_list = AIBridgeList_Length(++i);
  471.                 if (!bridge_list.IsEmpty() && _AIBridge.BuildBridge(_AIVehicle.VT_ROAD, bridge_list.Begin(), cur_node, target)) {
  472.                     tiles.push([target, bridge_dir]);
  473.                 }
  474.             }
  475.  
  476.             local slope = _AITile.GetSlope(cur_node);
  477.             if (slope == _AITile.SLOPE_SW || slope == _AITile.SLOPE_NW || slope == _AITile.SLOPE_SE || slope == _AITile.SLOPE_NE) {
  478.                 local other_tunnel_end = _AITunnel.GetOtherTunnelEnd(cur_node);
  479.                 if (_AIMap.IsValidTile(other_tunnel_end)) {
  480.                     local tunnel_length = _AIMap.DistanceManhattan(cur_node, other_tunnel_end);
  481.                     if (_AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
  482.                             cur_node + (cur_node - other_tunnel_end) / tunnel_length == last_node && tunnel_length < self._max_tunnel_length && _AITunnel.BuildTunnel(_AIVehicle.VT_ROAD, cur_node)) {
  483.                         tiles.push([other_tunnel_end, bridge_dir]);
  484.                     }
  485.                 }
  486.             }
  487.         }
  488.     }
  489. //  AILog.Info("Measured ops _Neighbours (efficient): " + (before - AIController.GetOpsTillSuspend()) + " ; cur_node = " + cur_node);
  490. //  AILog.Info("_Neighbours (efficient) out");
  491.     return tiles;
  492. }
  493.  
  494. function Road::_NeighboursOriginal(self, path, cur_node)
  495. {
  496.     /* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
  497.     if (path.GetCost() >= self._max_cost) return [];
  498. //  AILog.Info("_Neighbours in");
  499. //  local before = AIController.GetOpsTillSuspend();
  500.     local tiles = [];
  501.  
  502.     /* Check if the current tile is part of a bridge or tunnel. */
  503.     if ((AIBridge.IsBridgeTile(cur_node) || AITunnel.IsTunnelTile(cur_node)) && AITile.HasTransportType(cur_node, AITile.TRANSPORT_ROAD)) {
  504.         local other_end = AIBridge.IsBridgeTile(cur_node) ? AIBridge.GetOtherBridgeEnd(cur_node) : AITunnel.GetOtherTunnelEnd(cur_node);
  505.         local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
  506.         if ((AIRoad.AreRoadTilesConnected(cur_node, next_tile) || AIRoad.IsRoadTile(next_tile)) && !AIRail.IsLevelCrossingTile(next_tile) &&
  507.                 (!AIRoad.IsDriveThroughRoadStationTile(next_tile) || AIRoad.GetRoadStationFrontTile(next_tile) == cur_node || AIRoad.GetDriveThroughBackTile(next_tile) == cur_node) ||
  508.                 AITile.IsBuildable(next_tile)) {
  509.             tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
  510.         }
  511.         /* The other end of the bridge / tunnel is a neighbour. */
  512.         tiles.push([other_end, self._GetDirection(next_tile, cur_node, true) << 4]);
  513.     } else if (path.GetParent() != null && AIMap.DistanceManhattan(cur_node, path.GetParent().GetTile()) > 1) {
  514.         local other_end = path.GetParent().GetTile();
  515.         local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
  516.         if (AIRoad.AreRoadTilesConnected(cur_node, next_tile) && !AIRail.IsLevelCrossingTile(next_tile) ||
  517.                 AIRoad.BuildRoad(cur_node, next_tile) && !AIRail.IsRailTile(next_tile)) {
  518.             tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
  519.         }
  520.     } else {
  521.         local offsets = [AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1), AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0)];
  522.         /* Check all tiles adjacent to the current tile. */
  523.         foreach (offset in offsets) {
  524.             local next_tile = cur_node + offset;
  525.             /* We add them to the to the neighbours-list if one of the following applies:
  526.              * 1) There already is a connection between the current tile and the next tile, and it's not a level crossing.
  527.              * 2) We can build a road to the next tile, except when it's a level crossing.
  528.              *    We can connect to a regular road station or a road depot owned by us.
  529.              * 3) The next tile is the entrance of a tunnel / bridge in the correct direction. */
  530.             if (AIRoad.AreRoadTilesConnected(cur_node, next_tile) && !AIRail.IsLevelCrossingTile(next_tile)) {
  531.                 tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
  532.             } else if ((AITile.IsBuildable(next_tile) || AIRoad.IsRoadTile(next_tile) && !AIRail.IsLevelCrossingTile(next_tile) ||
  533.                     (AIRoad.IsRoadStationTile(next_tile) || AIRoad.IsRoadDepotTile(next_tile)) && AITile.GetOwner(next_tile) == AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)) &&
  534.                     (path.GetParent() == null || AIRoad.CanBuildConnectedRoadPartsHere(cur_node, path.GetParent().GetTile(), next_tile)) &&
  535.                     AIRoad.BuildRoad(cur_node, next_tile)) {
  536.                 tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
  537.             } else if (self._CheckTunnelBridge(cur_node, next_tile) && (path.GetParent() == null || AIRoad.CanBuildConnectedRoadPartsHere(cur_node, path.GetParent().GetTile(), next_tile)) && AIRoad.BuildRoad(cur_node, next_tile)) {
  538.                 tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
  539.             }
  540.         }
  541.         if (path.GetParent() != null) {
  542.             local bridges = self._GetTunnelsBridges(path.GetParent().GetTile(), cur_node, self._GetDirection(path.GetParent().GetTile(), cur_node, true) << 4);
  543.             foreach (tile in bridges) {
  544.                 tiles.push(tile);
  545.             }
  546.         }
  547.     }
  548. //  AILog.Info("Measured ops _Neighbours: " + (before - AIController.GetOpsTillSuspend()) + " ; cur_node = " + cur_node);
  549. //  AILog.Info("_Neighbours out");
  550. //  local tiles2 = self._NeighboursEfficient(self, path, cur_node);
  551. //  assert(tiles.len() == tiles2.len());
  552.     return tiles;
  553. }
  554.  
  555. function Road::_CheckDirection(self, tile, existing_direction, new_direction)
  556. {
  557.     return false;
  558. }
  559.  
  560. function Road::_GetDirectionEfficient(from, to, is_bridge,
  561.         map_size_x = Road._map_size_x, _AITile = AITile)
  562. {
  563.     if (!is_bridge && _AITile.GetSlope(to) == _AITile.SLOPE_FLAT) return 0xFF;
  564.     local difference = from - to;
  565.     if (difference == 1) return 1;
  566.     if (difference == -1) return 2;
  567.     if (difference == map_size_x) return 4;
  568.     if (difference == -map_size_x) return 8;
  569. }
  570.  
  571. function Road::_GetDirection(from, to, is_bridge)
  572. {
  573.     if (!is_bridge && AITile.GetSlope(to) == AITile.SLOPE_FLAT) return 0xFF;
  574.     if (from - to == 1) return 1;
  575.     if (from - to == -1) return 2;
  576.     if (from - to == AIMap.GetMapSizeX()) return 4;
  577.     if (from - to == -AIMap.GetMapSizeX()) return 8;
  578. }
  579.  
  580. /**
  581.  * Get a list of all bridges and tunnels that can be built from the
  582.  * current tile. Tunnels will only be built if no terraforming
  583.  * is needed on both ends.
  584.  */
  585. function Road::_GetTunnelsBridgesEfficient(last_node, cur_node, bridge_dir,
  586.         _AIBridge = AIBridge, _AITile = AITile, _AIMap = AIMap, _AITunnel = AITunnel, _AIVehicle = AIVehicle)
  587. {
  588.     local tiles = [];
  589.  
  590.     for (local i = 2; i < this._max_bridge_length;) {
  591.         local target = cur_node + i * (cur_node - last_node);
  592.         local bridge_list = AIBridgeList_Length(++i);
  593.         if (!bridge_list.IsEmpty() && _AIBridge.BuildBridge(_AIVehicle.VT_ROAD, bridge_list.Begin(), cur_node, target)) {
  594.             tiles.push([target, bridge_dir]);
  595.         }
  596.     }
  597.  
  598.     local slope = _AITile.GetSlope(cur_node);
  599.     if (slope != _AITile.SLOPE_SW && slope != _AITile.SLOPE_NW && slope != _AITile.SLOPE_SE && slope != _AITile.SLOPE_NE) return tiles;
  600.     local other_tunnel_end = _AITunnel.GetOtherTunnelEnd(cur_node);
  601.     if (!_AIMap.IsValidTile(other_tunnel_end)) return tiles;
  602.  
  603.     local tunnel_length = _AIMap.DistanceManhattan(cur_node, other_tunnel_end);
  604.     if (_AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
  605.             cur_node + (cur_node - other_tunnel_end) / tunnel_length == last_node && tunnel_length < _max_tunnel_length && _AITunnel.BuildTunnel(_AIVehicle.VT_ROAD, cur_node)) {
  606.         tiles.push([other_tunnel_end, bridge_dir]);
  607.     }
  608.     return tiles;
  609. }
  610.  
  611. /**
  612.  * Get a list of all bridges and tunnels that can be built from the
  613.  * current tile. Tunnels will only be built if no terraforming
  614.  * is needed on both ends.
  615.  */
  616. function Road::_GetTunnelsBridges(last_node, cur_node, bridge_dir)
  617. {
  618.     local tiles = [];
  619.  
  620.     for (local i = 2; i < this._max_bridge_length; i++) {
  621.         local bridge_list = AIBridgeList_Length(i + 1);
  622.         local target = cur_node + i * (cur_node - last_node);
  623.         if (!bridge_list.IsEmpty() && AIBridge.BuildBridge(AIVehicle.VT_ROAD, bridge_list.Begin(), cur_node, target)) {
  624.             tiles.push([target, bridge_dir]);
  625.         }
  626.     }
  627.  
  628.     local slope = AITile.GetSlope(cur_node);
  629.     if (slope != AITile.SLOPE_SW && slope != AITile.SLOPE_NW && slope != AITile.SLOPE_SE && slope != AITile.SLOPE_NE) return tiles;
  630.     local other_tunnel_end = AITunnel.GetOtherTunnelEnd(cur_node);
  631.     if (!AIMap.IsValidTile(other_tunnel_end)) return tiles;
  632.  
  633.     local tunnel_length = AIMap.DistanceManhattan(cur_node, other_tunnel_end);
  634.     local prev_tile = cur_node + (cur_node - other_tunnel_end) / tunnel_length;
  635.     if (AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
  636.             prev_tile == last_node && tunnel_length < _max_tunnel_length && AITunnel.BuildTunnel(AIVehicle.VT_ROAD, cur_node)) {
  637.         tiles.push([other_tunnel_end, bridge_dir]);
  638.     }
  639.     return tiles;
  640. }
  641.  
  642. function Road::_IsSlopedRoadEfficient(start, middle, end, map_size_x = Road._map_size_x, _AITile = AITile)
  643. {
  644.     local NW = middle - map_size_x;
  645.     local NE = middle - 1;
  646.     local SE = middle + map_size_x;
  647.     local SW = middle + 1;
  648.  
  649.     NW = NW == start || NW == end; //Set to true if we want to build a road to / from the north-west
  650.     NE = NE == start || NE == end; //Set to true if we want to build a road to / from the north-east
  651.     SE = SE == start || SE == end; //Set to true if we want to build a road to / from the south-west
  652.     SW = SW == start || SW == end; //Set to true if we want to build a road to / from the south-east
  653.  
  654.     /* If there is a turn in the current tile, it can't be sloped. */
  655.     if ((NW || SE) && (NE || SW)) return false;
  656.  
  657.     local slope = _AITile.GetSlope(middle);
  658.     /* A road on a steep slope is always sloped. */
  659.     if (_AITile.IsSteepSlope(slope)) return true;
  660.  
  661.     /* If only one corner is raised, the road is sloped. */
  662.     if (slope == _AITile.SLOPE_N || slope == _AITile.SLOPE_W) return true;
  663.     if (slope == _AITile.SLOPE_S || slope == _AITile.SLOPE_E) return true;
  664.  
  665.     if (NW && (slope == _AITile.SLOPE_NW || slope == _AITile.SLOPE_SE)) return true;
  666.     if (NE && (slope == _AITile.SLOPE_NE || slope == _AITile.SLOPE_SW)) return true;
  667.  
  668.     return false;
  669. }
  670.  
  671. function Road::_IsSlopedRoad(start, middle, end)
  672. {
  673.     local NW = 0; //Set to true if we want to build a road to / from the north-west
  674.     local NE = 0; //Set to true if we want to build a road to / from the north-east
  675.     local SW = 0; //Set to true if we want to build a road to / from the south-west
  676.     local SE = 0; //Set to true if we want to build a road to / from the south-east
  677.  
  678.     if (middle - AIMap.GetMapSizeX() == start || middle - AIMap.GetMapSizeX() == end) NW = 1;
  679.     if (middle - 1 == start || middle - 1 == end) NE = 1;
  680.     if (middle + AIMap.GetMapSizeX() == start || middle + AIMap.GetMapSizeX() == end) SE = 1;
  681.     if (middle + 1 == start || middle + 1 == end) SW = 1;
  682.  
  683.     /* If there is a turn in the current tile, it can't be sloped. */
  684.     if ((NW || SE) && (NE || SW)) return false;
  685.  
  686.     local slope = AITile.GetSlope(middle);
  687.     /* A road on a steep slope is always sloped. */
  688.     if (AITile.IsSteepSlope(slope)) return true;
  689.  
  690.     /* If only one corner is raised, the road is sloped. */
  691.     if (slope == AITile.SLOPE_N || slope == AITile.SLOPE_W) return true;
  692.     if (slope == AITile.SLOPE_S || slope == AITile.SLOPE_E) return true;
  693.  
  694.     if (NW && (slope == AITile.SLOPE_NW || slope == AITile.SLOPE_SE)) return true;
  695.     if (NE && (slope == AITile.SLOPE_NE || slope == AITile.SLOPE_SW)) return true;
  696.  
  697.     return false;
  698. }
  699.  
  700. function Road::_CheckTunnelBridgeEfficient(current_tile, new_tile,
  701.         map_size_x = Road._map_size_x, _AIBridge = AIBridge, _AITunnel = AITunnel)
  702. {
  703.     local dir2;
  704.     if (_AIBridge.IsBridgeTile(new_tile)) {
  705.         dir2 = _AIBridge.GetOtherBridgeEnd(new_tile) - new_tile;
  706.     } else if (_AITunnel.IsTunnelTile(new_tile)) {
  707.         dir2 = _AITunnel.GetOtherTunnelEnd(new_tile) - new_tile;
  708.     } else {
  709.         return false;
  710.     }
  711.  
  712.     local dir = new_tile - current_tile;
  713.     if ((dir < 0 && dir2 > 0) || (dir > 0 && dir2 < 0)) return false;
  714.     dir = abs(dir);
  715.     dir2 = abs(dir2);
  716.     if ((dir >= map_size_x && dir2 < map_size_x) ||
  717.         (dir < map_size_x && dir2 >= map_size_x)) return false;
  718.  
  719.     return true;
  720. }
  721.  
  722. function Road::_CheckTunnelBridge(current_tile, new_tile)
  723. {
  724.     if (!AIBridge.IsBridgeTile(new_tile) && !AITunnel.IsTunnelTile(new_tile)) return false;
  725.     local dir = new_tile - current_tile;
  726.     local other_end = AIBridge.IsBridgeTile(new_tile) ? AIBridge.GetOtherBridgeEnd(new_tile) : AITunnel.GetOtherTunnelEnd(new_tile);
  727.     local dir2 = other_end - new_tile;
  728.     if ((dir < 0 && dir2 > 0) || (dir > 0 && dir2 < 0)) return false;
  729.     dir = abs(dir);
  730.     dir2 = abs(dir2);
  731.     if ((dir >= AIMap.GetMapSizeX() && dir2 < AIMap.GetMapSizeX()) ||
  732.         (dir < AIMap.GetMapSizeX() && dir2 >= AIMap.GetMapSizeX())) return false;
  733.  
  734.     return true;
  735. }

Comments