00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #ifndef YAPF_COSTRAIL_HPP
00013 #define YAPF_COSTRAIL_HPP
00014
00015 #include "../../pbs.h"
00016
00017 template <class Types>
00018 class CYapfCostRailT
00019 : public CYapfCostBase
00020 , public CostRailSettings
00021 {
00022 public:
00023 typedef typename Types::Tpf Tpf;
00024 typedef typename Types::TrackFollower TrackFollower;
00025 typedef typename Types::NodeList::Titem Node;
00026 typedef typename Node::Key Key;
00027 typedef typename Node::CachedData CachedData;
00028
00029 protected:
00030
00031
00032 struct TILE {
00033 TileIndex tile;
00034 Trackdir td;
00035 TileType tile_type;
00036 RailType rail_type;
00037
00038 TILE()
00039 {
00040 tile = INVALID_TILE;
00041 td = INVALID_TRACKDIR;
00042 tile_type = MP_VOID;
00043 rail_type = INVALID_RAILTYPE;
00044 }
00045
00046 TILE(TileIndex tile, Trackdir td)
00047 {
00048 this->tile = tile;
00049 this->td = td;
00050 this->tile_type = GetTileType(tile);
00051 this->rail_type = GetTileRailType(tile);
00052 }
00053
00054 TILE(const TILE &src)
00055 {
00056 tile = src.tile;
00057 td = src.td;
00058 tile_type = src.tile_type;
00059 rail_type = src.rail_type;
00060 }
00061 };
00062
00063 protected:
00068 int m_max_cost;
00069 CBlobT<int> m_sig_look_ahead_costs;
00070 bool m_disable_cache;
00071
00072 public:
00073 bool m_stopped_on_first_two_way_signal;
00074 protected:
00075
00076 static const int s_max_segment_cost = 10000;
00077
00078 CYapfCostRailT()
00079 : m_max_cost(0)
00080 , m_disable_cache(false)
00081 , m_stopped_on_first_two_way_signal(false)
00082 {
00083
00084 int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0;
00085 int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1;
00086 int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2;
00087 int *pen = m_sig_look_ahead_costs.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals);
00088 for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) {
00089 pen[i] = p0 + i * (p1 + i * p2);
00090 }
00091 }
00092
00094 Tpf& Yapf()
00095 {
00096 return *static_cast<Tpf*>(this);
00097 }
00098
00099 public:
00100 FORCEINLINE int SlopeCost(TileIndex tile, Trackdir td)
00101 {
00102 CPerfStart perf_cost(Yapf().m_perf_slope_cost);
00103 if (!stSlopeCost(tile, td)) return 0;
00104 return Yapf().PfGetSettings().rail_slope_penalty;
00105 }
00106
00107 FORCEINLINE int CurveCost(Trackdir td1, Trackdir td2)
00108 {
00109 assert(IsValidTrackdir(td1));
00110 assert(IsValidTrackdir(td2));
00111 int cost = 0;
00112 if (TrackFollower::Allow90degTurns()
00113 && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) {
00114
00115 cost += Yapf().PfGetSettings().rail_curve90_penalty;
00116 } else if (td2 != NextTrackdir(td1)) {
00117
00118 cost += Yapf().PfGetSettings().rail_curve45_penalty;
00119 }
00120 return cost;
00121 }
00122
00123 FORCEINLINE int SwitchCost(TileIndex tile1, TileIndex tile2, DiagDirection exitdir)
00124 {
00125 if (IsPlainRailTile(tile1) && IsPlainRailTile(tile2)) {
00126 bool t1 = KillFirstBit(GetTrackBits(tile1) & DiagdirReachesTracks(ReverseDiagDir(exitdir))) != TRACK_BIT_NONE;
00127 bool t2 = KillFirstBit(GetTrackBits(tile2) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
00128 if (t1 && t2) return Yapf().PfGetSettings().rail_doubleslip_penalty;
00129 }
00130 return 0;
00131 }
00132
00134 FORCEINLINE int OneTileCost(TileIndex& tile, Trackdir trackdir)
00135 {
00136 int cost = 0;
00137
00138 if (IsDiagonalTrackdir(trackdir)) {
00139 cost += YAPF_TILE_LENGTH;
00140 switch (GetTileType(tile)) {
00141 case MP_ROAD:
00142
00143 if (IsLevelCrossing(tile)) {
00144 cost += Yapf().PfGetSettings().rail_crossing_penalty;
00145 }
00146 break;
00147
00148 default:
00149 break;
00150 }
00151 } else {
00152
00153 cost = YAPF_TILE_CORNER_LENGTH;
00154 }
00155 return cost;
00156 }
00157
00159 FORCEINLINE bool IsAnyStationTileReserved(TileIndex tile, Trackdir trackdir, int skipped)
00160 {
00161 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(trackdir)));
00162 for (; skipped >= 0; skipped--, tile += diff) {
00163 if (HasStationReservation(tile)) return true;
00164 }
00165 return false;
00166 }
00167
00169 FORCEINLINE int ReservationCost(Node& n, TileIndex tile, Trackdir trackdir, int skipped)
00170 {
00171 if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0;
00172 if (!IsPbsSignal(n.m_last_signal_type)) return 0;
00173
00174 if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) {
00175 return Yapf().PfGetSettings().rail_pbs_station_penalty * (skipped + 1);
00176 } else if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) {
00177 int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty;
00178 if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
00179 return cost * (skipped + 1);
00180 }
00181 return 0;
00182 }
00183
00184 int SignalCost(Node& n, TileIndex tile, Trackdir trackdir)
00185 {
00186 int cost = 0;
00187
00188 CPerfStart perf_cost(Yapf().m_perf_other_cost);
00189 if (IsTileType(tile, MP_RAILWAY)) {
00190 bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir));
00191 bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
00192 if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
00193
00194 n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
00195 } else {
00196 if (has_signal_along) {
00197 SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
00198 SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
00199
00200 n.m_last_signal_type = sig_type;
00201
00202
00203 int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0;
00204 if (sig_state != SIGNAL_STATE_RED) {
00205
00206 n.flags_u.flags_s.m_last_signal_was_red = false;
00207
00208 if (look_ahead_cost < 0) {
00209
00210 cost -= look_ahead_cost;
00211 }
00212 } else {
00213
00214
00215 if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
00216
00217 n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
00218 Yapf().m_stopped_on_first_two_way_signal = true;
00219 return -1;
00220 }
00221 n.m_last_red_signal_type = sig_type;
00222 n.flags_u.flags_s.m_last_signal_was_red = true;
00223
00224
00225 if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
00226
00227 cost += look_ahead_cost;
00228 }
00229
00230
00231 if (n.m_num_signals_passed == 0) {
00232 switch (sig_type) {
00233 case SIGTYPE_COMBO:
00234 case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break;
00235 case SIGTYPE_NORMAL:
00236 case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
00237 default: break;
00238 }
00239 }
00240 }
00241
00242 n.m_num_signals_passed++;
00243 n.m_segment->m_last_signal_tile = tile;
00244 n.m_segment->m_last_signal_td = trackdir;
00245 }
00246
00247 if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
00248 cost += n.m_num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
00249 }
00250 }
00251 }
00252 return cost;
00253 }
00254
00255 FORCEINLINE int PlatformLengthPenalty(int platform_length)
00256 {
00257 int cost = 0;
00258 const Train *v = Yapf().GetVehicle();
00259 assert(v != NULL);
00260 assert(v->type == VEH_TRAIN);
00261 assert(v->tcache.cached_total_length != 0);
00262 int missing_platform_length = (v->tcache.cached_total_length + TILE_SIZE - 1) / TILE_SIZE - platform_length;
00263 if (missing_platform_length < 0) {
00264
00265 cost += Yapf().PfGetSettings().rail_longer_platform_penalty + Yapf().PfGetSettings().rail_longer_platform_per_tile_penalty * -missing_platform_length;
00266 } else if (missing_platform_length > 0) {
00267
00268 cost += Yapf().PfGetSettings().rail_shorter_platform_penalty + Yapf().PfGetSettings().rail_shorter_platform_per_tile_penalty * missing_platform_length;
00269 }
00270 return cost;
00271 }
00272
00273 public:
00274 FORCEINLINE void SetMaxCost(int max_cost)
00275 {
00276 m_max_cost = max_cost;
00277 }
00278
00282 FORCEINLINE bool PfCalcCost(Node &n, const TrackFollower *tf)
00283 {
00284 assert(!n.flags_u.flags_s.m_targed_seen);
00285 assert(tf->m_new_tile == n.m_key.m_tile);
00286 assert((TrackdirToTrackdirBits(n.m_key.m_td) & tf->m_new_td_bits) != TRACKDIR_BIT_NONE);
00287
00288 CPerfStart perf_cost(Yapf().m_perf_cost);
00289
00290
00291 bool has_parent = (n.m_parent != NULL);
00292
00293
00294 CachedData &segment = *n.m_segment;
00295 bool is_cached_segment = (segment.m_cost >= 0);
00296
00297 int parent_cost = has_parent ? n.m_parent->m_cost : 0;
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318 int transition_cost = 0;
00319 int extra_cost = 0;
00320
00321
00322
00323
00324
00325
00326 int segment_entry_cost = 0;
00327 int segment_cost = 0;
00328
00329 const Train *v = Yapf().GetVehicle();
00330
00331
00332 TILE cur(n.m_key.m_tile, n.m_key.m_td);
00333
00334
00335 TILE prev = !has_parent ? TILE() : TILE(n.m_parent->GetLastTile(), n.m_parent->GetLastTrackdir());
00336
00337 EndSegmentReasonBits end_segment_reason = ESRB_NONE;
00338
00339 TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
00340
00341 if (!has_parent) {
00342
00343 assert(!is_cached_segment);
00344
00345 goto no_entry_cost;
00346 }
00347
00348 for (;;) {
00349
00350 transition_cost = Yapf().CurveCost(prev.td, cur.td);
00351 transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td));
00352
00353
00354
00355 if (segment_cost == 0) {
00356
00357 segment_entry_cost = transition_cost;
00358 transition_cost = 0;
00359
00360
00361 if (is_cached_segment) {
00362
00363 segment_cost = segment.m_cost;
00364
00365 end_segment_reason = segment.m_end_segment_reason;
00366
00367 if (segment.m_last_signal_tile != INVALID_TILE) {
00368 assert(HasSignalOnTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td));
00369 SignalState sig_state = GetSignalStateByTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td);
00370 bool is_red = (sig_state == SIGNAL_STATE_RED);
00371 n.flags_u.flags_s.m_last_signal_was_red = is_red;
00372 if (is_red) {
00373 n.m_last_red_signal_type = GetSignalType(segment.m_last_signal_tile, TrackdirToTrack(segment.m_last_signal_td));
00374 }
00375 }
00376
00377 cur = TILE(n.GetLastTile(), n.GetLastTrackdir());
00378 break;
00379 }
00380 } else {
00381
00382 segment_cost += transition_cost;
00383 }
00384
00385 no_entry_cost:
00386
00387
00388 segment_cost += Yapf().OneTileCost(cur.tile, cur.td);
00389
00390
00391 segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
00392
00393
00394 segment_cost += Yapf().SlopeCost(cur.tile, cur.td);
00395
00396
00397 segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);
00398
00399
00400 segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped);
00401
00402 end_segment_reason = segment.m_end_segment_reason;
00403
00404
00405 if (cur.tile == prev.tile) {
00406
00407 assert(IsRailDepot(cur.tile));
00408 segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty;
00409
00410 end_segment_reason |= ESRB_DEPOT;
00411
00412 } else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) {
00413 if (v->current_order.IsType(OT_GOTO_WAYPOINT) && GetStationIndex(cur.tile) == v->current_order.GetDestination()) {
00414
00415
00416
00417 CFollowTrackRail ft(v);
00418 TileIndex t = cur.tile;
00419 Trackdir td = cur.td;
00420 while (ft.Follow(t, td)) {
00421 assert(t != ft.m_new_tile);
00422 t = ft.m_new_tile;
00423 if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) {
00424
00425
00426
00427 td = INVALID_TRACKDIR;
00428 break;
00429 }
00430 td = RemoveFirstTrackdir(&ft.m_new_td_bits);
00431
00432 if (IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg)) break;
00433 }
00434 if (td == INVALID_TRACKDIR ||
00435 !IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg) ||
00436 !IsWaitingPositionFree(v, t, td, _settings_game.pf.forbid_90_deg)) {
00437 extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
00438 }
00439 }
00440
00441 end_segment_reason |= ESRB_WAYPOINT;
00442
00443 } else if (tf->m_is_station) {
00444
00445 uint platform_length = tf->m_tiles_skipped + 1;
00446
00447
00448 segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
00449
00450 end_segment_reason |= ESRB_STATION;
00451
00452 } else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) {
00453
00454 if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
00455 end_segment_reason |= ESRB_SAFE_TILE;
00456 }
00457 }
00458
00459
00460
00461 if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size())
00462 {
00463 int min_speed = 0;
00464 int max_speed = tf->GetSpeedLimit(&min_speed);
00465 if (max_speed < v->max_speed) {
00466 extra_cost += YAPF_TILE_LENGTH * (v->max_speed - max_speed) * (4 + tf->m_tiles_skipped) / v->max_speed;
00467 }
00468 if (min_speed > v->max_speed) {
00469 extra_cost += YAPF_TILE_LENGTH * (min_speed - v->max_speed);
00470 }
00471 }
00472
00473
00474
00475 if (m_max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > m_max_cost) {
00476 end_segment_reason |= ESRB_PATH_TOO_LONG;
00477 }
00478
00479
00480 tf = &tf_local;
00481 tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
00482
00483 if (!tf_local.Follow(cur.tile, cur.td)) {
00484 assert(tf_local.m_err != TrackFollower::EC_NONE);
00485
00486 if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) {
00487 end_segment_reason |= ESRB_RAIL_TYPE;
00488 } else {
00489 end_segment_reason |= ESRB_DEAD_END;
00490 }
00491
00492 if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
00493 end_segment_reason |= ESRB_SAFE_TILE;
00494 }
00495 break;
00496 }
00497
00498
00499 if (KillFirstBit(tf_local.m_new_td_bits) != TRACKDIR_BIT_NONE) {
00500
00501 end_segment_reason |= ESRB_CHOICE_FOLLOWS;
00502 break;
00503 }
00504
00505
00506 TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits));
00507
00508 if (TrackFollower::DoTrackMasking() && HasPbsSignalOnTrackdir(next.tile, next.td)) {
00509
00510 end_segment_reason |= ESRB_SAFE_TILE;
00511 }
00512
00513
00514 if (next.rail_type != cur.rail_type) {
00515
00516 end_segment_reason |= ESRB_RAIL_TYPE;
00517 break;
00518 }
00519
00520
00521 if (next.tile == n.m_key.m_tile && next.td == n.m_key.m_td) {
00522 end_segment_reason |= ESRB_INFINITE_LOOP;
00523 break;
00524 }
00525
00526 if (segment_cost > s_max_segment_cost) {
00527
00528
00529 if (IsTileType(tf->m_new_tile, MP_RAILWAY)) {
00530 end_segment_reason |= ESRB_SEGMENT_TOO_LONG;
00531 break;
00532 }
00533 }
00534
00535
00536 if (end_segment_reason != ESRB_NONE) {
00537 break;
00538 }
00539
00540
00541 prev = cur;
00542 cur = next;
00543
00544 }
00545
00546 bool target_seen = false;
00547 if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) {
00548
00549 if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
00550
00551 target_seen = true;
00552 }
00553 }
00554
00555
00556 if (!is_cached_segment) {
00557
00558 segment.m_cost = segment_cost;
00559 segment.m_end_segment_reason = end_segment_reason & ESRB_CACHED_MASK;
00560
00561 n.SetLastTileTrackdir(cur.tile, cur.td);
00562 }
00563
00564
00565 if (!target_seen && (end_segment_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) {
00566
00567 return false;
00568 }
00569
00570
00571 if (target_seen) {
00572 n.flags_u.flags_s.m_targed_seen = true;
00573
00574 if (n.flags_u.flags_s.m_last_signal_was_red) {
00575 if (n.m_last_red_signal_type == SIGTYPE_EXIT) {
00576
00577 extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
00578 } else {
00579
00580 extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
00581 }
00582 }
00583
00584
00585 if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) {
00586 const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
00587 assert(st != NULL);
00588 uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
00589
00590 extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length;
00591
00592 extra_cost += PlatformLengthPenalty(platform_length);
00593 }
00594 }
00595
00596
00597 n.m_cost = parent_cost + segment_entry_cost + segment_cost + extra_cost;
00598
00599 return true;
00600 }
00601
00602 FORCEINLINE bool CanUseGlobalCache(Node& n) const
00603 {
00604 return !m_disable_cache
00605 && (n.m_parent != NULL)
00606 && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size());
00607 }
00608
00609 FORCEINLINE void ConnectNodeToCachedData(Node& n, CachedData& ci)
00610 {
00611 n.m_segment = &ci;
00612 if (n.m_segment->m_cost < 0) {
00613 n.m_segment->m_last_tile = n.m_key.m_tile;
00614 n.m_segment->m_last_td = n.m_key.m_td;
00615 }
00616 }
00617
00618 void DisableCache(bool disable)
00619 {
00620 m_disable_cache = disable;
00621 }
00622 };
00623
00624 #endif