~ubuntu-branches/debian/squeeze/openttd/squeeze

« back to all changes in this revision

Viewing changes to src/pbs.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jordi Mallach, Matthijs Kooijman, Jordi Mallach
  • Date: 2009-04-15 18:22:10 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090415182210-22ktb8kdbp2tf3bm
[ Matthijs Kooijman ]
* New upstream release.
* Remove Debian specific desktop file, upstream provides one now. 
* Add debian/watch file.

[ Jordi Mallach ]
* Bump Standards-Version to 3.8.1, with no changes required.
* Move to debhelper compat 7. Bump Build-Depends accordingly.
* Use dh_prep.
* Add "set -e" to config script.
* Remove a few extra doc files that get installed by upstream Makefile.
* Add more complete copyright information.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: pbs.cpp 15299 2009-01-31 20:16:06Z smatz $ */
 
2
 
 
3
/** @file pbs.cpp */
 
4
#include "stdafx.h"
 
5
#include "pbs.h"
 
6
#include "functions.h"
 
7
#include "vehicle_func.h"
 
8
#include "yapf/follow_track.hpp"
 
9
 
 
10
/**
 
11
 * Get the reserved trackbits for any tile, regardless of type.
 
12
 * @param t the tile
 
13
 * @return the reserved trackbits. TRACK_BIT_NONE on nothing reserved or
 
14
 *     a tile without rail.
 
15
 */
 
16
TrackBits GetReservedTrackbits(TileIndex t)
 
17
{
 
18
        switch (GetTileType(t)) {
 
19
                case MP_RAILWAY:
 
20
                        if (IsRailWaypoint(t) || IsRailDepot(t)) return GetRailWaypointReservation(t);
 
21
                        if (IsPlainRailTile(t)) return GetTrackReservation(t);
 
22
                        break;
 
23
 
 
24
                case MP_ROAD:
 
25
                        if (IsLevelCrossing(t)) return GetRailCrossingReservation(t);
 
26
                        break;
 
27
 
 
28
                case MP_STATION:
 
29
                        if (IsRailwayStation(t)) return GetRailStationReservation(t);
 
30
                        break;
 
31
 
 
32
                case MP_TUNNELBRIDGE:
 
33
                        if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) return GetRailTunnelBridgeReservation(t);
 
34
                        break;
 
35
 
 
36
                default:
 
37
                        break;
 
38
        }
 
39
        return TRACK_BIT_NONE;
 
40
}
 
41
 
 
42
/**
 
43
 * Set the reservation for a complete station platform.
 
44
 * @pre IsRailwayStationTile(start)
 
45
 * @param start starting tile of the platform
 
46
 * @param dir the direction in which to follow the platform
 
47
 * @param b the state the reservation should be set to
 
48
 */
 
49
void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
 
50
{
 
51
        TileIndex     tile = start;
 
52
        TileIndexDiff diff = TileOffsByDiagDir(dir);
 
53
 
 
54
        assert(IsRailwayStationTile(start));
 
55
        assert(GetRailStationAxis(start) == DiagDirToAxis(dir));
 
56
 
 
57
        do {
 
58
                SetRailwayStationReservation(tile, b);
 
59
                MarkTileDirtyByTile(tile);
 
60
                tile = TILE_ADD(tile, diff);
 
61
        } while (IsCompatibleTrainStationTile(tile, start));
 
62
}
 
63
 
 
64
/**
 
65
 * Try to reserve a specific track on a tile
 
66
 * @param tile the tile
 
67
 * @param t the track
 
68
 * @return true if reservation was successfull, i.e. the track was
 
69
 *     free and didn't cross any other reserved tracks.
 
70
 */
 
71
bool TryReserveRailTrack(TileIndex tile, Track t)
 
