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