KUDr@3900: /* $Id$ */ KUDr@3900: KUDr@3900: #ifndef YAPF_COSTRAIL_HPP KUDr@3900: #define YAPF_COSTRAIL_HPP KUDr@3900: KUDr@3900: KUDr@3900: template KUDr@3900: class CYapfCostRailT KUDr@3900: : public CYapfCostBase KUDr@3900: , public CostRailSettings KUDr@3900: { KUDr@3900: public: KUDr@3914: typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) KUDr@3900: typedef typename Types::TrackFollower TrackFollower; KUDr@3900: typedef typename Types::NodeList::Titem Node; ///< this will be our node type KUDr@3914: typedef typename Node::Key Key; ///< key to hash tables KUDr@3900: typedef typename Node::CachedData CachedData; KUDr@3900: KUDr@3900: protected: KUDr@3900: int m_max_cost; KUDr@3900: CBlobT m_sig_look_ahead_costs; KUDr@3900: KUDr@3900: static const int s_max_segment_cost = 10000; KUDr@3900: KUDr@3900: CYapfCostRailT() : m_max_cost(0) KUDr@3900: { KUDr@3900: // pre-compute look-ahead penalties into array KUDr@3900: int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0; KUDr@3900: int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1; KUDr@3900: int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2; KUDr@3900: int *pen = m_sig_look_ahead_costs.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals); KUDr@3900: for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) KUDr@3900: pen[i] = p0 + i * (p1 + i * p2); KUDr@3900: } KUDr@3900: KUDr@3914: /// to access inherited path finder KUDr@3900: Tpf& Yapf() {return *static_cast(this);} KUDr@3900: KUDr@3900: public: KUDr@3900: FORCEINLINE int SlopeCost(TileIndex tile, Trackdir td) KUDr@3900: { KUDr@3900: CPerfStart perf_cost(Yapf().m_perf_slope_cost); KUDr@3900: if (!stSlopeCost(tile, td)) return 0; KUDr@3900: return Yapf().PfGetSettings().rail_slope_penalty; KUDr@3900: } KUDr@3900: KUDr@3900: FORCEINLINE int CurveCost(Trackdir td1, Trackdir td2) KUDr@3900: { KUDr@3900: int cost = 0; KUDr@3900: if (TrackFollower::Allow90degTurns() KUDr@3900: && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) { KUDr@3900: // 90-deg curve penalty KUDr@3900: cost += Yapf().PfGetSettings().rail_curve90_penalty; KUDr@3900: } else if (td2 != NextTrackdir(td1)) { KUDr@3900: // 45-deg curve penalty KUDr@3900: cost += Yapf().PfGetSettings().rail_curve45_penalty; KUDr@3900: } KUDr@3900: return cost; KUDr@3900: } KUDr@3900: KUDr@3900: /** return one tile cost. If tile is a tunnel entry, it is moved to the end of tunnel */ KUDr@3900: FORCEINLINE int OneTileCost(TileIndex& tile, Trackdir trackdir) KUDr@3900: { KUDr@3900: int cost = 0; KUDr@3900: // set base cost KUDr@3900: if (IsDiagonalTrackdir(trackdir)) { KUDr@3900: cost += YAPF_TILE_LENGTH; KUDr@3900: switch (GetTileType(tile)) { KUDr@3900: case MP_STREET: KUDr@3900: /* Increase the cost for level crossings */ KUDr@3900: if (IsLevelCrossing(tile)) KUDr@3900: cost += Yapf().PfGetSettings().rail_crossing_penalty; KUDr@3900: break; KUDr@3900: KUDr@3900: case MP_STATION: KUDr@3900: // penalty for passing station tiles KUDr@3900: cost += Yapf().PfGetSettings().rail_station_penalty; KUDr@3900: break; KUDr@3900: KUDr@3900: default: KUDr@3900: break; KUDr@3900: } KUDr@3900: } else { KUDr@3900: // non-diagonal trackdir KUDr@3900: cost = YAPF_TILE_CORNER_LENGTH; KUDr@3900: } KUDr@3900: return cost; KUDr@3900: } KUDr@3900: KUDr@3900: int SignalCost(Node& n, TileIndex tile, Trackdir trackdir) KUDr@3900: { KUDr@3900: int cost = 0; KUDr@3900: // if there is one-way signal in the opposite direction, then it is not our way KUDr@3900: CPerfStart perf_cost(Yapf().m_perf_other_cost); KUDr@3900: if (IsTileType(tile, MP_RAILWAY)) { KUDr@3900: bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir)); KUDr@3900: bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); KUDr@3900: if (has_signal_against && !has_signal_along) { KUDr@3900: // one-way signal in opposite direction KUDr@3900: n.m_segment->flags_u.flags_s.m_end_of_line = true; KUDr@3900: } else if (has_signal_along) { KUDr@3900: SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir); KUDr@3900: if (sig_state != SIGNAL_STATE_RED) { KUDr@3900: // green signal KUDr@3900: n.flags_u.flags_s.m_last_signal_was_red = false; KUDr@3900: } else { KUDr@3900: // we have a red signal in our direction KUDr@3900: // was it first signal which is two-way? KUDr@3979: if (Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) { KUDr@3900: // yes, the first signal is two-way red signal => DEAD END KUDr@3900: n.m_segment->flags_u.flags_s.m_end_of_line = true; KUDr@3900: return -1; KUDr@3900: } KUDr@3900: SignalType sig_type = GetSignalType(tile); KUDr@3900: n.m_last_red_signal_type = sig_type; KUDr@3900: n.flags_u.flags_s.m_last_signal_was_red = true; KUDr@3900: KUDr@3900: // look-ahead signal penalty KUDr@3900: if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) { KUDr@3900: cost += m_sig_look_ahead_costs.Data()[n.m_num_signals_passed]; KUDr@3900: } KUDr@3900: KUDr@3900: // special signal penalties KUDr@3900: if (n.m_num_signals_passed == 0) { KUDr@3900: switch (sig_type) { KUDr@3900: case SIGTYPE_COMBO: KUDr@3900: case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit KUDr@3900: case SIGTYPE_NORMAL: KUDr@3900: case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break; KUDr@3900: }; KUDr@3900: } KUDr@3900: } KUDr@3900: n.m_num_signals_passed++; KUDr@3900: n.m_segment->m_last_signal_tile = tile; KUDr@3900: n.m_segment->m_last_signal_td = trackdir; KUDr@3900: } KUDr@3900: } KUDr@3900: return cost; KUDr@3900: } KUDr@3900: KUDr@3931: FORCEINLINE int PlatformLengthPenalty(int platform_length) KUDr@3931: { KUDr@3931: int cost = 0; KUDr@3931: const Vehicle* v = Yapf().GetVehicle(); KUDr@3931: assert(v != NULL); KUDr@3931: assert(v->type == VEH_Train); KUDr@3931: assert(v->u.rail.cached_total_length != 0); KUDr@3931: int needed_platform_length = (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE; KUDr@3931: if (platform_length > needed_platform_length) { KUDr@3931: // apply penalty for longer platform than needed KUDr@3932: cost += Yapf().PfGetSettings().rail_longer_platform_penalty; KUDr@3932: } else if (needed_platform_length > platform_length) { KUDr@3931: // apply penalty for shorter platform than needed KUDr@3932: cost += Yapf().PfGetSettings().rail_shorter_platform_penalty; KUDr@3931: } KUDr@3931: return cost; KUDr@3931: } KUDr@3931: KUDr@3900: public: KUDr@3900: FORCEINLINE void SetMaxCost(int max_cost) {m_max_cost = max_cost;} KUDr@3900: KUDr@3914: /** Called by YAPF to calculate the cost from the origin to the given node. rubidium@4549: * Calculates only the cost of given node, adds it to the parent node cost rubidium@4549: * and stores the result into Node::m_cost member */ KUDr@3900: FORCEINLINE bool PfCalcCost(Node& n) KUDr@3900: { KUDr@3900: assert(!n.flags_u.flags_s.m_targed_seen); KUDr@3900: CPerfStart perf_cost(Yapf().m_perf_cost); KUDr@3900: int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0; KUDr@3900: int first_tile_cost = 0; KUDr@3900: int segment_cost = 0; KUDr@3900: int extra_cost = 0; KUDr@3915: const Vehicle* v = Yapf().GetVehicle(); KUDr@3900: KUDr@3900: // start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment KUDr@3900: TileIndex prev_tile = (n.m_parent != NULL) ? n.m_parent->GetLastTile() : INVALID_TILE; KUDr@3900: Trackdir prev_trackdir = (n.m_parent != NULL) ? n.m_parent->GetLastTrackdir() : INVALID_TRACKDIR; KUDr@3900: TileType prev_tile_type = (n.m_parent != NULL) ? GetTileType(n.m_parent->GetLastTile()) : MP_VOID; KUDr@3900: KUDr@3900: TileIndex tile = n.m_key.m_tile; KUDr@3900: Trackdir trackdir = n.m_key.m_td; KUDr@3900: TileType tile_type = GetTileType(tile); KUDr@3900: KUDr@3900: RailType rail_type = GetTileRailType(tile, trackdir); KUDr@3900: KUDr@3930: bool target_seen = Yapf().PfDetectDestination(tile, trackdir); KUDr@3900: KUDr@3900: while (true) { KUDr@3900: segment_cost += Yapf().OneTileCost(tile, trackdir); KUDr@3900: segment_cost += Yapf().CurveCost(prev_trackdir, trackdir); KUDr@3900: segment_cost += Yapf().SlopeCost(tile, trackdir); KUDr@3900: segment_cost += Yapf().SignalCost(n, tile, trackdir); KUDr@3900: if (n.m_segment->flags_u.flags_s.m_end_of_line) { KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // finish if we have reached the destination KUDr@3900: if (target_seen) { KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // finish on first station tile - segment should end here to avoid target skipping KUDr@3900: // when cached segments are used KUDr@3900: if (tile_type == MP_STATION && prev_tile_type != MP_STATION) { KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // finish also on waypoint - same workaround as for first station tile KUDr@3900: if (tile_type == MP_RAILWAY && IsRailWaypoint(tile)) { KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // if there are no reachable trackdirs on the next tile, we have end of road KUDr@3900: TrackFollower F(v, &Yapf().m_perf_ts_cost); KUDr@3900: if (!F.Follow(tile, trackdir)) { KUDr@3900: // we can't continue? KUDr@3900: // n.m_segment->flags_u.flags_s.m_end_of_line = true; KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // if there are more trackdirs available & reachable, we are at the end of segment KUDr@3900: if (KillFirstBit2x64(F.m_new_td_bits) != 0) { KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits); KUDr@3900: KUDr@3900: { KUDr@3900: // end segment if train is about to enter simple loop with no junctions KUDr@3900: // so next time it should stop on the next if KUDr@3900: if (segment_cost > s_max_segment_cost && IsTileType(F.m_new_tile, MP_RAILWAY)) KUDr@3900: break; KUDr@3900: KUDr@3900: // stop if train is on simple loop with no junctions KUDr@3900: if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) KUDr@3900: return false; KUDr@3900: } KUDr@3900: KUDr@3900: // if tail type changes, finish segment (cached segment can't contain more rail types) KUDr@3900: { KUDr@3900: RailType new_rail_type = GetTileRailType(F.m_new_tile, (Trackdir)FindFirstBit2x64(F.m_new_td_bits)); KUDr@3900: if (new_rail_type != rail_type) { KUDr@3900: break; KUDr@3900: } KUDr@3900: rail_type = new_rail_type; KUDr@3900: } KUDr@3900: KUDr@3900: // move to the next tile KUDr@3900: prev_tile = tile; KUDr@3900: prev_trackdir = trackdir; KUDr@3900: prev_tile_type = tile_type; KUDr@3900: KUDr@3900: tile = F.m_new_tile; KUDr@3900: trackdir = new_td; KUDr@3900: tile_type = GetTileType(tile); KUDr@3900: KUDr@3930: target_seen = Yapf().PfDetectDestination(tile, trackdir); KUDr@3930: KUDr@3900: // reversing in depot penalty KUDr@3900: if (tile == prev_tile) { KUDr@3900: segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty; KUDr@3900: break; KUDr@3900: } KUDr@3900: KUDr@3900: // if we skipped some tunnel tiles, add their cost KUDr@3931: segment_cost += YAPF_TILE_LENGTH * F.m_tiles_skipped; KUDr@3931: KUDr@3931: // add penalty for skipped station tiles KUDr@3931: if (F.m_is_station) KUDr@3931: { KUDr@3931: if (target_seen) { KUDr@3931: // it is our destination station KUDr@3931: uint platform_length = F.m_tiles_skipped + 1; KUDr@3931: segment_cost += PlatformLengthPenalty(platform_length); KUDr@3931: } else { KUDr@3931: // station is not our destination station, apply penalty for skipped platform tiles KUDr@3931: segment_cost += Yapf().PfGetSettings().rail_station_penalty * F.m_tiles_skipped; KUDr@3931: } KUDr@3931: } KUDr@3900: KUDr@3900: // add min/max speed penalties KUDr@3900: int min_speed = 0; KUDr@3900: int max_speed = F.GetSpeedLimit(&min_speed); KUDr@3900: if (max_speed < v->max_speed) KUDr@3900: segment_cost += YAPF_TILE_LENGTH * (v->max_speed - max_speed) / v->max_speed; KUDr@3900: if (min_speed > v->max_speed) KUDr@3900: segment_cost += YAPF_TILE_LENGTH * (min_speed - v->max_speed); KUDr@3900: KUDr@3900: // finish if we already exceeded the maximum cost KUDr@3900: if (m_max_cost > 0 && (parent_cost + first_tile_cost + segment_cost) > m_max_cost) { KUDr@3900: return false; KUDr@3900: } KUDr@3900: KUDr@3900: if (first_tile_cost == 0) { KUDr@3900: // we just have done first tile KUDr@3900: first_tile_cost = segment_cost; KUDr@3900: segment_cost = 0; KUDr@3900: KUDr@3900: // look if we can reuse existing (cached) segment cost KUDr@3900: if (n.m_segment->m_cost >= 0) { KUDr@3900: // reuse the cached segment cost KUDr@3900: break; KUDr@3900: } KUDr@3900: } KUDr@3900: // segment cost was not filled yes, we have not cached it yet KUDr@3900: n.SetLastTileTrackdir(tile, trackdir); KUDr@3900: KUDr@3900: } // while (true) KUDr@3900: KUDr@3900: if (first_tile_cost == 0) { KUDr@3900: // we have just finished first tile KUDr@3900: first_tile_cost = segment_cost; KUDr@3900: segment_cost = 0; KUDr@3900: } KUDr@3900: KUDr@3900: // do we have cached segment cost? KUDr@3900: if (n.m_segment->m_cost >= 0) { KUDr@3900: // reuse the cached segment cost KUDr@3900: segment_cost = n.m_segment->m_cost; KUDr@3900: } else { KUDr@3900: // save segment cost KUDr@3900: n.m_segment->m_cost = segment_cost; KUDr@3900: KUDr@3900: // save end of segment back to the node KUDr@3900: n.SetLastTileTrackdir(tile, trackdir); KUDr@3900: } KUDr@3900: KUDr@3900: // special costs for the case we have reached our target KUDr@3900: if (target_seen) { KUDr@3900: n.flags_u.flags_s.m_targed_seen = true; KUDr@3900: if (n.flags_u.flags_s.m_last_signal_was_red) { KUDr@3900: if (n.m_last_red_signal_type == SIGTYPE_EXIT) { KUDr@3900: // last signal was red pre-signal-exit KUDr@3900: extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty; KUDr@3900: } else { KUDr@3900: // last signal was red, but not exit KUDr@3900: extra_cost += Yapf().PfGetSettings().rail_lastred_penalty; KUDr@3900: } KUDr@3900: } KUDr@3900: } KUDr@3900: KUDr@3900: // total node cost KUDr@3900: n.m_cost = parent_cost + first_tile_cost + segment_cost + extra_cost; KUDr@3900: KUDr@3900: return !n.m_segment->flags_u.flags_s.m_end_of_line; KUDr@3900: } KUDr@3900: KUDr@3900: FORCEINLINE bool CanUseGlobalCache(Node& n) const KUDr@3900: { KUDr@3900: return (n.m_parent != NULL) KUDr@3900: && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size()); KUDr@3900: } KUDr@3900: KUDr@3900: FORCEINLINE void ConnectNodeToCachedData(Node& n, CachedData& ci) KUDr@3900: { KUDr@3900: n.m_segment = &ci; KUDr@3900: if (n.m_segment->m_cost < 0) { KUDr@3900: n.m_segment->m_last_tile = n.m_key.m_tile; KUDr@3900: n.m_segment->m_last_td = n.m_key.m_td; KUDr@3900: } KUDr@3900: } KUDr@3900: KUDr@3900: }; KUDr@3900: KUDr@3900: KUDr@3900: KUDr@3900: #endif /* YAPF_COSTRAIL_HPP */