OpenTTD
yapf_costrail.hpp
Go to the documentation of this file.
1 /* $Id: yapf_costrail.hpp 25609 2013-07-14 09:21:46Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #ifndef YAPF_COSTRAIL_HPP
13 #define YAPF_COSTRAIL_HPP
14 
15 #include "../../pbs.h"
16 
17 template <class Types>
19  : public CYapfCostBase
20 {
21 public:
22  typedef typename Types::Tpf Tpf;
23  typedef typename Types::TrackFollower TrackFollower;
24  typedef typename Types::NodeList::Titem Node;
25  typedef typename Node::Key Key;
26  typedef typename Node::CachedData CachedData;
27 
28 protected:
29 
30  /* Structure used inside PfCalcCost() to keep basic tile information. */
31  struct TILE {
32  TileIndex tile;
33  Trackdir td;
34  TileType tile_type;
35  RailType rail_type;
36 
37  TILE()
38  {
39  tile = INVALID_TILE;
40  td = INVALID_TRACKDIR;
41  tile_type = MP_VOID;
42  rail_type = INVALID_RAILTYPE;
43  }
44 
45  TILE(TileIndex tile, Trackdir td)
46  {
47  this->tile = tile;
48  this->td = td;
49  this->tile_type = GetTileType(tile);
50  this->rail_type = GetTileRailType(tile);
51  }
52 
53  TILE(const TILE &src)
54  {
55  tile = src.tile;
56  td = src.td;
57  tile_type = src.tile_type;
58  rail_type = src.rail_type;
59  }
60  };
61 
62 protected:
68  CBlobT<int> m_sig_look_ahead_costs;
69  bool m_disable_cache;
70 
71 public:
72  bool m_stopped_on_first_two_way_signal;
73 protected:
74 
75  static const int s_max_segment_cost = 10000;
76 
78  : m_max_cost(0)
79  , m_disable_cache(false)
80  , m_stopped_on_first_two_way_signal(false)
81  {
82  /* pre-compute look-ahead penalties into array */
83  int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0;
84  int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1;
85  int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2;
86  int *pen = m_sig_look_ahead_costs.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals);
87  for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) {
88  pen[i] = p0 + i * (p1 + i * p2);
89  }
90  }
91 
93  Tpf& Yapf()
94  {
95  return *static_cast<Tpf*>(this);
96  }
97 
98 public:
99  inline int SlopeCost(TileIndex tile, Trackdir td)
100  {
101  CPerfStart perf_cost(Yapf().m_perf_slope_cost);
102  if (!stSlopeCost(tile, td)) return 0;
103  return Yapf().PfGetSettings().rail_slope_penalty;
104  }
105 
106  inline int CurveCost(Trackdir td1, Trackdir td2)
107  {
108  assert(IsValidTrackdir(td1));
109  assert(IsValidTrackdir(td2));
110  int cost = 0;
111  if (TrackFollower::Allow90degTurns()
113  /* 90-deg curve penalty */
114  cost += Yapf().PfGetSettings().rail_curve90_penalty;
115  } else if (td2 != NextTrackdir(td1)) {
116  /* 45-deg curve penalty */
117  cost += Yapf().PfGetSettings().rail_curve45_penalty;
118  }
119  return cost;
120  }
121 
122  inline int SwitchCost(TileIndex tile1, TileIndex tile2, DiagDirection exitdir)
123  {
124  if (IsPlainRailTile(tile1) && IsPlainRailTile(tile2)) {
126  bool t2 = KillFirstBit(GetTrackBits(tile2) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
127  if (t1 && t2) return Yapf().PfGetSettings().rail_doubleslip_penalty;
128  }
129  return 0;
130  }
131 
133  inline int OneTileCost(TileIndex& tile, Trackdir trackdir)
134  {
135  int cost = 0;
136  /* set base cost */
137  if (IsDiagonalTrackdir(trackdir)) {
138  cost += YAPF_TILE_LENGTH;
139  switch (GetTileType(tile)) {
140  case MP_ROAD:
141  /* Increase the cost for level crossings */
142  if (IsLevelCrossing(tile)) {
143  cost += Yapf().PfGetSettings().rail_crossing_penalty;
144  }
145  break;
146 
147  default:
148  break;
149  }
150  } else {
151  /* non-diagonal trackdir */
153  }
154  return cost;
155  }
156 
158  inline bool IsAnyStationTileReserved(TileIndex tile, Trackdir trackdir, int skipped)
159  {
161  for (; skipped >= 0; skipped--, tile += diff) {
162  if (HasStationReservation(tile)) return true;
163  }
164  return false;
165  }
166 
168  inline int ReservationCost(Node& n, TileIndex tile, Trackdir trackdir, int skipped)
169  {
170  if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0;
171  if (!IsPbsSignal(n.m_last_signal_type)) return 0;
172 
173  if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) {
174  return Yapf().PfGetSettings().rail_pbs_station_penalty * (skipped + 1);
175  } else if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) {
176  int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty;
177  if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
178  return cost * (skipped + 1);
179  }
180  return 0;
181  }
182 
183  int SignalCost(Node& n, TileIndex tile, Trackdir trackdir)
184  {
185  int cost = 0;
186  /* if there is one-way signal in the opposite direction, then it is not our way */
187  CPerfStart perf_cost(Yapf().m_perf_other_cost);
188  if (IsTileType(tile, MP_RAILWAY)) {
189  bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir));
190  bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
191  if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
192  /* one-way signal in opposite direction */
193  n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
194  } else {
195  if (has_signal_along) {
196  SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
197  SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
198 
199  n.m_last_signal_type = sig_type;
200 
201  /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
202  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;
203  if (sig_state != SIGNAL_STATE_RED) {
204  /* green signal */
205  n.flags_u.flags_s.m_last_signal_was_red = false;
206  /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
207  if (look_ahead_cost < 0) {
208  /* add its negation to the cost */
209  cost -= look_ahead_cost;
210  }
211  } else {
212  /* we have a red signal in our direction
213  * was it first signal which is two-way? */
214  if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
215  /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
216  Yapf().PruneIntermediateNodeBranch();
217  n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
218  Yapf().m_stopped_on_first_two_way_signal = true;
219  return -1;
220  }
221  n.m_last_red_signal_type = sig_type;
222  n.flags_u.flags_s.m_last_signal_was_red = true;
223 
224  /* look-ahead signal penalty */
225  if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
226  /* add the look ahead penalty only if it is positive */
227  cost += look_ahead_cost;
228  }
229 
230  /* special signal penalties */
231  if (n.m_num_signals_passed == 0) {
232  switch (sig_type) {
233  case SIGTYPE_COMBO:
234  case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
235  case SIGTYPE_NORMAL:
236  case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
237  default: break;
238  }
239  }
240  }
241 
242  n.m_num_signals_passed++;
243  n.m_segment->m_last_signal_tile = tile;
244  n.m_segment->m_last_signal_td = trackdir;
245  }
246 
247  if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
248  cost += n.m_num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
249  }
250  }
251  }
252  return cost;
253  }
254 
255  inline int PlatformLengthPenalty(int platform_length)
256  {
257  int cost = 0;
258  const Train *v = Yapf().GetVehicle();
259  assert(v != NULL);
260  assert(v->type == VEH_TRAIN);
261  assert(v->gcache.cached_total_length != 0);
262  int missing_platform_length = CeilDiv(v->gcache.cached_total_length, TILE_SIZE) - platform_length;
263  if (missing_platform_length < 0) {
264  /* apply penalty for longer platform than needed */
265  cost += Yapf().PfGetSettings().rail_longer_platform_penalty + Yapf().PfGetSettings().rail_longer_platform_per_tile_penalty * -missing_platform_length;
266  } else if (missing_platform_length > 0) {
267  /* apply penalty for shorter platform than needed */
268  cost += Yapf().PfGetSettings().rail_shorter_platform_penalty + Yapf().PfGetSettings().rail_shorter_platform_per_tile_penalty * missing_platform_length;
269  }
270  return cost;
271  }
272 
273 public:
274  inline void SetMaxCost(int max_cost)
275  {
276  m_max_cost = max_cost;
277  }
278 
284  inline bool PfCalcCost(Node &n, const TrackFollower *tf)
285  {
286  assert(!n.flags_u.flags_s.m_targed_seen);
287  assert(tf->m_new_tile == n.m_key.m_tile);
288  assert((TrackdirToTrackdirBits(n.m_key.m_td) & tf->m_new_td_bits) != TRACKDIR_BIT_NONE);
289 
290  CPerfStart perf_cost(Yapf().m_perf_cost);
291 
292  /* Does the node have some parent node? */
293  bool has_parent = (n.m_parent != NULL);
294 
295  /* Do we already have a cached segment? */
296  CachedData &segment = *n.m_segment;
297  bool is_cached_segment = (segment.m_cost >= 0);
298 
299  int parent_cost = has_parent ? n.m_parent->m_cost : 0;
300 
301  /* Each node cost contains 2 or 3 main components:
302  * 1. Transition cost - cost of the move from previous node (tile):
303  * - curve cost (or zero for straight move)
304  * 2. Tile cost:
305  * - base tile cost
306  * - YAPF_TILE_LENGTH for diagonal tiles
307  * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
308  * - tile penalties
309  * - tile slope penalty (upward slopes)
310  * - red signal penalty
311  * - level crossing penalty
312  * - speed-limit penalty (bridges)
313  * - station platform penalty
314  * - penalty for reversing in the depot
315  * - etc.
316  * 3. Extra cost (applies to the last node only)
317  * - last red signal penalty
318  * - penalty for too long or too short platform on the destination station
319  */
320  int transition_cost = 0;
321  int extra_cost = 0;
322 
323  /* Segment: one or more tiles connected by contiguous tracks of the same type.
324  * Each segment cost includes 'Tile cost' for all its tiles (including the first
325  * and last), and the 'Transition cost' between its tiles. The first transition
326  * cost of segment entry (move from the 'parent' node) is not included!
327  */
328  int segment_entry_cost = 0;
329  int segment_cost = 0;
330 
331  const Train *v = Yapf().GetVehicle();
332 
333  /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
334  TILE cur(n.m_key.m_tile, n.m_key.m_td);
335 
336  /* the previous tile will be needed for transition cost calculations */
337  TILE prev = !has_parent ? TILE() : TILE(n.m_parent->GetLastTile(), n.m_parent->GetLastTrackdir());
338 
339  EndSegmentReasonBits end_segment_reason = ESRB_NONE;
340 
341  TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
342 
343  if (!has_parent) {
344  /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */
345  assert(!is_cached_segment);
346  /* Skip the first transition cost calculation. */
347  goto no_entry_cost;
348  }
349 
350  for (;;) {
351  /* Transition cost (cost of the move from previous tile) */
352  transition_cost = Yapf().CurveCost(prev.td, cur.td);
353  transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td));
354 
355  /* First transition cost counts against segment entry cost, other transitions
356  * inside segment will come to segment cost (and will be cached) */
357  if (segment_cost == 0) {
358  /* We just entered the loop. First transition cost goes to segment entry cost)*/
359  segment_entry_cost = transition_cost;
360  transition_cost = 0;
361 
362  /* It is the right time now to look if we can reuse the cached segment cost. */
363  if (is_cached_segment) {
364  /* Yes, we already know the segment cost. */
365  segment_cost = segment.m_cost;
366  /* We know also the reason why the segment ends. */
367  end_segment_reason = segment.m_end_segment_reason;
368  /* We will need also some information about the last signal (if it was red). */
369  if (segment.m_last_signal_tile != INVALID_TILE) {
370  assert(HasSignalOnTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td));
371  SignalState sig_state = GetSignalStateByTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td);
372  bool is_red = (sig_state == SIGNAL_STATE_RED);
373  n.flags_u.flags_s.m_last_signal_was_red = is_red;
374  if (is_red) {
375  n.m_last_red_signal_type = GetSignalType(segment.m_last_signal_tile, TrackdirToTrack(segment.m_last_signal_td));
376  }
377  }
378  /* No further calculation needed. */
379  cur = TILE(n.GetLastTile(), n.GetLastTrackdir());
380  break;
381  }
382  } else {
383  /* Other than first transition cost count as the regular segment cost. */
384  segment_cost += transition_cost;
385  }
386 
387 no_entry_cost: // jump here at the beginning if the node has no parent (it is the first node)
388 
389  /* All other tile costs will be calculated here. */
390  segment_cost += Yapf().OneTileCost(cur.tile, cur.td);
391 
392  /* If we skipped some tunnel/bridge/station tiles, add their base cost */
393  segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
394 
395  /* Slope cost. */
396  segment_cost += Yapf().SlopeCost(cur.tile, cur.td);
397 
398  /* Signal cost (routine can modify segment data). */
399  segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);
400 
401  /* Reserved tiles. */
402  segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped);
403 
404  end_segment_reason = segment.m_end_segment_reason;
405 
406  /* Tests for 'potential target' reasons to close the segment. */
407  if (cur.tile == prev.tile) {
408  /* Penalty for reversing in a depot. */
409  assert(IsRailDepot(cur.tile));
410  segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty;
411  /* We will end in this pass (depot is possible target) */
412  end_segment_reason |= ESRB_DEPOT;
413 
414  } else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) {
415  if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
416  GetStationIndex(cur.tile) == v->current_order.GetDestination() &&
417  !Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
418  /* This waypoint is our destination; maybe this isn't an unreserved
419  * one, so check that and if so see that as the last signal being
420  * red. This way waypoints near stations should work better. */
421  CFollowTrackRail ft(v);
422  TileIndex t = cur.tile;
423  Trackdir td = cur.td;
424  while (ft.Follow(t, td)) {
425  assert(t != ft.m_new_tile);
426  t = ft.m_new_tile;
428  /* We encountered a junction; it's going to be too complex to
429  * handle this perfectly, so just bail out. There is no simple
430  * free path, so try the other possibilities. */
431  td = INVALID_TRACKDIR;
432  break;
433  }
435  /* If this is a safe waiting position we're done searching for it */
436  if (IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg)) break;
437  }
438 
439  /* In the case this platform is (possibly) occupied we add penalty so the
440  * other platforms of this waypoint are evaluated as well, i.e. we assume
441  * that there is a red signal in the waypoint when it's occupied. */
442  if (td == INVALID_TRACKDIR ||
445  extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
446  }
447  }
448  /* Waypoint is also a good reason to finish. */
449  end_segment_reason |= ESRB_WAYPOINT;
450 
451  } else if (tf->m_is_station) {
452  /* Station penalties. */
453  uint platform_length = tf->m_tiles_skipped + 1;
454  /* We don't know yet if the station is our target or not. Act like
455  * if it is pass-through station (not our destination). */
456  segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
457  /* We will end in this pass (station is possible target) */
458  end_segment_reason |= ESRB_STATION;
459 
460  } else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) {
461  /* Searching for a safe tile? */
462  if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
463  end_segment_reason |= ESRB_SAFE_TILE;
464  }
465  }
466 
467  /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
468  * it would cause desync in MP. */
469  if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size())
470  {
471  int min_speed = 0;
472  int max_speed = tf->GetSpeedLimit(&min_speed);
473  int max_veh_speed = v->GetDisplayMaxSpeed();
474  if (max_speed < max_veh_speed) {
475  extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (4 + tf->m_tiles_skipped) / max_veh_speed;
476  }
477  if (min_speed > max_veh_speed) {
478  extra_cost += YAPF_TILE_LENGTH * (min_speed - max_veh_speed);
479  }
480  }
481 
482  /* Finish if we already exceeded the maximum path cost (i.e. when
483  * searching for the nearest depot). */
484  if (m_max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > m_max_cost) {
485  end_segment_reason |= ESRB_PATH_TOO_LONG;
486  }
487 
488  /* Move to the next tile/trackdir. */
489  tf = &tf_local;
490  tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
491 
492  if (!tf_local.Follow(cur.tile, cur.td)) {
493  assert(tf_local.m_err != TrackFollower::EC_NONE);
494  /* Can't move to the next tile (EOL?). */
495  if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) {
496  end_segment_reason |= ESRB_RAIL_TYPE;
497  } else {
498  end_segment_reason |= ESRB_DEAD_END;
499  }
500 
501  if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
502  end_segment_reason |= ESRB_SAFE_TILE;
503  }
504  break;
505  }
506 
507  /* Check if the next tile is not a choice. */
508  if (KillFirstBit(tf_local.m_new_td_bits) != TRACKDIR_BIT_NONE) {
509  /* More than one segment will follow. Close this one. */
510  end_segment_reason |= ESRB_CHOICE_FOLLOWS;
511  break;
512  }
513 
514  /* Gather the next tile/trackdir/tile_type/rail_type. */
515  TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits));
516 
517  if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, MP_RAILWAY)) {
518  if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) {
519  /* Possible safe tile. */
520  end_segment_reason |= ESRB_SAFE_TILE;
521  } else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) {
522  /* Possible safe tile, but not so good as it's the back of a signal... */
523  end_segment_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END;
524  extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
525  }
526  }
527 
528  /* Check the next tile for the rail type. */
529  if (next.rail_type != cur.rail_type) {
530  /* Segment must consist from the same rail_type tiles. */
531  end_segment_reason |= ESRB_RAIL_TYPE;
532  break;
533  }
534 
535  /* Avoid infinite looping. */
536  if (next.tile == n.m_key.m_tile && next.td == n.m_key.m_td) {
537  end_segment_reason |= ESRB_INFINITE_LOOP;
538  break;
539  }
540 
541  if (segment_cost > s_max_segment_cost) {
542  /* Potentially in the infinite loop (or only very long segment?). We should
543  * not force it to finish prematurely unless we are on a regular tile. */
544  if (IsTileType(tf->m_new_tile, MP_RAILWAY)) {
545  end_segment_reason |= ESRB_SEGMENT_TOO_LONG;
546  break;
547  }
548  }
549 
550  /* Any other reason bit set? */
551  if (end_segment_reason != ESRB_NONE) {
552  break;
553  }
554 
555  /* For the next loop set new prev and cur tile info. */
556  prev = cur;
557  cur = next;
558 
559  } // for (;;)
560 
561  bool target_seen = false;
562  if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) {
563  /* Depot, station or waypoint. */
564  if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
565  /* Destination found. */
566  target_seen = true;
567  }
568  }
569 
570  /* Update the segment if needed. */
571  if (!is_cached_segment) {
572  /* Write back the segment information so it can be reused the next time. */
573  segment.m_cost = segment_cost;
574  segment.m_end_segment_reason = end_segment_reason & ESRB_CACHED_MASK;
575  /* Save end of segment back to the node. */
576  n.SetLastTileTrackdir(cur.tile, cur.td);
577  }
578 
579  /* Do we have an excuse why not to continue pathfinding in this direction? */
580  if (!target_seen && (end_segment_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) {
581  /* Reason to not continue. Stop this PF branch. */
582  return false;
583  }
584 
585  /* Special costs for the case we have reached our target. */
586  if (target_seen) {
587  n.flags_u.flags_s.m_targed_seen = true;
588  /* Last-red and last-red-exit penalties. */
589  if (n.flags_u.flags_s.m_last_signal_was_red) {
590  if (n.m_last_red_signal_type == SIGTYPE_EXIT) {
591  /* last signal was red pre-signal-exit */
592  extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
593  } else if (!IsPbsSignal(n.m_last_red_signal_type)) {
594  /* Last signal was red, but not exit or path signal. */
595  extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
596  }
597  }
598 
599  /* Station platform-length penalty. */
600  if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) {
601  const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
602  assert(st != NULL);
603  uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
604  /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
605  extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length;
606  /* Add penalty for the inappropriate platform length. */
607  extra_cost += PlatformLengthPenalty(platform_length);
608  }
609  }
610 
611  /* total node cost */
612  n.m_cost = parent_cost + segment_entry_cost + segment_cost + extra_cost;
613 
614  return true;
615  }
616 
617  inline bool CanUseGlobalCache(Node& n) const
618  {
619  return !m_disable_cache
620  && (n.m_parent != NULL)
621  && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size());
622  }
623 
624  inline void ConnectNodeToCachedData(Node& n, CachedData& ci)
625  {
626  n.m_segment = &ci;
627  if (n.m_segment->m_cost < 0) {
628  n.m_segment->m_last_tile = n.m_key.m_tile;
629  n.m_segment->m_last_td = n.m_key.m_td;
630  }
631  }
632 
633  void DisableCache(bool disable)
634  {
635  m_disable_cache = disable;
636  }
637 };
638 
639 #endif /* YAPF_COSTRAIL_HPP */