72
{
 
73
        assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
 
74
 
 
75
        if (_settings_client.gui.show_track_reservation) {
 
76
                /* show the reserved rail if needed */
 
77
                MarkTileDirtyByTile(tile);
 
78
        }
 
79
 
 
80
        switch (GetTileType(tile)) {
 
81
                case MP_RAILWAY:
 
82
                        if (IsPlainRailTile(tile)) return TryReserveTrack(tile, t);
 
83
                        if (IsRailWaypoint(tile) || IsRailDepot(tile)) {
 
84
                                if (!GetDepotWaypointReservation(tile)) {
 
85
                                        SetDepotWaypointReservation(tile, true);
 
86
                                        MarkTileDirtyByTile(tile); // some GRFs change their appearance when tile is reserved
 
87
                                        return true;
 
88
                                }
 
89
                        }
 
90
                        break;
 
91
 
 
92
                case MP_ROAD:
 
93
                        if (IsLevelCrossing(tile) && !GetCrossingReservation(tile)) {
 
94
                                SetCrossingReservation(tile, true);
 
95
                                BarCrossing(tile);
 
96
                                MarkTileDirtyByTile(tile); // crossing barred, make tile dirty
 
97
                                return true;
 
98
                        }
 
99
                        break;
 
100
 
 
101
                case MP_STATION:
 
102
                        if (IsRailwayStation(tile) && !GetRailwayStationReservation(tile)) {
 
103
                                SetRailwayStationReservation(tile, true);
 
104
                                MarkTileDirtyByTile(tile); // some GRFs need redraw after reserving track
 
105
                                return true;
 
106
                        }
 
107
                        break;
 
108
 
 
109
                case MP_TUNNELBRIDGE:
 
110
                        if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetRailTunnelBridgeReservation(tile)) {
 
111
                                SetTunnelBridgeReservation(tile, true);
 
112
                                return true;
 
113
                        }
 
114
                        break;
 
115
 
 
116
                default:
 
117
                        break;
 
118
        }
 
119
        return false;
 
120
}
 
121
 
 
122
/**
 
123
 * Lift the reservation of a specific track on a tile
 
124
 * @param tile the tile
 
125
 * @param t the track
 
126
 */
 
127
 void UnreserveRailTrack(TileIndex tile, Track t)
 
128
{
 
129
        assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
 
130
 
 
131
        if (_settings_client.gui.show_track_reservation) {
 
132
                MarkTileDirtyByTile(tile);
 
133
        }
 
134
 
 
135
        switch (GetTileType(tile)) {
 
136
                case MP_RAILWAY:
 
137
                        if (IsRailWaypoint(tile) || IsRailDepot(tile)) {
 
138
                                SetDepotWaypointReservation(tile, false);
 
139
                                MarkTileDirtyByTile(tile);
 
140
                                break;
 
141
                        }
 
142
                        if (IsPlainRailTile(tile)) UnreserveTrack(tile, t);
 
143
                        break;
 
144
 
 
145
                case MP_ROAD:
 
146
                        if (IsLevelCrossing(tile)) {
 
147
                                SetCrossingReservation(tile, false);
 
148
                                UpdateLevelCrossing(tile);
 
149
                        }
 
150
                        break;
 
151
 
 
152
                case MP_STATION:
 
153
                        if (IsRailwayStation(tile)) {
 
154
                                SetRailwayStationReservation(tile, false);
 
155
                                MarkTileDirtyByTile(tile);
 
156
                        }
 
157
                        break;
 
158
 
 
159
                case MP_TUNNELBRIDGE:
 
160
                        if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) SetTunnelBridgeReservation(tile, false);
 
161
                        break;
 
162
 
 
163
                default:
 
164
                        break;
 
165
        }
 
166
}
 
167
 
 
168
 
 
169
/** Follow a reservation starting from a specific tile to the end. */
 
170
static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Trackdir trackdir, bool ignore_oneway = false)
 
171
{
 
172
        TileIndex start_tile = tile;
 
173
        Trackdir  start_trackdir = trackdir;
 
174
        bool      first_loop = true;
 
175
 
 
176
        /* Start track not reserved? This can happen if two trains
 
177
         * are on the same tile. The reservation on the next tile
 
178
         * is not ours in this case, so exit. */
 
179
        if (!HasReservedTracks(tile, TrackToTrackBits(TrackdirToTrack(trackdir)))) return PBSTileInfo(tile, trackdir, false);
 
180
 
 
181
        /* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */
 
182
        CFollowTrackRail ft(o, rts);
 
183
        while (ft.Follow(tile, trackdir)) {
 
184
                TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile));
 
