# 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 }