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