185
 
 
186
                /* No reservation --> path end found */
 
187
                if (reserved == TRACKDIR_BIT_NONE) break;
 
188
 
 
189
                /* Can't have more than one reserved trackdir */
 
190
                Trackdir new_trackdir = FindFirstTrackdir(reserved);
 
191
 
 
192
                /* One-way signal against us. The reservation can't be ours as it is not
 
193
                 * a safe position from our direction and we can never pass the signal. */
 
194
                if (!ignore_oneway && HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break;
 
195
 
 
196
                tile = ft.m_new_tile;
 
197
                trackdir = new_trackdir;
 
198
 
 
199
                if (first_loop) {
 
200
                        /* Update the start tile after we followed the track the first
 
201
                         * time. This is neccessary because the track follower can skip
 
202
                         * tiles (in stations for example) which means that we might
 
203
                         * never visit our original starting tile again. */
 
204
                        start_tile = tile;
 
205
                        start_trackdir = trackdir;
 
206
                        first_loop = false;
 
207
                } else {
 
208
                        /* Loop encountered? */
 
209
                        if (tile == start_tile && trackdir == start_trackdir) break;
 
210
                }
 
211
                /* Depot tile? Can't continue. */
 
212
                if (IsRailDepotTile(tile)) break;
 
213
                /* Non-pbs signal? Reservation can't continue. */
 
214
                if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break;
 
215
        }
 
216
 
 
217
        return PBSTileInfo(tile, trackdir, false);
 
218
}
 
219
 
 
220
/**
 
221
 * Helper struct for finding the best matching vehicle on a specific track.
 
222
 */
 
223
struct FindTrainOnTrackInfo {
 
224
        PBSTileInfo res; ///< Information about the track.
 
225
        Vehicle *best;   ///< The currently "best" vehicle we have found.
 
226
 
 
227
        /** Init the best location to NULL always! */
 
228
        FindTrainOnTrackInfo() : best(NULL) {}
 
229
};
 
230
 
 
231
/** Callback for Has/FindVehicleOnPos to find a train on a specific track. */
 
232
static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data)
 
233
{
 
234
        FindTrainOnTrackInfo *info = (FindTrainOnTrackInfo *)data;
 
235
 
 
236
        if (v->type == VEH_TRAIN && !(v->vehstatus & VS_CRASHED) && HasBit((TrackBits)v->u.rail.track, TrackdirToTrack(info->res.trackdir))) {
 
237
                v = v->First();
 
238
 
 
239
                /* ALWAYS return the lowest ID (anti-desync!) */
 
240
                if (info->best == NULL || v->index < info->best->index) info->best = v;
 
241
                return v;
 
242
        }
 
243
 
 
244
        return NULL;
 
245
}
 
246
 
 
247
/**
 
248
 * Follow a train reservation to the last tile.
 
249
 *
 
250
 * @param v the vehicle
 
251
 * @param train_on_res Is set to a train we might encounter
 
252
 * @returns The last tile of the reservation or the current train tile if no reservation present.
 
253
 */
 
254
PBSTileInfo FollowTrainReservation(const Vehicle *v, bool *train_on_res)
 
255
{
 
256
        assert(v->type == VEH_TRAIN);
 
257
 
 
258
        TileIndex tile = v->tile;
 
259
        Trackdir  trackdir = GetVehicleTrackdir(v);
 
260
 
 
261
        if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false);
 
262
 
 
263
        FindTrainOnTrackInfo ftoti;
 
264
        ftoti.res = FollowReservation(v->owner, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes, tile, trackdir);
 
265
        ftoti.res.okay = IsSafeWaitingPosition(v, ftoti.res.tile, ftoti.res.trackdir, true, _settings_game.pf.forbid_90_deg);
 
266
        if (train_on_res != NULL) *train_on_res = HasVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
 
267
        return ftoti.res;
 
268
}
 
269
 
 
270
/**
 
271
 * Find the train which has reserved a specific path.
 
272
 *
 
273
 * @param tile A tile on the path.
 
274
 * @param track A reserved track on the tile.
 
275
 * @return The vehicle holding the reservation or NULL if the path is stray.
 
276
 */
 
