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 "viewport_func.h"
00018 #include "train.h"
00019 #include "company_base.h"
00020
00021
00023 static const uint SIG_TBU_SIZE = 64;
00024 static const uint SIG_TBD_SIZE = 256;
00025 static const uint SIG_GLOB_SIZE = 128;
00026 static const uint SIG_GLOB_UPDATE = 64;
00027
00028 assert_compile(SIG_GLOB_UPDATE <= SIG_GLOB_SIZE);
00029
00031 static const TrackBits _enterdir_to_trackbits[DIAGDIR_END] = {
00032 TRACK_BIT_3WAY_NE,
00033 TRACK_BIT_3WAY_SE,
00034 TRACK_BIT_3WAY_SW,
00035 TRACK_BIT_3WAY_NW
00036 };
00037
00039 static const TrackdirBits _enterdir_to_trackdirbits[DIAGDIR_END] = {
00040 TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S,
00041 TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N,
00042 TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N,
00043 TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S
00044 };
00045
00051 template <typename Tdir, uint items>
00052 struct SmallSet {
00053 private:
00054 uint n;
00055 bool overflowed;
00056 const char *name;
00057
00059 struct SSdata {
00060 TileIndex tile;
00061 Tdir dir;
00062 } data[items];
00063
00064 public:
00066 SmallSet(const char *name) : n(0), overflowed(false), name(name) { }
00067
00069 void Reset()
00070 {
00071 this->n = 0;
00072 this->overflowed = false;
00073 }
00074
00079 bool Overflowed()
00080 {
00081 return this->overflowed;
00082 }
00083
00088 bool IsEmpty()
00089 {
00090 return this->n == 0;
00091 }
00092
00097 bool IsFull()
00098 {
00099 return this->n == lengthof(data);
00100 }
00101
00106 uint Items()
00107 {
00108 return this->n;
00109 }
00110
00111
00118 bool Remove(TileIndex tile, Tdir dir)
00119 {
00120 for (uint i = 0; i < this->n; i++) {
00121 if (this->data[i].tile == tile && this->data[i].dir == dir) {
00122 this->data[i] = this->data[--this->n];
00123 return true;
00124 }
00125 }
00126
00127 return false;
00128 }
00129
00136 bool IsIn(TileIndex tile, Tdir dir)
00137 {
00138 for (uint i = 0; i < this->n; i++) {
00139 if (this->data[i].tile == tile && this->data[i].dir == dir) return true;
00140 }
00141
00142 return false;
00143 }
00144
00152 bool Add(TileIndex tile, Tdir dir)
00153 {
00154 if (this->IsFull()) {
00155 overflowed = true;
00156 DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items);
00157 return false;
00158 }
00159
00160 this->data[this->n].tile = tile;
00161 this->data[this->n].dir = dir;
00162 this->n++;
00163
00164 return true;
00165 }
00166
00173 bool Get(TileIndex *tile, Tdir *dir)
00174 {
00175 if (this->n == 0) return false;
00176
00177 this->n--;
00178 *tile = this->data[this->n].tile;
00179 *dir = this->data[this->n].dir;
00180
00181 return true;
00182 }
00183 };
00184
00185 static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset");
00186 static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset");
00187 static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset");
00188
00189
00191 static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
00192 {
00193 if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return NULL;
00194
00195 return v;
00196 }
00197
00198
00212 static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00213 {
00214 _globset.Remove(t1, d1);
00215 _globset.Remove(t2, d2);
00216
00217 assert(!_tbdset.IsIn(t1, d1));
00218
00219 if (_tbdset.Remove(t2, d2)) return false;
00220
00221 return true;
00222 }
00223
00224
00238 static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00239 {
00240 if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true;
00241
00242 return _tbdset.Add(t1, d1);
00243 }
00244
00245
00247 enum SigFlags {
00248 SF_NONE = 0,
00249 SF_TRAIN = 1 << 0,
00250 SF_EXIT = 1 << 1,
00251 SF_EXIT2 = 1 << 2,
00252 SF_GREEN = 1 << 3,
00253 SF_GREEN2 = 1 << 4,
00254 SF_FULL = 1 << 5,
00255 SF_PBS = 1 << 6,
00256 };
00257
00258 DECLARE_ENUM_AS_BIT_SET(SigFlags)
00259
00260
00261
00267 static SigFlags ExploreSegment(Owner owner)
00268 {
00269 SigFlags flags = SF_NONE;
00270
00271 TileIndex tile;
00272 DiagDirection enterdir;
00273
00274 while (_tbdset.Get(&tile, &enterdir)) {
00275 TileIndex oldtile = tile;
00276 DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir);
00277
00278 switch (GetTileType(tile)) {
00279 case MP_RAILWAY: {
00280 if (GetTileOwner(tile) != owner) continue;
00281
00282 if (IsRailDepot(tile)) {
00283 if (enterdir == INVALID_DIAGDIR) {
00284 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00285 exitdir = GetRailDepotDirection(tile);
00286 tile += TileOffsByDiagDir(exitdir);
00287 enterdir = ReverseDiagDir(exitdir);
00288 break;
00289 } else if (enterdir == GetRailDepotDirection(tile)) {
00290 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00291 continue;
00292 } else {
00293 continue;
00294 }
00295 }
00296
00297 assert(IsValidDiagDirection(enterdir));
00298 TrackBits tracks = GetTrackBits(tile);
00299 TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]);
00300
00301 if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) {
00302 tracks = tracks_masked;
00303
00304 if (!(flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) 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 (GetTileOwner(tile) != owner) 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 (GetTileOwner(tile) != owner) 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 (GetTileOwner(tile) != owner) 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 assert(_globset.IsEmpty() || owner == _last_owner);
00589
00590 _last_owner = owner;
00591
00592 _globset.Add(tile, _search_dir_1[track]);
00593 _globset.Add(tile, _search_dir_2[track]);
00594
00595 if (_globset.Items() >= SIG_GLOB_UPDATE) {
00596
00597 UpdateSignalsInBuffer(_last_owner);
00598 _last_owner = INVALID_OWNER;
00599 }
00600 }
00601
00602
00610 void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
00611 {
00612
00613 assert(_globset.IsEmpty() || owner == _last_owner);
00614
00615 _last_owner = owner;
00616
00617 _globset.Add(tile, side);
00618
00619 if (_globset.Items() >= SIG_GLOB_UPDATE) {
00620
00621 UpdateSignalsInBuffer(_last_owner);
00622 _last_owner = INVALID_OWNER;
00623 }
00624 }
00625
00636 SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
00637 {
00638 assert(_globset.IsEmpty());
00639 _globset.Add(tile, side);
00640
00641 return UpdateSignalsInBuffer(owner);
00642 }
00643
00644
00654 void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
00655 {
00656 assert(_globset.IsEmpty());
00657
00658 AddTrackToSignalBuffer(tile, track, owner);
00659 UpdateSignalsInBuffer(owner);
00660 }