00001
00002
00005 #include "stdafx.h"
00006 #include "debug.h"
00007 #include "station_map.h"
00008 #include "tunnelbridge_map.h"
00009 #include "vehicle_func.h"
00010 #include "vehicle_base.h"
00011 #include "functions.h"
00012
00013
00015 enum {
00016 SIG_TBU_SIZE = 64,
00017 SIG_TBD_SIZE = 256,
00018 SIG_GLOB_SIZE = 128,
00019 SIG_GLOB_UPDATE = 64,
00020 };
00021
00022
00023 assert_compile((int)SIG_GLOB_UPDATE <= (int)SIG_GLOB_SIZE);
00024
00026 static const TrackBitsByte _enterdir_to_trackbits[DIAGDIR_END] = {
00027 {TRACK_BIT_3WAY_NE},
00028 {TRACK_BIT_3WAY_SE},
00029 {TRACK_BIT_3WAY_SW},
00030 {TRACK_BIT_3WAY_NW}
00031 };
00032
00034 static const TrackdirBitsShort _enterdir_to_trackdirbits[DIAGDIR_END] = {
00035 {TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S},
00036 {TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N},
00037 {TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N},
00038 {TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S}
00039 };
00040
00046 template <typename Tdir, uint items>
00047 struct SmallSet {
00048 private:
00049 uint n;
00050 bool overflowed;
00051 const char *name;
00052
00054 struct SSdata {
00055 TileIndex tile;
00056 Tdir dir;
00057 } data[items];
00058
00059 public:
00061 SmallSet(const char *name) : n(0), overflowed(false), name(name) { }
00062
00064 void Reset()
00065 {
00066 this->n = 0;
00067 this->overflowed = false;
00068 }
00069
00074 bool Overflowed()
00075 {
00076 return this->overflowed;
00077 }
00078
00083 bool IsEmpty()
00084 {
00085 return this->n == 0;
00086 }
00087
00092 bool IsFull()
00093 {
00094 return this->n == lengthof(data);
00095 }
00096
00101 uint Items()
00102 {
00103 return this->n;
00104 }
00105
00106
00113 bool Remove(TileIndex tile, Tdir dir)
00114 {
00115 for (uint i = 0; i < this->n; i++) {
00116 if (this->data[i].tile == tile && this->data[i].dir == dir) {
00117 this->data[i] = this->data[--this->n];
00118 return true;
00119 }
00120 }
00121
00122 return false;
00123 }
00124
00131 bool IsIn(TileIndex tile, Tdir dir)
00132 {
00133 for (uint i = 0; i < this->n; i++) {
00134 if (this->data[i].tile == tile && this->data[i].dir == dir) return true;
00135 }
00136
00137 return false;
00138 }
00139
00147 bool Add(TileIndex tile, Tdir dir)
00148 {
00149 if (this->IsFull()) {
00150 overflowed = true;
00151 DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items);
00152 return false;
00153 }
00154
00155 this->data[this->n].tile = tile;
00156 this->data[this->n].dir = dir;
00157 this->n++;
00158
00159 return true;
00160 }
00161
00168 bool Get(TileIndex *tile, Tdir *dir)
00169 {
00170 if (this->n == 0) return false;
00171
00172 this->n--;
00173 *tile = this->data[this->n].tile;
00174 *dir = this->data[this->n].dir;
00175
00176 return true;
00177 }
00178 };
00179
00180 static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset");
00181 static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset");
00182 static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset");
00183
00184
00186 static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
00187 {
00188 if (v->type != VEH_TRAIN || v->u.rail.track == TRACK_BIT_DEPOT) return NULL;
00189
00190 return v;
00191 }
00192
00193
00207 static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00208 {
00209 _globset.Remove(t1, d1);
00210 _globset.Remove(t2, d2);
00211
00212 assert(!_tbdset.IsIn(t1, d1));
00213
00214 if (_tbdset.Remove(t2, d2)) return false;
00215
00216 return true;
00217 }
00218
00219
00233 static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00234 {
00235 if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true;
00236
00237 return _tbdset.Add(t1, d1);
00238 }
00239
00240
00242 enum SigFlags {
00243 SF_NONE = 0,
00244 SF_TRAIN = 1 << 0,
00245 SF_EXIT = 1 << 1,
00246 SF_EXIT2 = 1 << 2,
00247 SF_GREEN = 1 << 3,
00248 SF_GREEN2 = 1 << 4,
00249 SF_FULL = 1 << 5,
00250 SF_PBS = 1 << 6,
00251 };
00252
00253 DECLARE_ENUM_AS_BIT_SET(SigFlags)
00254
00255
00256
00262 static SigFlags ExploreSegment(Owner owner)
00263 {
00264 SigFlags flags = SF_NONE;
00265
00266 TileIndex tile;
00267 DiagDirection enterdir;
00268
00269 while (_tbdset.Get(&tile, &enterdir)) {
00270 TileIndex oldtile = tile;
00271 DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir);
00272
00273 switch (GetTileType(tile)) {
00274 case MP_RAILWAY: {
00275 if (GetTileOwner(tile) != owner) continue;
00276
00277 if (IsRailDepot(tile)) {
00278 if (enterdir == INVALID_DIAGDIR) {
00279 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00280 exitdir = GetRailDepotDirection(tile);
00281 tile += TileOffsByDiagDir(exitdir);
00282 enterdir = ReverseDiagDir(exitdir);
00283 break;
00284 } else if (enterdir == GetRailDepotDirection(tile)) {
00285 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00286 continue;
00287 } else {
00288 continue;
00289 }
00290 }
00291
00292 if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) {
00293 if (GetWaypointAxis(tile) != DiagDirToAxis(enterdir)) continue;
00294 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00295 tile += TileOffsByDiagDir(exitdir);
00296
00297 break;
00298 }
00299
00300 TrackBits tracks = GetTrackBits(tile);
00301 TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]);
00302
00303 if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) {
00304 tracks = tracks_masked;
00305 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tracks, &EnsureNoTrainOnTrackProc)) flags |= SF_TRAIN;
00306 } else {
00307 if (tracks_masked == TRACK_BIT_NONE) continue;
00308 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00309 }
00310
00311 if (HasSignals(tile)) {
00312 Track track = TrackBitsToTrack(tracks_masked);
00313 if (HasSignalOnTrack(tile, track)) {
00314 SignalType sig = GetSignalType(tile, track);
00315 Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
00316 Trackdir reversedir = ReverseTrackdir(trackdir);
00317
00318
00319
00320 if (HasSignalOnTrackdir(tile, reversedir)) {
00321 if (IsPbsSignal(sig)) {
00322 flags |= SF_PBS;
00323 } else if (!_tbuset.Add(tile, reversedir)) {
00324 return flags | SF_FULL;
00325 }
00326 }
00327 if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS;
00328
00329
00330 if (!(flags & SF_GREEN2) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) {
00331 if (flags & SF_EXIT) flags |= SF_EXIT2;
00332 flags |= SF_EXIT;
00333 if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) {
00334 if (flags & SF_GREEN) flags |= SF_GREEN2;
00335 flags |= SF_GREEN;
00336 }
00337 }
00338
00339 continue;
00340 }
00341 }
00342
00343 for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
00344 if (dir != enterdir && tracks & _enterdir_to_trackbits[dir]) {
00345 TileIndex newtile = tile + TileOffsByDiagDir(dir);
00346 DiagDirection newdir = ReverseDiagDir(dir);
00347 if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL;
00348 }
00349 }
00350
00351 continue;
00352 }
00353
00354 case MP_STATION:
00355 if (!IsRailwayStation(tile)) continue;
00356 if (GetTileOwner(tile) != owner) continue;
00357 if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue;
00358 if (IsStationTileBlocked(tile)) continue;
00359
00360 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00361 tile += TileOffsByDiagDir(exitdir);
00362 break;
00363
00364 case MP_ROAD:
00365 if (!IsLevelCrossing(tile)) continue;
00366 if (GetTileOwner(tile) != owner) continue;
00367 if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue;
00368
00369 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00370 tile += TileOffsByDiagDir(exitdir);
00371 break;
00372
00373 case MP_TUNNELBRIDGE: {
00374 if (GetTileOwner(tile) != owner) continue;
00375 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
00376 DiagDirection dir = GetTunnelBridgeDirection(tile);
00377
00378 if (enterdir == INVALID_DIAGDIR) {
00379 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00380 enterdir = dir;
00381 exitdir = ReverseDiagDir(dir);
00382 tile += TileOffsByDiagDir(exitdir);
00383 } else {
00384 if (ReverseDiagDir(enterdir) != dir) continue;
00385 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
00386 tile = GetOtherTunnelBridgeEnd(tile);
00387 enterdir = INVALID_DIAGDIR;
00388 exitdir = INVALID_DIAGDIR;
00389 }
00390 }
00391 break;
00392
00393 default:
00394 continue;
00395 }
00396
00397 if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL;
00398 }
00399
00400 return flags;
00401 }
00402
00403
00409 static void UpdateSignalsAroundSegment(SigFlags flags)
00410 {
00411 TileIndex tile;
00412 Trackdir trackdir;
00413
00414 while (_tbuset.Get(&tile, &trackdir)) {
00415 assert(HasSignalOnTrackdir(tile, trackdir));
00416
00417 SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir));
00418 SignalState newstate = SIGNAL_STATE_GREEN;
00419
00420
00421 if (flags & SF_TRAIN) {
00422
00423 newstate = SIGNAL_STATE_RED;
00424 } else {
00425
00426 if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
00427
00428 if (flags & SF_EXIT2 &&
00429
00430 (!(flags & SF_GREEN) ||
00431
00432 (!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) {
00433 newstate = SIGNAL_STATE_RED;
00434 }
00435 } else {
00436 if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && flags & SF_EXIT && !(flags & SF_GREEN)) newstate = SIGNAL_STATE_RED;
00437 }
00438 }
00439
00440
00441 if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
00442 if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) {
00443
00444 DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
00445 _globset.Add(tile, exitdir);
00446 }
00447 SetSignalStateByTrackdir(tile, trackdir, newstate);
00448 MarkTileDirtyByTile(tile);
00449 }
00450 }
00451
00452 }
00453
00454
00456 static inline void ResetSets()
00457 {
00458 _tbuset.Reset();
00459 _tbdset.Reset();
00460 _globset.Reset();
00461 }
00462
00463
00471 static SigSegState UpdateSignalsInBuffer(Owner owner)
00472 {
00473 assert(IsValidCompanyID(owner));
00474
00475 bool first = true;
00476 SigSegState state = SIGSEG_FREE;
00477
00478 TileIndex tile;
00479 DiagDirection dir;
00480
00481 while (_globset.Get(&tile, &dir)) {
00482 assert(_tbuset.IsEmpty());
00483 assert(_tbdset.IsEmpty());
00484
00485
00486
00487
00488
00489
00490 switch (GetTileType(tile)) {
00491 case MP_TUNNELBRIDGE:
00492
00493 assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
00494 assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
00495 _tbdset.Add(tile, INVALID_DIAGDIR);
00496 _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
00497 break;
00498
00499 case MP_RAILWAY:
00500 if (IsRailDepot(tile)) {
00501
00502 assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
00503 _tbdset.Add(tile, INVALID_DIAGDIR);
00504 break;
00505 }
00506
00507 case MP_STATION:
00508 case MP_ROAD:
00509 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00510
00511 _tbdset.Add(tile, dir);
00512 _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
00513 break;
00514 }
00515
00516 default:
00517
00518 tile = tile + TileOffsByDiagDir(dir);
00519 dir = ReverseDiagDir(dir);
00520 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00521 _tbdset.Add(tile, dir);
00522 break;
00523 }
00524
00525 continue;
00526 }
00527
00528 assert(!_tbdset.Overflowed());
00529 assert(!_tbdset.IsEmpty());
00530
00531 SigFlags flags = ExploreSegment(owner);
00532
00533 if (first) {
00534 first = false;
00535
00536 if (flags & SF_PBS) {
00537 state = SIGSEG_PBS;
00538 } else if (flags & SF_TRAIN || (flags & SF_EXIT && !(flags & SF_GREEN)) || flags & SF_FULL) {
00539 state = SIGSEG_FULL;
00540 }
00541 }
00542
00543
00544 if (flags & SF_FULL) {
00545 ResetSets();
00546 break;
00547 }
00548
00549 UpdateSignalsAroundSegment(flags);
00550 }
00551
00552 return state;
00553 }
00554
00555
00556 static Owner _last_owner = INVALID_OWNER;
00557
00558
00563 void UpdateSignalsInBuffer()
00564 {
00565 if (!_globset.IsEmpty()) {
00566 UpdateSignalsInBuffer(_last_owner);
00567 _last_owner = INVALID_OWNER;
00568 }
00569 }
00570
00571
00579 void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
00580 {
00581 static const DiagDirection _search_dir_1[] = {
00582 DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
00583 };
00584 static const DiagDirection _search_dir_2[] = {
00585 DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
00586 };
00587
00588
00589 assert(_globset.IsEmpty() || 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 assert(_globset.IsEmpty() || owner == _last_owner);
00615
00616 _last_owner = owner;
00617
00618 _globset.Add(tile, side);
00619
00620 if (_globset.Items() >= SIG_GLOB_UPDATE) {
00621
00622 UpdateSignalsInBuffer(_last_owner);
00623 _last_owner = INVALID_OWNER;
00624 }
00625 }
00626
00637 SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
00638 {
00639 assert(_globset.IsEmpty());
00640 _globset.Add(tile, side);
00641
00642 return UpdateSignalsInBuffer(owner);
00643 }
00644
00645
00655 void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
00656 {
00657 assert(_globset.IsEmpty());
00658
00659 AddTrackToSignalBuffer(tile, track, owner);
00660 UpdateSignalsInBuffer(owner);
00661 }