277
Vehicle *GetTrainForReservation(TileIndex tile, Track track)
 
278
{
 
279
        assert(HasReservedTracks(tile, TrackToTrackBits(track)));
 
280
        Trackdir  trackdir = TrackToTrackdir(track);
 
281
 
 
282
        RailTypes rts = GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes;
 
283
 
 
284
        /* Follow the path from tile to both ends, one of the end tiles should
 
285
         * have a train on it. We need FollowReservation to ignore one-way signals
 
286
         * here, as one of the two search directions will be the "wrong" way. */
 
287
        for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) {
 
288
                FindTrainOnTrackInfo ftoti;
 
289
                ftoti.res = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true);
 
290
 
 
291
                FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
 
292
                if (ftoti.best != NULL) return ftoti.best;
 
293
 
 
294
                /* Special case for stations: check the whole platform for a vehicle. */
 
295
                if (IsRailwayStationTile(ftoti.res.tile)) {
 
296
                        TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir)));
 
297
                        for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
 
298
                                FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
 
299
                                if (ftoti.best != NULL) return ftoti.best;
 
300
                        }
 
301
                }
 
302
 
 
303
                /* Special case for bridges/tunnels: check the other end as well. */
 
304
                if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
 
305
                        FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
 
306
                        if (ftoti.best != NULL) return ftoti.best;
 
307
                }
 
308
        }
 
309
 
 
310
        return NULL;
 
311
}
 
312
 
 
313
/**
 
314
 * Determine whether a certain track on a tile is a safe position to end a path.
 
315
 *
 
316
 * @param v the vehicle to test for
 
317
 * @param tile The tile
 
318
 * @param trackdir The trackdir to test
 
319
 * @param include_line_end Should end-of-line tiles be considered safe?
 
320
 * @param forbid_90def Don't allow trains to make 90 degree turns
 
321
 * @return True if it is a safe position
 
322
 */
 
323
bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
 
324
{
 
325
        if (IsRailDepotTile(tile)) return true;
 
326
 
 
327
        if (IsTileType(tile, MP_RAILWAY)) {
 
328
                /* For non-pbs signals, stop on the signal tile. */
 
329
                if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
 
330
        }
 
331
 
 
332
        /* Check next tile. For perfomance reasons, we check for 90 degree turns ourself. */
 
333
        CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes);
 
334
 
 
335
        /* End of track? */
 
336
        if (!ft.Follow(tile, trackdir)) {
 
337
                /* Last tile of a terminus station is a safe position. */
 
338
                if (include_line_end) return true;
 
339
        }
 
340
 
 
341
        /* Check for reachable tracks. */
 
342
        ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir);
 
343
        if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
 
344
        if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) return include_line_end;
 
345
 
 
346
        if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) {
 
347
                /* PBS signal on next trackdir? Safe position. */
 
348
                if (HasPbsSignalOnTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) return true;
 
349
        }
 
350
 
 
351
        return false;
 
352
}
 
353
 
 
354
/**
 
355
 * Check if a safe position is free.
 
356
 *
 
357
 * @param v the vehicle to test for
 
358
 * @param tile The tile
 
359
 * @param trackdir The trackdir to test
 
360
 * @param forbid_90def Don't allow trains to make 90 degree turns
 
361
 * @return True if the position is free
 
362
 */
 
363
bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
 
364
{
 
365
        Track     track = TrackdirToTrack(trackdir);
 
366
        TrackBits reserved = GetReservedTrackbits(tile);
 
367
 
 
368
        /* Tile reserved? Can never be a free waiting position. */
 
369
        if (TrackOverlapsTracks(reserved, track)) return false;
 
370
 
 
371
        /* Not reserved and depot or not a pbs signal -> free. */
 
372
        if (IsRailDepotTile(tile)) return true;
 
373
        if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true;
 
374
 
 
375
        /* Check the next tile, if it's a PBS signal, it has to be free as well. */
 
376
        CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes);
 
377
 
 
378
        if (!ft.Follow(tile, trackdir)) return true;
 
379
 
 
380
        /* Check for reachable tracks. */
 
381
        ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir);
 
382
        if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
 
383
 
 
384
        return !HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits));
 
385
}