/** * Check whether a ship should reverse to reach its destination. * Called when leaving depot. * @param v Ship * @param tile Current position * @param td1 Forward direction * @param td2 Reverse direction * @return true if the reverse direction is better */ static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2) { /* get available trackdirs on the destination tile */ TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); /* create pathfinder instance */ Tpf pf; /* set origin and destination nodes */ pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2)); pf.SetDestination(v->dest_tile, dest_trackdirs); /* find best path */ if (!pf.FindPath(v)) return false; Node *pNode = pf.GetBestNode(); if (pNode == NULL) return false; /* path was found * walk through the path back to the origin */ while (pNode->m_parent != NULL) { pNode = pNode->m_parent; } Trackdir best_trackdir = pNode->GetTrackdir(); assert(best_trackdir == td1 || best_trackdir == td2); return best_trackdir == td2; }
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found) { /* handle special case - when next tile is destination tile */ if (tile == v->dest_tile) { /* convert tracks to trackdirs */ TrackdirBits trackdirs = (TrackdirBits)(tracks | ((int)tracks << 8)); /* limit to trackdirs reachable from enterdir */ trackdirs &= DiagdirReachesTrackdirs(enterdir); /* use vehicle's current direction if that's possible, otherwise use first usable one. */ Trackdir veh_dir = v->GetVehicleTrackdir(); return ((trackdirs & TrackdirToTrackdirBits(veh_dir)) != 0) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs); } /* move back to the old tile/trackdir (where ship is coming from) */ TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir))); Trackdir trackdir = v->GetVehicleTrackdir(); assert(IsValidTrackdir(trackdir)); /* convert origin trackdir to TrackdirBits */ TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir); /* get available trackdirs on the destination tile */ TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); /* create pathfinder instance */ Tpf pf; /* set origin and destination nodes */ pf.SetOrigin(src_tile, trackdirs); pf.SetDestination(v->dest_tile, dest_trackdirs); /* find best path */ path_found = pf.FindPath(v); Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found" Node *pNode = pf.GetBestNode(); if (pNode != NULL) { /* walk through the path back to the origin */ Node *pPrevNode = NULL; while (pNode->m_parent != NULL) { pPrevNode = pNode; pNode = pNode->m_parent; } /* return trackdir from the best next node (direct child of origin) */ Node& best_next_node = *pPrevNode; assert(best_next_node.GetTile() == tile); next_trackdir = best_next_node.GetTrackdir(); } return next_trackdir; }
inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) { if (m_dest_station != INVALID_STATION) { return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == m_dest_station && (m_bus ? IsBusStop(tile) : IsTruckStop(tile)) && (m_non_artic || IsDriveThroughStopTile(tile)); } return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); }
/** Return true if the valid origin (tile/trackdir) was set from the current vehicle position. */ inline bool SetOriginFromVehiclePos(const RoadVehicle *v) { /* set origin (tile, trackdir) */ TileIndex src_tile = v->tile; Trackdir src_td = v->GetVehicleTrackdir(); if ((TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(src_td)) == 0) { /* sometimes the roadveh is not on the road (it resides on non-existing track) * how should we handle that situation? */ return false; } Yapf().SetOrigin(src_tile, TrackdirToTrackdirBits(src_td)); return true; }
/** * Find the best depot for a road vehicle. * @param v Vehicle * @param tile Tile of the vehicle. * @param td Trackdir of the vehicle. * @param max_distance max length (penalty) for paths. */ inline FindDepotData FindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance) { /* Set origin. */ Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td)); Yapf().SetMaxCost(max_distance); /* Find the best path and return if no depot is found. */ if (!Yapf().FindPath(v)) return FindDepotData(); /* Return the cost of the best path and its depot. */ Node *n = Yapf().GetBestNode(); return FindDepotData(n->m_segment_last_tile, n->m_cost); }
/** Called by YAPF to detect if node ends in the desired destination */ FORCEINLINE bool PfDetectDestination(TileIndex tile, Trackdir td) { bool bDest; if (m_dest_station_id != INVALID_STATION) { bDest = HasStationTileRail(tile) && (GetStationIndex(tile) == m_dest_station_id) && (GetRailStationTrack(tile) == TrackdirToTrack(td)); } else { bDest = (tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(td)) != TRACKDIR_BIT_NONE); } return bDest; }
inline int CurveCost(Trackdir td1, Trackdir td2) { assert(IsValidTrackdir(td1)); assert(IsValidTrackdir(td2)); int cost = 0; if (TrackFollower::Allow90degTurns() && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) { /* 90-deg curve penalty */ cost += Yapf().PfGetSettings().rail_curve90_penalty; } else if (td2 != NextTrackdir(td1)) { /* 45-deg curve penalty */ cost += Yapf().PfGetSettings().rail_curve45_penalty; } return cost; }
/** * main follower routine. Fills all members and return true on success. * Otherwise returns false if track can't be followed. */ inline bool Follow(TileIndex old_tile, Trackdir old_td) { m_old_tile = old_tile; m_old_td = old_td; m_err = EC_NONE; assert( ((TrackStatusToTrackdirBits( GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != NULL) ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0) ) & TrackdirToTrackdirBits(m_old_td)) != 0) || (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR) // Disable the assertion for single tram bits ); m_exitdir = TrackdirToExitdir(m_old_td); if (ForcedReverse()) return true; if (!CanExitOldTile()) return false; FollowTileExit(); if (!QueryNewTileTrackStatus()) return TryReverse(); m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir); if (m_new_td_bits == TRACKDIR_BIT_NONE || !CanEnterNewTile()) { /* In case we can't enter the next tile, but are * a normal road vehicle, then we can actually * try to reverse as this is the end of the road. * Trams can only turn on the appropriate bits in * which case reaching this would mean a dead end * near a building and in that case there would * a "false" QueryNewTileTrackStatus result and * as such reversing is already tried. The fact * that function failed can have to do with a * missing road bit, or inability to connect the * different bits due to slopes. */ if (IsRoadTT() && !IsTram() && TryReverse()) return true; /* CanEnterNewTile already set a reason. * Do NOT overwrite it (important for example for EC_RAIL_TYPE). * Only set a reason if CanEnterNewTile was not called */ if (m_new_td_bits == TRACKDIR_BIT_NONE) m_err = EC_NO_WAY; return false; } if (!Allow90degTurns()) { m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td); if (m_new_td_bits == TRACKDIR_BIT_NONE) { m_err = EC_90DEG; return false; } } return true; }
inline bool FindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance, TileIndex *depot_tile) { /* set origin and destination nodes */ Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td)); /* find the best path */ bool bFound = Yapf().FindPath(v); if (!bFound) return false; /* some path found * get found depot tile */ Node *n = Yapf().GetBestNode(); if (max_distance > 0 && n->m_cost > max_distance * YAPF_TILE_LENGTH) return false; *depot_tile = n->m_segment_last_tile; return true; }
/* Will just follow the results of GetTileTrackStatus concerning where we can * go and where not. Uses AyStar.user_data[NPF_TYPE] as the transport type and * an argument to GetTileTrackStatus. Will skip tunnels, meaning that the * entry and exit are neighbours. Will fill * AyStarNode.user_data[NPF_TRACKDIR_CHOICE] with an appropriate value, and * copy AyStarNode.user_data[NPF_NODE_FLAGS] from the parent */ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) { /* We leave src_tile on track src_trackdir in direction src_exitdir */ Trackdir src_trackdir = current->path.node.direction; TileIndex src_tile = current->path.node.tile; DiagDirection src_exitdir = TrackdirToExitdir(src_trackdir); /* Is src_tile valid, and can be used? * When choosing track on a junction src_tile is the tile neighboured to the junction wrt. exitdir. * But we must not check the validity of this move, as src_tile is totally unrelated to the move, if a roadvehicle reversed on a junction. */ bool ignore_src_tile = (current->path.parent == NULL && NPFGetFlag(¤t->path.node, NPF_FLAG_IGNORE_START_TILE)); /* Information about the vehicle: TransportType (road/rail/water) and SubType (compatible rail/road types) */ TransportType type = (TransportType)aystar->user_data[NPF_TYPE]; uint subtype = aystar->user_data[NPF_SUB_TYPE]; /* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */ aystar->num_neighbours = 0; DEBUG(npf, 4, "Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile); /* We want to determine the tile we arrive, and which choices we have there */ TileIndex dst_tile; TrackdirBits trackdirbits; /* Find dest tile */ if (ignore_src_tile) { /* Do not perform any checks that involve src_tile */ dst_tile = src_tile + TileOffsByDiagDir(src_exitdir); trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype); } else if (IsTileType(src_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(src_tile) == src_exitdir) { /* We drive through the wormhole and arrive on the other side */ dst_tile = GetOtherTunnelBridgeEnd(src_tile); trackdirbits = TrackdirToTrackdirBits(src_trackdir); } else if (ForceReverse(src_tile, src_exitdir, type, subtype)) { /* We can only reverse on this tile */ dst_tile = src_tile; src_trackdir = ReverseTrackdir(src_trackdir); trackdirbits = TrackdirToTrackdirBits(src_trackdir); } else { /* We leave src_tile in src_exitdir and reach dst_tile */ dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir)); if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, type, subtype, (RailTypes)aystar->user_data[NPF_RAILTYPES], (Owner)aystar->user_data[NPF_OWNER])) dst_tile = INVALID_TILE; if (dst_tile == INVALID_TILE) { /* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */ if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return; dst_tile = src_tile; src_trackdir = ReverseTrackdir(src_trackdir); } trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype); if (trackdirbits == 0) { /* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */ if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return; dst_tile = src_tile; src_trackdir = ReverseTrackdir(src_trackdir); trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype); } } if (NPFGetFlag(¤t->path.node, NPF_FLAG_IGNORE_RESERVED)) { /* Mask out any reserved tracks. */ TrackBits reserved = GetReservedTrackbits(dst_tile); trackdirbits &= ~TrackBitsToTrackdirBits(reserved); uint bits = TrackdirBitsToTrackBits(trackdirbits); int i; FOR_EACH_SET_BIT(i, bits) { if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i); } }
FindDepotData YapfRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_distance) { TileIndex tile = v->tile; Trackdir trackdir = v->GetVehicleTrackdir(); if ((TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(trackdir)) == 0) { return FindDepotData(); } /* default is YAPF type 2 */ typedef bool (*PfnFindNearestDepot)(const RoadVehicle*, TileIndex, Trackdir, int, TileIndex*); PfnFindNearestDepot pfnFindNearestDepot = &CYapfRoadAnyDepot2::stFindNearestDepot; /* check if non-default YAPF type should be used */ if (_settings_game.pf.yapf.disable_node_optimization) { pfnFindNearestDepot = &CYapfRoadAnyDepot1::stFindNearestDepot; // Trackdir, allow 90-deg } FindDepotData fdd; bool ret = pfnFindNearestDepot(v, tile, trackdir, max_distance, &fdd.tile); fdd.best_length = ret ? max_distance / 2 : UINT_MAX; // some fake distance or NOT_FOUND return fdd; }
/** main follower routine. Fills all members and return true on success. * Otherwise returns false if track can't be followed. */ inline bool Follow(TileIndex old_tile, Trackdir old_td) { m_old_tile = old_tile; m_old_td = old_td; m_err = EC_NONE; assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), IsRoadTT() && m_veh != NULL ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)) & TrackdirToTrackdirBits(m_old_td)) != 0) || (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR)); // Disable the assertion for single tram bits m_exitdir = TrackdirToExitdir(m_old_td); if (ForcedReverse()) return true; if (!CanExitOldTile()) return false; FollowTileExit(); if (!QueryNewTileTrackStatus()) return TryReverse(); if (!CanEnterNewTile()) return false; m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir); if (m_new_td_bits == TRACKDIR_BIT_NONE) { m_err = EC_NO_WAY; return false; } if (!Allow90degTurns()) { m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td); if (m_new_td_bits == TRACKDIR_BIT_NONE) { m_err = EC_90DEG; return false; } } return true; }
/** * Called by YAPF to calculate the cost from the origin to the given node. * Calculates only the cost of given node, adds it to the parent node cost * and stores the result into Node::m_cost member */ inline bool PfCalcCost(Node &n, const TrackFollower *tf) { assert(!n.flags_u.flags_s.m_targed_seen); assert(tf->m_new_tile == n.m_key.m_tile); assert((TrackdirToTrackdirBits(n.m_key.m_td) & tf->m_new_td_bits) != TRACKDIR_BIT_NONE); CPerfStart perf_cost(Yapf().m_perf_cost); /* Does the node have some parent node? */ bool has_parent = (n.m_parent != NULL); /* Do we already have a cached segment? */ CachedData &segment = *n.m_segment; bool is_cached_segment = (segment.m_cost >= 0); int parent_cost = has_parent ? n.m_parent->m_cost : 0; /* Each node cost contains 2 or 3 main components: * 1. Transition cost - cost of the move from previous node (tile): * - curve cost (or zero for straight move) * 2. Tile cost: * - base tile cost * - YAPF_TILE_LENGTH for diagonal tiles * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles * - tile penalties * - tile slope penalty (upward slopes) * - red signal penalty * - level crossing penalty * - speed-limit penalty (bridges) * - station platform penalty * - penalty for reversing in the depot * - etc. * 3. Extra cost (applies to the last node only) * - last red signal penalty * - penalty for too long or too short platform on the destination station */ int transition_cost = 0; int extra_cost = 0; /* Segment: one or more tiles connected by contiguous tracks of the same type. * Each segment cost includes 'Tile cost' for all its tiles (including the first * and last), and the 'Transition cost' between its tiles. The first transition * cost of segment entry (move from the 'parent' node) is not included! */ int segment_entry_cost = 0; int segment_cost = 0; const Train *v = Yapf().GetVehicle(); /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */ TILE cur(n.m_key.m_tile, n.m_key.m_td); /* the previous tile will be needed for transition cost calculations */ TILE prev = !has_parent ? TILE() : TILE(n.m_parent->GetLastTile(), n.m_parent->GetLastTrackdir()); EndSegmentReasonBits end_segment_reason = ESRB_NONE; TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); if (!has_parent) { /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */ assert(!is_cached_segment); /* Skip the first transition cost calculation. */ goto no_entry_cost; } for (;;) { /* Transition cost (cost of the move from previous tile) */ transition_cost = Yapf().CurveCost(prev.td, cur.td); transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td)); /* First transition cost counts against segment entry cost, other transitions * inside segment will come to segment cost (and will be cached) */ if (segment_cost == 0) { /* We just entered the loop. First transition cost goes to segment entry cost)*/ segment_entry_cost = transition_cost; transition_cost = 0; /* It is the right time now to look if we can reuse the cached segment cost. */ if (is_cached_segment) { /* Yes, we already know the segment cost. */ segment_cost = segment.m_cost; /* We know also the reason why the segment ends. */ end_segment_reason = segment.m_end_segment_reason; /* We will need also some information about the last signal (if it was red). */ if (segment.m_last_signal_tile != INVALID_TILE) { assert(HasSignalOnTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td)); SignalState sig_state = GetSignalStateByTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td); bool is_red = (sig_state == SIGNAL_STATE_RED); n.flags_u.flags_s.m_last_signal_was_red = is_red; if (is_red) { n.m_last_red_signal_type = GetSignalType(segment.m_last_signal_tile, TrackdirToTrack(segment.m_last_signal_td)); } } /* No further calculation needed. */ cur = TILE(n.GetLastTile(), n.GetLastTrackdir()); break; } } else { /* Other than first transition cost count as the regular segment cost. */ segment_cost += transition_cost; } no_entry_cost: // jump here at the beginning if the node has no parent (it is the first node) /* All other tile costs will be calculated here. */ segment_cost += Yapf().OneTileCost(cur.tile, cur.td); /* If we skipped some tunnel/bridge/station tiles, add their base cost */ segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped; /* Slope cost. */ segment_cost += Yapf().SlopeCost(cur.tile, cur.td); /* Signal cost (routine can modify segment data). */ segment_cost += Yapf().SignalCost(n, cur.tile, cur.td); /* Reserved tiles. */ segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped); end_segment_reason = segment.m_end_segment_reason; /* Tests for 'potential target' reasons to close the segment. */ if (cur.tile == prev.tile) { /* Penalty for reversing in a depot. */ assert(IsRailDepot(cur.tile)); segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty; /* We will end in this pass (depot is possible target) */ end_segment_reason |= ESRB_DEPOT; } else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) { if (v->current_order.IsType(OT_GOTO_WAYPOINT) && GetStationIndex(cur.tile) == v->current_order.GetDestination() && !Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) { /* This waypoint is our destination; maybe this isn't an unreserved * one, so check that and if so see that as the last signal being * red. This way waypoints near stations should work better. */ CFollowTrackRail ft(v); TileIndex t = cur.tile; Trackdir td = cur.td; while (ft.Follow(t, td)) { assert(t != ft.m_new_tile); t = ft.m_new_tile; if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { /* We encountered a junction; it's going to be too complex to * handle this perfectly, so just bail out. There is no simple * free path, so try the other possibilities. */ td = INVALID_TRACKDIR; break; } td = RemoveFirstTrackdir(&ft.m_new_td_bits); /* If this is a safe waiting position we're done searching for it */ if (IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg)) break; } /* In the case this platform is (possibly) occupied we add penalty so the * other platforms of this waypoint are evaluated as well, i.e. we assume * that there is a red signal in the waypoint when it's occupied. */ if (td == INVALID_TRACKDIR || !IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg) || !IsWaitingPositionFree(v, t, td, _settings_game.pf.forbid_90_deg)) { extra_cost += Yapf().PfGetSettings().rail_lastred_penalty; } } /* Waypoint is also a good reason to finish. */ end_segment_reason |= ESRB_WAYPOINT; } else if (tf->m_is_station) { /* Station penalties. */ uint platform_length = tf->m_tiles_skipped + 1; /* We don't know yet if the station is our target or not. Act like * if it is pass-through station (not our destination). */ segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length; /* We will end in this pass (station is possible target) */ end_segment_reason |= ESRB_STATION; } else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) { /* Searching for a safe tile? */ if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) { end_segment_reason |= ESRB_SAFE_TILE; } } /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise * it would cause desync in MP. */ if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) { int min_speed = 0; int max_speed = tf->GetSpeedLimit(&min_speed); int max_veh_speed = v->GetDisplayMaxSpeed(); if (max_speed < max_veh_speed) { extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (4 + tf->m_tiles_skipped) / max_veh_speed; } if (min_speed > max_veh_speed) { extra_cost += YAPF_TILE_LENGTH * (min_speed - max_veh_speed); } } /* Finish if we already exceeded the maximum path cost (i.e. when * searching for the nearest depot). */ if (m_max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > m_max_cost) { end_segment_reason |= ESRB_PATH_TOO_LONG; } /* Move to the next tile/trackdir. */ tf = &tf_local; tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); if (!tf_local.Follow(cur.tile, cur.td)) { assert(tf_local.m_err != TrackFollower::EC_NONE); /* Can't move to the next tile (EOL?). */ if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) { end_segment_reason |= ESRB_RAIL_TYPE; } else { end_segment_reason |= ESRB_DEAD_END; } if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) { end_segment_reason |= ESRB_SAFE_TILE; } break; } /* Check if the next tile is not a choice. */ if (KillFirstBit(tf_local.m_new_td_bits) != TRACKDIR_BIT_NONE) { /* More than one segment will follow. Close this one. */ end_segment_reason |= ESRB_CHOICE_FOLLOWS; break; } /* Gather the next tile/trackdir/tile_type/rail_type. */ TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits)); if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, MP_RAILWAY)) { if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) { /* Possible safe tile. */ end_segment_reason |= ESRB_SAFE_TILE; } else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) { /* Possible safe tile, but not so good as it's the back of a signal... */ end_segment_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END; extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty; } } /* Check the next tile for the rail type. */ if (next.rail_type != cur.rail_type) { /* Segment must consist from the same rail_type tiles. */ end_segment_reason |= ESRB_RAIL_TYPE; break; } /* Avoid infinite looping. */ if (next.tile == n.m_key.m_tile && next.td == n.m_key.m_td) { end_segment_reason |= ESRB_INFINITE_LOOP; break; } if (segment_cost > s_max_segment_cost) { /* Potentially in the infinite loop (or only very long segment?). We should * not force it to finish prematurely unless we are on a regular tile. */ if (IsTileType(tf->m_new_tile, MP_RAILWAY)) { end_segment_reason |= ESRB_SEGMENT_TOO_LONG; break; } } /* Any other reason bit set? */ if (end_segment_reason != ESRB_NONE) { break; } /* For the next loop set new prev and cur tile info. */ prev = cur; cur = next; } // for (;;) bool target_seen = false; if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) { /* Depot, station or waypoint. */ if (Yapf().PfDetectDestination(cur.tile, cur.td)) { /* Destination found. */ target_seen = true; } } /* Update the segment if needed. */ if (!is_cached_segment) { /* Write back the segment information so it can be reused the next time. */ segment.m_cost = segment_cost; segment.m_end_segment_reason = end_segment_reason & ESRB_CACHED_MASK; /* Save end of segment back to the node. */ n.SetLastTileTrackdir(cur.tile, cur.td); } /* Do we have an excuse why not to continue pathfinding in this direction? */ if (!target_seen && (end_segment_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) { /* Reason to not continue. Stop this PF branch. */ return false; } /* Special costs for the case we have reached our target. */ if (target_seen) { n.flags_u.flags_s.m_targed_seen = true; /* Last-red and last-red-exit penalties. */ if (n.flags_u.flags_s.m_last_signal_was_red) { if (n.m_last_red_signal_type == SIGTYPE_EXIT) { /* last signal was red pre-signal-exit */ extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty; } else if (!IsPbsSignal(n.m_last_red_signal_type)) { /* Last signal was red, but not exit or path signal. */ extra_cost += Yapf().PfGetSettings().rail_lastred_penalty; } } /* Station platform-length penalty. */ if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) { const BaseStation *st = BaseStation::GetByTile(n.GetLastTile()); assert(st != NULL); uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */ extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length; /* Add penalty for the inappropriate platform length. */ extra_cost += PlatformLengthPenalty(platform_length); } } /* total node cost */ n.m_cost = parent_cost + segment_entry_cost + segment_cost + extra_cost; return true; }
/** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(Node &n) { bool bDest = (n.m_key.m_tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(n.GetTrackdir())) != TRACKDIR_BIT_NONE); return bDest; }