signal.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
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 #include "gui.h"
00021 #include "table/strings.h"
00022 #include "programmable_signals.h"
00023 #include "infrastructure_func.h"
00024 
00026 typedef SmallVector<SignalReference, 4>     SignalDependencyList;
00027 
00032 typedef std::map<SignalReference, SignalDependencyList> SignalDependencyMap;
00033 
00034 static SignalDependencyMap _signal_dependencies;
00035 static void MarkDependencidesForUpdate(SignalReference sig);
00036 
00038 static const uint SIG_TBU_SIZE    =  64; 
00039 static const uint SIG_TBD_SIZE    = 256; 
00040 static const uint SIG_GLOB_SIZE   = 128; 
00041 static const uint SIG_GLOB_UPDATE =  64; 
00042 
00043 assert_compile(SIG_GLOB_UPDATE <= SIG_GLOB_SIZE);
00044 
00046 static const TrackBits _enterdir_to_trackbits[DIAGDIR_END] = {
00047   TRACK_BIT_3WAY_NE,
00048   TRACK_BIT_3WAY_SE,
00049   TRACK_BIT_3WAY_SW,
00050   TRACK_BIT_3WAY_NW
00051 };
00052 
00054 static const TrackdirBits _enterdir_to_trackdirbits[DIAGDIR_END] = {
00055   TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S,
00056   TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N,
00057   TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N,
00058   TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S
00059 };
00060 
00066 template <typename Tdir, uint items>
00067 struct SmallSet {
00068 private:
00069   uint n;           // actual number of units
00070   bool overflowed;  // did we try to oveflow the set?
00071   const char *name; // name, used for debugging purposes...
00072 
00074   struct SSdata {
00075     TileIndex tile;
00076     Tdir dir;
00077   } data[items];
00078 
00079 public:
00081   SmallSet(const char *name) : n(0), overflowed(false), name(name) { }
00082 
00084   void Reset()
00085   {
00086     this->n = 0;
00087     this->overflowed = false;
00088   }
00089 
00094   bool Overflowed()
00095   {
00096     return this->overflowed;
00097   }
00098 
00103   bool IsEmpty()
00104   {
00105     return this->n == 0;
00106   }
00107 
00112   bool IsFull()
00113   {
00114     return this->n == lengthof(data);
00115   }
00116 
00121   uint Items()
00122   {
00123     return this->n;
00124   }
00125 
00126 
00133   bool Remove(TileIndex tile, Tdir dir)
00134   {
00135     for (uint i = 0; i < this->n; i++) {
00136       if (this->data[i].tile == tile && this->data[i].dir == dir) {
00137         this->data[i] = this->data[--this->n];
00138         return true;
00139       }
00140     }
00141 
00142     return false;
00143   }
00144 
00151   bool IsIn(TileIndex tile, Tdir dir)
00152   {
00153     for (uint i = 0; i < this->n; i++) {
00154       if (this->data[i].tile == tile && this->data[i].dir == dir) return true;
00155     }
00156 
00157     return false;
00158   }
00159 
00167   bool Add(TileIndex tile, Tdir dir)
00168   {
00169     if (this->IsFull()) {
00170       overflowed = true;
00171       DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items);
00172       return false; // set is full
00173     }
00174 
00175     this->data[this->n].tile = tile;
00176     this->data[this->n].dir = dir;
00177     this->n++;
00178 
00179     return true;
00180   }
00181 
00188   bool Get(TileIndex *tile, Tdir *dir)
00189   {
00190     if (this->n == 0) return false;
00191 
00192     this->n--;
00193     *tile = this->data[this->n].tile;
00194     *dir = this->data[this->n].dir;
00195 
00196     return true;
00197   }
00198 };
00199 
00200 static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset");         
00201 static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset");    
00202 static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset"); 
00203 static uint _num_signals_evaluated;                                 
00204 
00206 static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
00207 {
00208   if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return NULL;
00209 
00210   return v;
00211 }
00212 
00226 static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00227 {
00228   _globset.Remove(t1, d1); // it can be in Global but not in Todo
00229   _globset.Remove(t2, d2); // remove in all cases
00230 
00231   assert(!_tbdset.IsIn(t1, d1)); // it really shouldn't be there already
00232 
00233   if (_tbdset.Remove(t2, d2)) return false;
00234 
00235   return true;
00236 }
00237 
00239 static Vehicle *TrainInWormholeTileEnum(Vehicle *v, void *data)
00240 {
00241   /* Only look for front engine or last wagon. */
00242   if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
00243   if (*(TileIndex *)data != TileVirtXY(v->x_pos, v->y_pos)) return NULL;
00244   return v;
00245 }
00246 
00260 static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
00261 {
00262   if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true;
00263 
00264   return _tbdset.Add(t1, d1);
00265 }
00266 
00267 
00269 enum SigFlags {
00270   SF_NONE  = 0,
00271   SF_TRAIN = 1 << 0, 
00272   SF_FULL  = 1 << 1, 
00273   SF_PBS   = 1 << 2, 
00274 };
00275 
00276 DECLARE_ENUM_AS_BIT_SET(SigFlags)
00277 
00278 struct SigInfo {
00279   inline SigInfo()
00280   {
00281     flags = SF_NONE;
00282     num_exits = 0;
00283     num_green = 0;
00284   }
00285   SigFlags flags;
00286   uint num_exits;
00287   uint num_green;
00288 };
00289 
00296 static SigInfo ExploreSegment(Owner owner)
00297 {
00298   SigInfo info;
00299   TileIndex tile;
00300   DiagDirection enterdir;
00301 
00302   while (_tbdset.Get(&tile, &enterdir)) {
00303     TileIndex oldtile = tile; // tile we are leaving
00304     DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir); // expected new exit direction (for straight line)
00305 
00306     switch (GetTileType(tile)) {
00307       case MP_RAILWAY: {
00308         if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
00309 
00310         if (IsRailDepot(tile)) {
00311           if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
00312             if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00313             exitdir = GetRailDepotDirection(tile);
00314             tile += TileOffsByDiagDir(exitdir);
00315             enterdir = ReverseDiagDir(exitdir);
00316             break;
00317           } else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot
00318             if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00319             continue;
00320           } else {
00321             continue;
00322           }
00323         }
00324 
00325         TrackBits tracks = GetTrackBits(tile); // trackbits of tile
00326         TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
00327 
00328         if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check
00329           tracks = tracks_masked;
00330           /* If no train detected yet, and there is not no train -> there is a train -> set the flag */
00331           if (!(info.flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) info.flags |= SF_TRAIN;
00332         } else {
00333           if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track
00334           if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00335         }
00336 
00337         if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile
00338           Track track = TrackBitsToTrack(tracks_masked); // mask TRACK_BIT_X and Y too
00339           if (HasSignalOnTrack(tile, track) && !IsSpeedSignal(GetSignalType(tile, track))) { // now check whole track, not trackdir
00340             SignalType sig = GetSignalType(tile, track);
00341             Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
00342             Trackdir reversedir = ReverseTrackdir(trackdir);
00343             /* add (tile, reversetrackdir) to 'to-be-updated' set when there is
00344              * ANY conventional signal in REVERSE direction
00345              * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
00346             if (HasSignalOnTrackdir(tile, reversedir)) {
00347               if (IsPbsSignal(sig)) {
00348                 info.flags |= SF_PBS;
00349               } else if (!_tbuset.Add(tile, reversedir)) {
00350                 info.flags |= SF_FULL;
00351                 return info;
00352               }
00353             }
00354             if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) info.flags |= SF_PBS;
00355 
00356             /* if it is a presignal EXIT in OUR direction, count it */
00357             if (IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
00358               info.num_exits++;
00359               if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
00360                 info.num_green++;
00361               }
00362             }
00363 
00364             continue;
00365           }
00366         }
00367 
00368         for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { // test all possible exit directions
00369           if (dir != enterdir && (tracks & _enterdir_to_trackbits[dir])) { // any track incidating?
00370             TileIndex newtile = tile + TileOffsByDiagDir(dir);  // new tile to check
00371             DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from
00372             if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) {
00373               info.flags |= SF_FULL;
00374               return info;
00375             }
00376           }
00377         }
00378 
00379         continue; // continue the while() loop
00380         }
00381 
00382       case MP_STATION:
00383         if (!HasStationRail(tile)) continue;
00384         if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
00385         if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
00386         if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
00387 
00388         if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00389         tile += TileOffsByDiagDir(exitdir);
00390         break;
00391 
00392       case MP_ROAD:
00393         if (!IsLevelCrossing(tile)) continue;
00394         if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
00395         if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
00396 
00397         if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00398         tile += TileOffsByDiagDir(exitdir);
00399         break;
00400 
00401       case MP_TUNNELBRIDGE:
00402         {
00403         if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
00404           if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
00405           DiagDirection dir;
00406           dir = GetTunnelBridgeDirection(tile);
00407 
00408           if (HasWormholeSignals(tile)) {
00409             if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
00410               if (!(info.flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { // tunnel entrence is ignored
00411                 if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
00412                 if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
00413               }
00414               enterdir = dir;
00415               exitdir = ReverseDiagDir(dir);
00416               tile += TileOffsByDiagDir(exitdir); // just skip to next tile
00417             } else { // NOT incoming from the wormhole!
00418               if (ReverseDiagDir(enterdir) != dir) continue;
00419               if (!(info.flags & SF_TRAIN)) {
00420                 if (HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
00421                 if (!(info.flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) {
00422                   if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
00423                 } 
00424               }
00425               continue;
00426             }
00427           } else {
00428             if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
00429               if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00430               enterdir = dir;
00431               exitdir = ReverseDiagDir(dir);
00432               tile += TileOffsByDiagDir(exitdir); // just skip to next tile
00433             } else { // NOT incoming from the wormhole!
00434               if (ReverseDiagDir(enterdir) != dir) continue;
00435               if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
00436               tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
00437               enterdir = INVALID_DIAGDIR;
00438               exitdir = INVALID_DIAGDIR;
00439             }
00440           }
00441         }
00442         break;
00443 
00444       default:
00445         continue; // continue the while() loop
00446     }
00447 
00448     if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) {
00449       info.flags |= SF_FULL;
00450     }
00451   }
00452 
00453   return info;
00454 }
00455 
00456 
00462 static void UpdateSignalsAroundSegment(SigInfo info)
00463 {
00464   TileIndex tile;
00465   Trackdir trackdir;
00466   Track track;
00467 
00468   while (_tbuset.Get(&tile, &trackdir)) {
00469     assert(HasSignalOnTrackdir(tile, trackdir));
00470 
00471     track = TrackdirToTrack(trackdir);
00472     SignalType sig = GetSignalType(tile, track);
00473     SignalState newstate = SIGNAL_STATE_GREEN;
00474 
00475     /* determine whether the new state is red */
00476     if (sig == SIGTYPE_SPEED) {
00477       newstate = SIGNAL_STATE_GREEN;
00478     } else if (info.flags & SF_TRAIN) {
00479       /* train in the segment */
00480       newstate = SIGNAL_STATE_RED;
00481     } else if (sig == SIGTYPE_PROG && 
00482         _num_signals_evaluated > _settings_game.construction.maximum_signal_evaluations) {
00483       /* too many cascades */
00484       newstate = SIGNAL_STATE_RED;
00485     } else {
00486       /* is it a bidir combo? - then do not count its other signal direction as exit */
00487       if (IsComboSignal(sig) && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
00488         // Don't count ourselves
00489         uint exits = info.num_exits - 1;
00490         uint green = info.num_green;
00491         if (GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN)
00492           green--;
00493         
00494         if (sig == SIGTYPE_PROG) { /* Programmable */
00495           _num_signals_evaluated++;
00496           
00497           if (!RunSignalProgram(SignalReference(tile, track), exits, green))
00498             newstate = SIGNAL_STATE_RED;
00499         } else { /* traditional combo */
00500           if (!green && exits)
00501             newstate = SIGNAL_STATE_RED;
00502         }
00503       } else { // entry, at least one exit, no green exit
00504         if (IsEntrySignal(sig)) {
00505           if (sig == SIGTYPE_PROG) {
00506             _num_signals_evaluated++;
00507             if (!RunSignalProgram(SignalReference(tile, track), info.num_exits, info.num_green))
00508               newstate = SIGNAL_STATE_RED;
00509           } else { /* traditional combo */
00510             if (!info.num_green && info.num_exits) newstate = SIGNAL_STATE_RED;
00511           }
00512         }
00513       }
00514     }
00515 
00516     /* only when the state changes */
00517     if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
00518       if (IsExitSignal(sig)) {
00519         /* for pre-signal exits, add block to the global set */
00520         DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
00521         _globset.Add(tile, exitdir); // do not check for full global set, first update all signals
00522 
00523         /* Progsig dependencies */
00524         MarkDependencidesForUpdate(SignalReference(tile, track));
00525       }
00526       SetSignalStateByTrackdir(tile, trackdir, newstate);
00527       MarkTileDirtyByTile(tile);
00528     }
00529   }
00530 }
00531 
00532 
00534 static inline void ResetSets()
00535 {
00536   _tbuset.Reset();
00537   _tbdset.Reset();
00538   _globset.Reset();
00539 }
00540 
00541 
00549 static SigSegState UpdateSignalsInBuffer(Owner owner)
00550 {
00551   assert(Company::IsValidID(owner));
00552 
00553   bool first = true;  // first block?
00554   SigSegState state = SIGSEG_FREE; // value to return
00555   _num_signals_evaluated = 0;
00556 
00557   TileIndex tile;
00558   DiagDirection dir;
00559 
00560   while (_globset.Get(&tile, &dir)) {
00561     assert(_tbuset.IsEmpty());
00562     assert(_tbdset.IsEmpty());
00563 
00564     /* After updating signal, data stored are always MP_RAILWAY with signals.
00565      * Other situations happen when data are from outside functions -
00566      * modification of railbits (including both rail building and removal),
00567      * train entering/leaving block, train leaving depot...
00568      */
00569     switch (GetTileType(tile)) {
00570       case MP_TUNNELBRIDGE:
00571         /* 'optimization assert' - do not try to update signals when it is not needed */
00572         assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
00573         assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
00574         _tbdset.Add(tile, INVALID_DIAGDIR);  // we can safely start from wormhole centre
00575         if (!HasWormholeSignals(tile)) {  // Don't worry with other side of tunnel.
00576           _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
00577         }
00578         break;
00579 
00580       case MP_RAILWAY:
00581         if (IsRailDepot(tile)) {
00582           /* 'optimization assert' do not try to update signals in other cases */
00583           assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
00584           _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside
00585           break;
00586         }
00587         /* FALL THROUGH */
00588       case MP_STATION:
00589       case MP_ROAD:
00590         if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00591           /* only add to set when there is some 'interesting' track */
00592           _tbdset.Add(tile, dir);
00593           _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
00594           break;
00595         }
00596         /* FALL THROUGH */
00597       default:
00598         /* jump to next tile */
00599         tile = tile + TileOffsByDiagDir(dir);
00600         dir = ReverseDiagDir(dir);
00601         if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
00602           _tbdset.Add(tile, dir);
00603           break;
00604         }
00605         /* happens when removing a rail that wasn't connected at one or both sides */
00606         continue; // continue the while() loop
00607     }
00608 
00609     assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items
00610     assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
00611 
00612     SigInfo info = ExploreSegment(owner);
00613 
00614     if (first) {
00615       first = false;
00616       /* SIGSEG_FREE is set by default */
00617       if (info.flags & SF_PBS) {
00618         state = SIGSEG_PBS;
00619       } else if ((info.flags & SF_TRAIN) || ((info.num_exits) && !(info.num_green)) || (info.flags & SF_FULL)) {
00620         state = SIGSEG_FULL;
00621       }
00622     }
00623 
00624     /* do not do anything when some buffer was full */
00625     if (info.flags & SF_FULL) {
00626       ResetSets(); // free all sets
00627       break;
00628     }
00629 
00630     if (_num_signals_evaluated > _settings_game.construction.maximum_signal_evaluations) {
00631       ShowErrorMessage(STR_ERROR_SIGNAL_CHANGES, STR_EMPTY, WL_INFO);
00632     }
00633 
00634     UpdateSignalsAroundSegment(info);
00635   }
00636 
00637   return state;
00638 }
00639 
00640 
00641 static Owner _last_owner = INVALID_OWNER; 
00642 
00643 
00648 void UpdateSignalsInBuffer()
00649 {
00650   if (!_globset.IsEmpty()) {
00651     UpdateSignalsInBuffer(_last_owner);
00652     _last_owner = INVALID_OWNER; // invalidate
00653   }
00654 }
00655 
00656 
00664 void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
00665 {
00666   static const DiagDirection _search_dir_1[] = {
00667     DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
00668   };
00669   static const DiagDirection _search_dir_2[] = {
00670     DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
00671   };
00672 
00673   /* do not allow signal updates for two companies in one run,
00674    * if these companies are not part of the same signal block */
00675   assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
00676 
00677   _last_owner = owner;
00678 
00679   _globset.Add(tile, _search_dir_1[track]);
00680   _globset.Add(tile, _search_dir_2[track]);
00681 
00682   if (_globset.Items() >= SIG_GLOB_UPDATE) {
00683     /* too many items, force update */
00684     UpdateSignalsInBuffer(_last_owner);
00685     _last_owner = INVALID_OWNER;
00686   }
00687 }
00688 
00689 
00697 void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
00698 {
00699   /* do not allow signal updates for two companies in one run,
00700    * if these companies are not part of the same signal block */
00701   assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
00702 
00703   _last_owner = owner;
00704 
00705   _globset.Add(tile, side);
00706 
00707   if (_globset.Items() >= SIG_GLOB_UPDATE) {
00708     /* too many items, force update */
00709     UpdateSignalsInBuffer(_last_owner);
00710     _last_owner = INVALID_OWNER;
00711   }
00712 }
00713 
00724 SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
00725 {
00726   assert(_globset.IsEmpty());
00727   _globset.Add(tile, side);
00728 
00729   return UpdateSignalsInBuffer(owner);
00730 }
00731 
00732 
00742 void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
00743 {
00744   assert(_globset.IsEmpty());
00745 
00746   AddTrackToSignalBuffer(tile, track, owner);
00747   UpdateSignalsInBuffer(owner);
00748 }
00749 
00750 void AddSignalDependency(SignalReference on, SignalReference dep)
00751 { 
00752   assert(GetTileOwner(on.tile) == GetTileOwner(dep.tile));
00753   SignalDependencyList &dependencies = _signal_dependencies[on];
00754   (*dependencies.Append()) = dep;
00755 }
00756 
00757 void RemoveSignalDependency(SignalReference on, SignalReference dep)
00758 {
00759   SignalDependencyList &dependencies = _signal_dependencies[on];
00760   SignalReference *ob = dependencies.Find(dep);
00761 
00762   // Destroying both signals in same command
00763   if(ob == dependencies.End())
00764     return;
00765 
00766   dependencies.Erase(ob);
00767   if (dependencies.Length() == 0)
00768     _signal_dependencies.erase(on);
00769 }
00770 
00771 void FreeSignalDependencies()
00772 {
00773   _signal_dependencies.clear();
00774 }
00775 
00776 static void MarkDependencidesForUpdate(SignalReference on)
00777 {
00778   SignalDependencyMap::iterator f = _signal_dependencies.find(on);
00779   if (f == _signal_dependencies.end()) return;
00780   
00781   SignalDependencyList &dependencies = f->second;
00782   for (SignalReference *i = dependencies.Begin(), *e = dependencies.End();
00783       i != e; ++i) {
00784     assert(GetTileOwner(i->tile) == GetTileOwner(on.tile));
00785   
00786     Trackdir td = TrackToTrackdir(i->track);
00787     _globset.Add(i->tile, TrackdirToExitdir(td));
00788     _globset.Add(i->tile, TrackdirToExitdir(ReverseTrackdir(td)));
00789   }
00790 }
00791 
00792 void CheckRemoveSignalsFromTile(TileIndex tile)
00793 {
00794   if (!HasSignals(tile)) return;
00795   
00796   TrackBits tb = GetTrackBits(tile);
00797   Track tr;
00798   while ((tr = RemoveFirstTrack(&tb)) != INVALID_TRACK) {
00799     if (HasSignalOnTrack(tile, tr)) CheckRemoveSignal(tile, tr);
00800   }
00801 }
00802 
00803 static void NotifyRemovingDependentSignal(SignalReference on, SignalReference by)
00804 {
00805     SignalType t = GetSignalType(by.tile, by.track);
00806     if (IsProgrammableSignal(t)) {
00807         RemoveProgramDependencies(by, on);
00808     } else DEBUG(misc, 0, "Removing dependency held by non-programmable signal (Unexpected)");
00809 }
00810 
00811 void CheckRemoveSignal(TileIndex tile, Track track)
00812 {
00813   if (!HasSignalOnTrack(tile, track)) return;
00814   SignalReference thisRef(tile, track);
00815   
00816   SignalType t = GetSignalType(tile, track);
00817   if (IsProgrammableSignal(t)) {
00818     FreeSignalProgram(thisRef);
00819   } else if (IsSpeedSignal(t)) {
00820     FreeSignalSpeed(thisRef);
00821   }
00822   
00823   SignalDependencyMap::iterator i = _signal_dependencies.find(SignalReference(tile, track)), e = _signal_dependencies.end();
00824 
00825   if (i != e) {
00826     SignalDependencyList &dependencies = i->second;
00827 
00828     for (SignalReference *ir = dependencies.Begin(), *er = dependencies.End(); ir != er; ++ir) {
00829       assert(GetTileOwner(ir->tile) == GetTileOwner(tile));
00830       NotifyRemovingDependentSignal(thisRef, *ir);
00831     }
00832 
00833     _signal_dependencies.erase(i);
00834   }
00835 }