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 "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 TrackBits tracks = GetTrackBits(tile);
00298 TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]);
00299
00300 if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) {
00301 tracks = tracks_masked;
00302
00303 if (!(flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) 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 }