00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "tile_cmd.h"
00009 #include "rail_map.h"
00010 #include "road_map.h"
00011 #include "station_map.h"
00012 #include "tunnelbridge_map.h"
00013 #include "vehicle_func.h"
00014 #include "train.h"
00015 #include "newgrf_station.h"
00016 #include "functions.h"
00017 #include "track_type.h"
00018 #include "track_func.h"
00019 #include "signal_func.h"
00020 #include "player_func.h"
00021
00022
00024 enum {
00025 SIG_TBU_SIZE = 64,
00026 SIG_TBD_SIZE = 256,
00027 SIG_GLOB_SIZE = 128,
00028 SIG_GLOB_UPDATE = 64,
00029 };
00030
00031
00032 assert_compile((int)SIG_GLOB_UPDATE <= (int)SIG_GLOB_SIZE);
00033
00035 static const TrackBitsByte _enterdir_to_trackbits[DIAGDIR_END] = {
00036 {TRACK_BIT_3WAY_NE},
00037 {TRACK_BIT_3WAY_SE},
00038 {TRACK_BIT_3WAY_SW},
00039 {TRACK_BIT_3WAY_NW}
00040 };
00041
00043 static const TrackdirBitsShort _enterdir_to_trackdirbits[DIAGDIR_END] = {
00044 {TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S},
00045 {TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N},
00046 {TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N},
00047 {TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S}
00048 };
00049
00055 template <typename Tdir, uint items>
00056 struct SmallSet {
00057 private:
00058 uint n;
00059 bool overflowed;
00060 const char *name;
00061
00063 struct SSdata {
00064 TileIndex tile;
00065 Tdir dir;
00066 } data[items];
00067
00068 public:
00070 SmallSet(const char *name) : n(0), overflowed(false), name(name) { }
00071
00073 void Reset()
00074 {
00075 this->n = 0;
00076 this->overflowed = false;
00077 }
00078
00083 bool Overflowed()
00084 {
00085 return this->overflowed;
00086 }
00087
00092 bool IsEmpty()
00093 {
00094 return this->n == 0;
00095 }
00096
00101 bool IsFull()
00102 {
00103 return this->n == lengthof(data);
00104 }
00105
00110 uint Items()
00111 {
00112 return this->n;
00113 }
00114
00115
00122 bool Remove(TileIndex tile, Tdir dir)
00123 {
00124 for (uint i = 0; i < this->n; i++) {
00125 if (this->data[i].tile == tile && this->data[i].dir == dir) {
00126 this->data[i] = this->data[--this->n];
00127 return true;
00128 }
00129 }
00130
00131 return false;
00132 }
00133
00140 bool IsIn(TileIndex tile, Tdir dir)
00141 {
00142 for (uint i = 0; i < this->n; i++) {
00143 if (this->data[i].tile == tile && this->data[i].dir == dir) return true;
00144 }
00145
00146 return false;
00147 }
00148
00156 bool Add(TileIndex tile, Tdir dir)
00157 {
00158 if (this->IsFull()) {
00159 overflowed = true;
00160 DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items);
00161 return false;
00162 }
00163
00164 this->data[this->n].tile = tile;
00165 this->data[this->n].dir = dir;
00166 this->n++;
00167
00168 return true;
00169 }
00170
00177 bool Get(TileIndex *tile, Tdir *dir)
00178 {
00179 if (this->n == 0) return false;
00180
00181 this->n--;
00182 *tile = this->data[this->n].tile;
00183 *dir = this->data[this->n].dir;
00184
00185 return true;
00186 }
00187 };
00188
00189 static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset");
00190 static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset");
00191 static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset");
00192
00193
00195 static void *TrainOnTileEnum(Vehicle *v, void *)
00196 {
00197 if (v->type != VEH_TRAIN || v->u.rail.track == TRACK_BIT_DEPOT) return NULL;
00198
00199 return v;
00200 }
00201
00202
00216 static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00217 {
00218 _globset.Remove(t1, d1);
00219 _globset.Remove(t2, d2);
00220
00221 assert(!_tbdset.IsIn(t1, d1));
00222
00223 if (_tbdset.Remove(t2, d2)) return false;
00224
00225 return true;
00226 }
00227
00228
00242 static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00243 {
00244 if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true;
00245
00246 return _tbdset.Add(t1, d1);
00247 }
00248
00249
00251 enum SigFlags {
00252 SF_NONE = 0,
00253 SF_TRAIN = 1 << 0,
00254 SF_EXIT = 1 << 1,
00255 SF_EXIT2 = 1 << 2,
00256 SF_GREEN = 1 << 3,
00257 SF_GREEN2 = 1 << 4,
00258 SF_FULL = 1 << 5,
00259 };
00260
00261 DECLARE_ENUM_AS_BIT_SET(SigFlags)
00262
00263
00264
00270 static SigFlags ExploreSegment(Owner owner)
00271 {
00272 SigFlags flags = SF_NONE;
00273
00274 TileIndex tile;
00275 DiagDirection enterdir;
00276
00277 while (_tbdset.Get(&tile, &enterdir)) {
00278 TileIndex oldtile = tile;
00279 DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir);
00280
00281 switch (GetTileType(tile)) {
00282 case MP_RAILWAY: {
00283 if (GetTileOwner(tile) != owner) continue;
00284
00285 if (IsRailDepot(tile)) {
00286 if (enterdir == INVALID_DIAGDIR) {
00287 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00288 exitdir = GetRailDepotDirection(tile);
00289 tile += TileOffsByDiagDir(exitdir);
00290 enterdir = ReverseDiagDir(exitdir);
00291 break;
00292 } else if (enterdir == GetRailDepotDirection(tile)) {
00293 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00294 continue;
00295 } else {
00296 continue;
00297 }
00298 }
00299
00300 if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) {
00301 if (GetWaypointAxis(tile) != DiagDirToAxis(enterdir)) continue;
00302 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00303 tile += TileOffsByDiagDir(exitdir);
00304
00305 break;
00306 }
00307
00308 TrackBits tracks = GetTrackBits(tile);
00309 TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]);
00310
00311 if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) {
00312 tracks = tracks_masked;
00313 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tracks, &EnsureNoTrainOnTrackProc)) flags |= SF_TRAIN;
00314 } else {
00315 if (tracks_masked == TRACK_BIT_NONE) continue;
00316 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00317 }
00318
00319 if (HasSignals(tile)) {
00320 Track track = TrackBitsToTrack(tracks_masked);
00321 if (HasSignalOnTrack(tile, track)) {
00322 SignalType sig = GetSignalType(tile, track);
00323 Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
00324 Trackdir reversedir = ReverseTrackdir(trackdir);
00325
00326
00327
00328 if (HasSignalOnTrackdir(tile, reversedir)) {
00329 if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL;
00330 }
00331
00332 if (!(flags & SF_GREEN2) && (sig & SIGTYPE_EXIT) && HasSignalOnTrackdir(tile, trackdir)) {
00333 if (flags & SF_EXIT) flags |= SF_EXIT2;
00334 flags |= SF_EXIT;
00335 if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) {
00336 if (flags & SF_GREEN) flags |= SF_GREEN2;
00337 flags |= SF_GREEN;
00338 }
00339 }
00340 continue;
00341 }
00342 }
00343
00344 for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
00345 if (dir != enterdir && tracks & _enterdir_to_trackbits[dir]) {
00346 TileIndex newtile = tile + TileOffsByDiagDir(dir);
00347 DiagDirection newdir = ReverseDiagDir(dir);
00348 if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL;
00349 }
00350 }
00351
00352 continue;
00353 }
00354
00355 case MP_STATION:
00356 if (!IsRailwayStation(tile)) continue;
00357 if (GetTileOwner(tile) != owner) continue;
00358 if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue;
00359 if (IsStationTileBlocked(tile)) continue;
00360
00361 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00362 tile += TileOffsByDiagDir(exitdir);
00363 break;
00364
00365 case MP_ROAD:
00366 if (!IsLevelCrossing(tile)) continue;
00367 if (GetTileOwner(tile) != owner) continue;
00368 if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue;
00369
00370 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00371 tile += TileOffsByDiagDir(exitdir);
00372 break;
00373
00374 case MP_TUNNELBRIDGE: {
00375 if (GetTileOwner(tile) != owner) continue;
00376 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
00377 DiagDirection dir = GetTunnelBridgeDirection(tile);
00378
00379 if (enterdir == INVALID_DIAGDIR) {
00380 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00381 enterdir = dir;
00382 exitdir = ReverseDiagDir(dir);
00383 tile += TileOffsByDiagDir(exitdir);
00384 } else {
00385 if (ReverseDiagDir(enterdir) != dir) continue;
00386 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00387 tile = GetOtherTunnelBridgeEnd(tile);
00388 enterdir = INVALID_DIAGDIR;
00389 exitdir = INVALID_DIAGDIR;
00390 }
00391 }
00392 break;
00393
00394 default:
00395 continue;
00396 }
00397
00398 if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL;
00399 }
00400
00401 return flags;
00402 }
00403
00404
00410 static void UpdateSignalsAroundSegment(SigFlags flags)
00411 {
00412 TileIndex tile;
00413 Trackdir trackdir;
00414
00415 while (_tbuset.Get(&tile, &trackdir)) {
00416 assert(HasSignalOnTrackdir(tile, trackdir));
00417
00418 SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir));
00419 SignalState newstate = SIGNAL_STATE_GREEN;
00420
00421
00422 if (flags & SF_TRAIN) {
00423
00424 newstate = SIGNAL_STATE_RED;
00425 } else {
00426
00427 if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
00428
00429 if (flags & SF_EXIT2 &&
00430
00431 (!(flags & SF_GREEN) ||
00432
00433 (!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) {
00434 newstate = SIGNAL_STATE_RED;
00435 }
00436 } else {
00437 if (sig & SIGTYPE_ENTRY && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED;
00438 }
00439 }
00440
00441
00442 if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
00443 if (sig & SIGTYPE_EXIT) {
00444
00445 DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
00446 _globset.Add(tile, exitdir);
00447 }
00448 SetSignalStateByTrackdir(tile, trackdir, newstate);
00449 MarkTileDirtyByTile(tile);
00450 }
00451 }
00452
00453 }
00454
00455
00457 static inline void ResetSets()
00458 {
00459 _tbuset.Reset();
00460 _tbdset.Reset();
00461 _globset.Reset();
00462 }
00463
00464
00472 static bool UpdateSignalsInBuffer(Owner owner)
00473 {
00474 assert(IsValidPlayer(owner));
00475
00476 bool first = true;
00477 bool state = false;
00478
00479 TileIndex tile;
00480 DiagDirection dir;
00481
00482 while (_globset.Get(&tile, &dir)) {
00483 assert(_tbuset.IsEmpty());
00484 assert(_tbdset.IsEmpty());
00485
00486
00487
00488
00489
00490
00491 switch (GetTileType(tile)) {
00492 case MP_TUNNELBRIDGE:
00493
00494 assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
00495 assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
00496 _tbdset.Add(tile, INVALID_DIAGDIR);
00497 _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
00498 break;
00499
00500 case MP_RAILWAY:
00501 if (IsRailDepot(tile)) {
00502
00503 assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
00504 _tbdset.Add(tile, INVALID_DIAGDIR);
00505 break;
00506 }
00507
00508 case MP_STATION:
00509 case MP_ROAD:
00510 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00511
00512 _tbdset.Add(tile, dir);
00513 _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
00514 break;
00515 }
00516
00517 default:
00518
00519 tile = tile + TileOffsByDiagDir(dir);
00520 dir = ReverseDiagDir(dir);
00521 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00522 _tbdset.Add(tile, dir);
00523 break;
00524 }
00525
00526 continue;
00527 }
00528
00529 assert(!_tbdset.Overflowed());
00530 assert(!_tbdset.IsEmpty());
00531
00532 SigFlags flags = ExploreSegment(owner);
00533
00534 if (first) {
00535 first = false;
00536 state = (flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL);
00537 }
00538
00539
00540 if (flags & SF_FULL) {
00541 ResetSets();
00542 break;
00543 }
00544
00545 UpdateSignalsAroundSegment(flags);
00546 }
00547
00548 return state;
00549 }
00550
00551
00552 static Owner _last_owner = INVALID_OWNER;
00553
00554
00559 void UpdateSignalsInBuffer()
00560 {
00561 if (!_globset.IsEmpty()) {
00562 UpdateSignalsInBuffer(_last_owner);
00563 _last_owner = INVALID_OWNER;
00564 }
00565 }
00566
00567
00575 void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
00576 {
00577 static const DiagDirection _search_dir_1[] = {
00578 DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
00579 };
00580 static const DiagDirection _search_dir_2[] = {
00581 DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
00582 };
00583
00584
00585 assert(_globset.IsEmpty() || owner == _last_owner);
00586
00587 _last_owner = owner;
00588
00589 _globset.Add(tile, _search_dir_1[track]);
00590 _globset.Add(tile, _search_dir_2[track]);
00591
00592 if (_globset.Items() >= SIG_GLOB_UPDATE) {
00593
00594 UpdateSignalsInBuffer(_last_owner);
00595 _last_owner = INVALID_OWNER;
00596 }
00597 }
00598
00599
00607 void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
00608 {
00609
00610 assert(_globset.IsEmpty() || owner == _last_owner);
00611
00612 _last_owner = owner;
00613
00614 _globset.Add(tile, side);
00615
00616 if (_globset.Items() >= SIG_GLOB_UPDATE) {
00617
00618 UpdateSignalsInBuffer(_last_owner);
00619 _last_owner = INVALID_OWNER;
00620 }
00621 }
00622
00633 bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
00634 {
00635 assert(_globset.IsEmpty());
00636 _globset.Add(tile, side);
00637
00638 return UpdateSignalsInBuffer(owner);
00639 }
00640
00641
00651 void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
00652 {
00653 assert(_globset.IsEmpty());
00654
00655 AddTrackToSignalBuffer(tile, track, owner);
00656 UpdateSignalsInBuffer(owner);
00657 }