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