OpenTTD
pbs.cpp
Go to the documentation of this file.
1 /* $Id: pbs.cpp 26482 2014-04-23 20:13:33Z 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 #include "stdafx.h"
13 #include "viewport_func.h"
14 #include "vehicle_func.h"
15 #include "newgrf_station.h"
17 
18 #include "safeguards.h"
19 
27 {
28  switch (GetTileType(t)) {
29  case MP_RAILWAY:
30  if (IsRailDepot(t)) return GetDepotReservationTrackBits(t);
31  if (IsPlainRail(t)) return GetRailReservationTrackBits(t);
32  break;
33 
34  case MP_ROAD:
36  break;
37 
38  case MP_STATION:
40  break;
41 
42  case MP_TUNNELBRIDGE:
44  break;
45 
46  default:
47  break;
48  }
49  return TRACK_BIT_NONE;
50 }
51 
60 {
61  TileIndex tile = start;
62  TileIndexDiff diff = TileOffsByDiagDir(dir);
63 
64  assert(IsRailStationTile(start));
65  assert(GetRailStationAxis(start) == DiagDirToAxis(dir));
66 
67  do {
69  MarkTileDirtyByTile(tile);
70  tile = TILE_ADD(tile, diff);
71  } while (IsCompatibleTrainStationTile(tile, start));
72 }
73 
82 bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
83 {
84  assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
85 
87  /* show the reserved rail if needed */
88  MarkTileDirtyByTile(tile);
89  }
90 
91  switch (GetTileType(tile)) {
92  case MP_RAILWAY:
93  if (IsPlainRail(tile)) return TryReserveTrack(tile, t);
94  if (IsRailDepot(tile)) {
95  if (!HasDepotReservation(tile)) {
96  SetDepotReservation(tile, true);
97  MarkTileDirtyByTile(tile); // some GRFs change their appearance when tile is reserved
98  return true;
99  }
100  }
101  break;
102 
103  case MP_ROAD:
104  if (IsLevelCrossing(tile) && !HasCrossingReservation(tile)) {
105  SetCrossingReservation(tile, true);
106  BarCrossing(tile);
107  MarkTileDirtyByTile(tile); // crossing barred, make tile dirty
108  return true;
109  }
110  break;
111 
112  case MP_STATION:
113  if (HasStationRail(tile) && !HasStationReservation(tile)) {
114  SetRailStationReservation(tile, true);
115  if (trigger_stations && IsRailStation(tile)) TriggerStationRandomisation(NULL, tile, SRT_PATH_RESERVATION);
116  MarkTileDirtyByTile(tile); // some GRFs need redraw after reserving track
117  return true;
118  }
119  break;
120 
121  case MP_TUNNELBRIDGE:
123  SetTunnelBridgeReservation(tile, true);
124  return true;
125  }
126  break;
127 
128  default:
129  break;
130  }
131  return false;
132 }
133 
140 {
141  assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
142 
144  MarkTileDirtyByTile(tile);
145  }
146 
147  switch (GetTileType(tile)) {
148  case MP_RAILWAY:
149  if (IsRailDepot(tile)) {
150  SetDepotReservation(tile, false);
151  MarkTileDirtyByTile(tile);
152  break;
153  }
154  if (IsPlainRail(tile)) UnreserveTrack(tile, t);
155  break;
156 
157  case MP_ROAD:
158  if (IsLevelCrossing(tile)) {
159  SetCrossingReservation(tile, false);
160  UpdateLevelCrossing(tile);
161  }
162  break;
163 
164  case MP_STATION:
165  if (HasStationRail(tile)) {
166  SetRailStationReservation(tile, false);
167  MarkTileDirtyByTile(tile);
168  }
169  break;
170 
171  case MP_TUNNELBRIDGE:
173  break;
174 
175  default:
176  break;
177  }
178 }
179 
180 
182 static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Trackdir trackdir, bool ignore_oneway = false)
183 {
184  TileIndex start_tile = tile;
185  Trackdir start_trackdir = trackdir;
186  bool first_loop = true;
187 
188  /* Start track not reserved? This can happen if two trains
189  * are on the same tile. The reservation on the next tile
190  * is not ours in this case, so exit. */
191  if (!HasReservedTracks(tile, TrackToTrackBits(TrackdirToTrack(trackdir)))) return PBSTileInfo(tile, trackdir, false);
192 
193  /* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */
194  CFollowTrackRail ft(o, rts);
195  while (ft.Follow(tile, trackdir)) {
197 
198  /* No reservation --> path end found */
199  if (reserved == TRACKDIR_BIT_NONE) {
200  if (ft.m_is_station) {
201  /* Check skipped station tiles as well, maybe our reservation ends inside the station. */
203  while (ft.m_tiles_skipped-- > 0) {
204  ft.m_new_tile -= diff;
206  tile = ft.m_new_tile;
207  trackdir = DiagDirToDiagTrackdir(ft.m_exitdir);
208  break;
209  }
210  }
211  }
212  break;
213  }
214 
215  /* Can't have more than one reserved trackdir */
216  Trackdir new_trackdir = FindFirstTrackdir(reserved);
217 
218  /* One-way signal against us. The reservation can't be ours as it is not
219  * a safe position from our direction and we can never pass the signal. */
220  if (!ignore_oneway && HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break;
221 
222  tile = ft.m_new_tile;
223  trackdir = new_trackdir;
224 
225  if (first_loop) {
226  /* Update the start tile after we followed the track the first
227  * time. This is necessary because the track follower can skip
228  * tiles (in stations for example) which means that we might
229  * never visit our original starting tile again. */
230  start_tile = tile;
231  start_trackdir = trackdir;
232  first_loop = false;
233  } else {
234  /* Loop encountered? */
235  if (tile == start_tile && trackdir == start_trackdir) break;
236  }
237  /* Depot tile? Can't continue. */
238  if (IsRailDepotTile(tile)) break;
239  /* Non-pbs signal? Reservation can't continue. */
240  if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break;
241  }
242 
243  return PBSTileInfo(tile, trackdir, false);
244 }
245 
252 
255 };
256 
258 static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data)
259 {
261 
262  if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return NULL;
263 
264  Train *t = Train::From(v);
265  if (t->track == TRACK_BIT_WORMHOLE || HasBit((TrackBits)t->track, TrackdirToTrack(info->res.trackdir))) {
266  t = t->First();
267 
268  /* ALWAYS return the lowest ID (anti-desync!) */
269  if (info->best == NULL || t->index < info->best->index) info->best = t;
270  return t;
271  }
272 
273  return NULL;
274 }
275 
284 {
285  assert(v->type == VEH_TRAIN);
286 
287  TileIndex tile = v->tile;
288  Trackdir trackdir = v->GetVehicleTrackdir();
289 
290  if (IsRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return PBSTileInfo(tile, trackdir, false);
291 
292  FindTrainOnTrackInfo ftoti;
293  ftoti.res = FollowReservation(v->owner, GetRailTypeInfo(v->railtype)->compatible_railtypes, tile, trackdir);
295  if (train_on_res != NULL) {
297  if (ftoti.best != NULL) *train_on_res = ftoti.best->First();
298  if (*train_on_res == NULL && IsRailStationTile(ftoti.res.tile)) {
299  /* The target tile is a rail station. The track follower
300  * has stopped on the last platform tile where we haven't
301  * found a train. Also check all previous platform tiles
302  * for a possible train. */
304  for (TileIndex st_tile = ftoti.res.tile + diff; *train_on_res == NULL && IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
305  FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
306  if (ftoti.best != NULL) *train_on_res = ftoti.best->First();
307  }
308  }
309  if (*train_on_res == NULL && IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
310  /* The target tile is a bridge/tunnel, also check the other end tile. */
312  if (ftoti.best != NULL) *train_on_res = ftoti.best->First();
313  }
314  }
315  return ftoti.res;
316 }
317 
326 {
327  assert(HasReservedTracks(tile, TrackToTrackBits(track)));
328  Trackdir trackdir = TrackToTrackdir(track);
329 
331 
332  /* Follow the path from tile to both ends, one of the end tiles should
333  * have a train on it. We need FollowReservation to ignore one-way signals
334  * here, as one of the two search directions will be the "wrong" way. */
335  for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) {
336  /* If the tile has a one-way block signal in the current trackdir, skip the
337  * search in this direction as the reservation can't come from this side.*/
338  if (HasOnewaySignalBlockingTrackdir(tile, ReverseTrackdir(trackdir)) && !HasPbsSignalOnTrackdir(tile, trackdir)) continue;
339 
340  FindTrainOnTrackInfo ftoti;
341  ftoti.res = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true);
342 
344  if (ftoti.best != NULL) return ftoti.best;
345 
346  /* Special case for stations: check the whole platform for a vehicle. */
347  if (IsRailStationTile(ftoti.res.tile)) {
349  for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
350  FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
351  if (ftoti.best != NULL) return ftoti.best;
352  }
353  }
354 
355  /* Special case for bridges/tunnels: check the other end as well. */
356  if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
358  if (ftoti.best != NULL) return ftoti.best;
359  }
360  }
361 
362  return NULL;
363 }
364 
375 bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
376 {
377  if (IsRailDepotTile(tile)) return true;
378 
379  if (IsTileType(tile, MP_RAILWAY)) {
380  /* For non-pbs signals, stop on the signal tile. */
381  if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
382  }
383 
384  /* Check next tile. For performance reasons, we check for 90 degree turns ourself. */
386 
387  /* End of track? */
388  if (!ft.Follow(tile, trackdir)) {
389  /* Last tile of a terminus station is a safe position. */
390  if (include_line_end) return true;
391  }
392 
393  /* Check for reachable tracks. */
395  if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
396  if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) return include_line_end;
397 
400  /* PBS signal on next trackdir? Safe position. */
401  if (HasPbsSignalOnTrackdir(ft.m_new_tile, td)) return true;
402  /* One-way PBS signal against us? Safe if end-of-line is allowed. */
404  GetSignalType(ft.m_new_tile, TrackdirToTrack(td)) == SIGTYPE_PBS_ONEWAY) {
405  return include_line_end;
406  }
407  }
408 
409  return false;
410 }
411 
421 bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
422 {
423  Track track = TrackdirToTrack(trackdir);
424  TrackBits reserved = GetReservedTrackbits(tile);
425 
426  /* Tile reserved? Can never be a free waiting position. */
427  if (TrackOverlapsTracks(reserved, track)) return false;
428 
429  /* Not reserved and depot or not a pbs signal -> free. */
430  if (IsRailDepotTile(tile)) return true;
431  if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true;
432 
433  /* Check the next tile, if it's a PBS signal, it has to be free as well. */
435 
436  if (!ft.Follow(tile, trackdir)) return true;
437 
438  /* Check for reachable tracks. */
440  if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
441 
443 }