src/signal.cpp
branchnoai
changeset 9724 b39bc69bb2f2
child 6878 7d1ff2f621c7
equal deleted inserted replaced
9723:eee46cb39750 9724:b39bc69bb2f2
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file signal.cpp functions related to rail signals updating */
       
     4 
       
     5 #include "stdafx.h"
       
     6 #include "openttd.h"
       
     7 #include "debug.h"
       
     8 #include "tile_cmd.h"
       
     9 #include "rail_map.h"
       
    10 #include "road_map.h"
       
    11 #include "station_map.h"
       
    12 #include "tunnelbridge_map.h"
       
    13 #include "vehicle_func.h"
       
    14 #include "train.h"
       
    15 #include "newgrf_station.h"
       
    16 #include "functions.h"
       
    17 #include "track_type.h"
       
    18 #include "track_func.h"
       
    19 #include "signal_func.h"
       
    20 #include "player_func.h"
       
    21 
       
    22 
       
    23 /** these are the maximums used for updating signal blocks */
       
    24 enum {
       
    25 	SIG_TBU_SIZE    =  64, ///< number of signals entering to block
       
    26 	SIG_TBD_SIZE    = 256, ///< number of intersections - open nodes in current block
       
    27 	SIG_GLOB_SIZE   = 128, ///< number of open blocks (block can be opened more times until detected)
       
    28 	SIG_GLOB_UPDATE =  64, ///< how many items need to be in _globset to force update
       
    29 };
       
    30 
       
    31 /* need to typecast to compile with MorphOS */
       
    32 assert_compile((int)SIG_GLOB_UPDATE <= (int)SIG_GLOB_SIZE);
       
    33 
       
    34 /** incidating trackbits with given enterdir */
       
    35 static const TrackBitsByte _enterdir_to_trackbits[DIAGDIR_END] = {
       
    36 	{TRACK_BIT_3WAY_NE},
       
    37 	{TRACK_BIT_3WAY_SE},
       
    38 	{TRACK_BIT_3WAY_SW},
       
    39 	{TRACK_BIT_3WAY_NW}
       
    40 };
       
    41 
       
    42 /** incidating trackdirbits with given enterdir */
       
    43 static const TrackdirBitsShort _enterdir_to_trackdirbits[DIAGDIR_END] = {
       
    44 	{TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S},
       
    45 	{TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N},
       
    46 	{TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N},
       
    47 	{TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S}
       
    48 };
       
    49 
       
    50 /**
       
    51  * Set containing 'items' items of 'tile and Tdir'
       
    52  * No tree structure is used because it would cause
       
    53  * slowdowns in most usual cases
       
    54  */
       
    55 template <typename Tdir, uint items>
       
    56 struct SmallSet {
       
    57 private:
       
    58 	uint n;           // actual number of units
       
    59 	bool overflowed;  // did we try to oveflow the set?
       
    60 	const char *name; // name, used for debugging purposes...
       
    61 
       
    62 	/** Element of set */
       
    63 	struct SSdata {
       
    64 		TileIndex tile;
       
    65 		Tdir dir;
       
    66 	} data[items];
       
    67 
       
    68 public:
       
    69 	/** Constructor - just set default values and 'name' */
       
    70 	SmallSet(const char *name) : n(0), overflowed(false), name(name) { }
       
    71 
       
    72 	/** Reset variables to default values */
       
    73 	void Reset()
       
    74 	{
       
    75 		this->n = 0;
       
    76 		this->overflowed = false;
       
    77 	}
       
    78 
       
    79 	/**
       
    80 	 * Returns value of 'oveflowed'
       
    81 	 * @return did we try to overflow the set?
       
    82 	 */
       
    83 	bool Overflowed()
       
    84 	{
       
    85 		return this->overflowed;
       
    86 	}
       
    87 
       
    88 	/**
       
    89 	 * Checks for empty set
       
    90 	 * @return is the set empty?
       
    91 	 */
       
    92 	bool IsEmpty()
       
    93 	{
       
    94 		return this->n == 0;
       
    95 	}
       
    96 
       
    97 	/**
       
    98 	 * Checks for full set
       
    99 	 * @return is the set full?
       
   100 	 */
       
   101 	bool IsFull()
       
   102 	{
       
   103 		return this->n == lengthof(data);
       
   104 	}
       
   105 
       
   106 	/**
       
   107 	 * Reads the number of items
       
   108 	 * @return current number of items
       
   109 	 */
       
   110 	uint Items()
       
   111 	{
       
   112 		return this->n;
       
   113 	}
       
   114 
       
   115 
       
   116 	/**
       
   117 	 * Tries to remove first instance of given tile and dir
       
   118 	 * @param tile tile
       
   119 	 * @param dir and dir to remove
       
   120 	 * @return element was found and removed
       
   121 	 */
       
   122 	bool Remove(TileIndex tile, Tdir dir)
       
   123 	{
       
   124 		for (uint i = 0; i < this->n; i++) {
       
   125 			if (this->data[i].tile == tile && this->data[i].dir == dir) {
       
   126 				this->data[i] = this->data[--this->n];
       
   127 				return true;
       
   128 			}
       
   129 		}
       
   130 
       
   131 		return false;
       
   132 	}
       
   133 
       
   134 	/**
       
   135 	 * Tries to find given tile and dir in the set
       
   136 	 * @param tile tile
       
   137 	 * @param dir and dir to find
       
   138 	 * @return true iff the tile & dir elemnt was found
       
   139 	 */
       
   140 	bool IsIn(TileIndex tile, Tdir dir)
       
   141 	{
       
   142 		for (uint i = 0; i < this->n; i++) {
       
   143 			if (this->data[i].tile == tile && this->data[i].dir == dir) return true;
       
   144 		}
       
   145 
       
   146 		return false;
       
   147 	}
       
   148 
       
   149 	/**
       
   150 	 * Adds tile & dir into the set, checks for full set
       
   151 	 * Sets the 'overflowed' flag if the set was full
       
   152 	 * @param tile tile
       
   153 	 * @param dir and dir to add
       
   154 	 * @return true iff the item could be added (set wasn't full)
       
   155 	 */
       
   156 	bool Add(TileIndex tile, Tdir dir)
       
   157 	{
       
   158 		if (this->IsFull()) {
       
   159 			overflowed = true;
       
   160 			DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items);
       
   161 			return false; // set is full
       
   162 		}
       
   163 
       
   164 		this->data[this->n].tile = tile;
       
   165 		this->data[this->n].dir = dir;
       
   166 		this->n++;
       
   167 
       
   168 		return true;
       
   169 	}
       
   170 
       
   171 	/**
       
   172 	 * Reads the last added element into the set
       
   173 	 * @param tile pointer where tile is written to
       
   174 	 * @param dir pointer where dir is written to
       
   175 	 * @return false iff the set was empty
       
   176 	 */
       
   177 	bool Get(TileIndex *tile, Tdir *dir)
       
   178 	{
       
   179 		if (this->n == 0) return false;
       
   180 
       
   181 		this->n--;
       
   182 		*tile = this->data[this->n].tile;
       
   183 		*dir = this->data[this->n].dir;
       
   184 
       
   185 		return true;
       
   186 	}
       
   187 };
       
   188 
       
   189 static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset");         ///< set of signals that will be updated
       
   190 static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset");    ///< set of open nodes in current signal block
       
   191 static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset"); ///< set of places to be updated in following runs
       
   192 
       
   193 
       
   194 /** Check whether there is a train on rail, not in a depot */
       
   195 static void *TrainOnTileEnum(Vehicle *v, void *)
       
   196 {
       
   197 	if (v->type != VEH_TRAIN || v->u.rail.track == TRACK_BIT_DEPOT) return NULL;
       
   198 
       
   199 	return v;
       
   200 }
       
   201 
       
   202 
       
   203 /**
       
   204  * Perform some operations before adding data into Todo set
       
   205  * The new and reverse direction is removed from _globset, because we are sure
       
   206  * it doesn't need to be checked again
       
   207  * Also, remove reverse direction from _tbdset
       
   208  * This is the 'core' part so the graph seaching won't enter any tile twice
       
   209  *
       
   210  * @param t1 tile we are entering
       
   211  * @param d1 direction (tile side) we are entering
       
   212  * @param t2 tile we are leaving
       
   213  * @param d2 direction (tile side) we are leaving
       
   214  * @return false iff reverse direction was in Todo set
       
   215  */
       
   216 static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
       
   217 {
       
   218 	_globset.Remove(t1, d1); // it can be in Global but not in Todo
       
   219 	_globset.Remove(t2, d2); // remove in all cases
       
   220 
       
   221 	assert(!_tbdset.IsIn(t1, d1)); // it really shouldn't be there already
       
   222 
       
   223 	if (_tbdset.Remove(t2, d2)) return false;
       
   224 
       
   225 	return true;
       
   226 }
       
   227 
       
   228 
       
   229 /**
       
   230  * Perform some operations before adding data into Todo set
       
   231  * The new and reverse direction is removed from Global set, because we are sure
       
   232  * it doesn't need to be checked again
       
   233  * Also, remove reverse direction from Todo set
       
   234  * This is the 'core' part so the graph seaching won't enter any tile twice
       
   235  *
       
   236  * @param t1 tile we are entering
       
   237  * @param d1 direction (tile side) we are entering
       
   238  * @param t2 tile we are leaving
       
   239  * @param d2 direction (tile side) we are leaving
       
   240  * @return false iff the Todo buffer would be overrun
       
   241  */
       
   242 static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2)
       
   243 {
       
   244 	if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true;
       
   245 
       
   246 	return _tbdset.Add(t1, d1);
       
   247 }
       
   248 
       
   249 
       
   250 /** Current signal block state flags */
       
   251 enum SigFlags {
       
   252 	SF_NONE   = 0,
       
   253 	SF_TRAIN  = 1 << 0, ///< train found in segment
       
   254 	SF_EXIT   = 1 << 1, ///< exitsignal found
       
   255 	SF_EXIT2  = 1 << 2, ///< two or more exits found
       
   256 	SF_GREEN  = 1 << 3, ///< green exitsignal found
       
   257 	SF_GREEN2 = 1 << 4, ///< two or more green exits found
       
   258 	SF_FULL   = 1 << 5, ///< some of buffers was full, do not continue
       
   259 };
       
   260 
       
   261 DECLARE_ENUM_AS_BIT_SET(SigFlags)
       
   262 
       
   263 
       
   264 /**
       
   265  * Search signal block
       
   266  *
       
   267  * @param owner owner whose signals we are updating
       
   268  * @return SigFlags
       
   269  */
       
   270 static SigFlags ExploreSegment(Owner owner)
       
   271 {
       
   272 	SigFlags flags = SF_NONE;
       
   273 
       
   274 	TileIndex tile;
       
   275 	DiagDirection enterdir;
       
   276 
       
   277 	while (_tbdset.Get(&tile, &enterdir)) {
       
   278 		TileIndex oldtile = tile; // tile we are leaving
       
   279 		DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir); // expected new exit direction (for straight line)
       
   280 
       
   281 		switch (GetTileType(tile)) {
       
   282 			case MP_RAILWAY: {
       
   283 				if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)
       
   284 
       
   285 				if (IsRailDepot(tile)) {
       
   286 					if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
       
   287 						if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   288 						exitdir = GetRailDepotDirection(tile);
       
   289 						tile += TileOffsByDiagDir(exitdir);
       
   290 						enterdir = ReverseDiagDir(exitdir);
       
   291 						break;
       
   292 					} else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot
       
   293 						if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   294 						continue;
       
   295 					} else {
       
   296 						continue;
       
   297 					}
       
   298 				}
       
   299 
       
   300 				if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) {
       
   301 					if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   302 					tile += TileOffsByDiagDir(exitdir);
       
   303 					/* enterdir and exitdir stay the same */
       
   304 					break;
       
   305 				}
       
   306 
       
   307 				TrackBits tracks = GetTrackBits(tile); // trackbits of tile
       
   308 				TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
       
   309 
       
   310 				if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check
       
   311 					tracks = tracks_masked;
       
   312 					if (!(flags & SF_TRAIN) && VehicleFromPos(tile, &tracks, &EnsureNoTrainOnTrackProc)) flags |= SF_TRAIN;
       
   313 				} else {
       
   314 					if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track
       
   315 					if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   316 				}
       
   317 
       
   318 				if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile
       
   319 					Track track = TrackBitsToTrack(tracks_masked); // mask TRACK_BIT_X and Y too
       
   320 					if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir
       
   321 						SignalType sig = GetSignalType(tile, track);
       
   322 						Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
       
   323 						Trackdir reversedir = ReverseTrackdir(trackdir);
       
   324 						/* add (tile, reversetrackdir) to 'to-be-updated' set when there is
       
   325 						 * ANY signal in REVERSE direction
       
   326 						 * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
       
   327 						if (HasSignalOnTrackdir(tile, reversedir)) {
       
   328 							if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL;
       
   329 						}
       
   330 						/* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
       
   331 						if (!(flags & SF_GREEN2) && (sig & SIGTYPE_EXIT) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
       
   332 							if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits
       
   333 							flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations
       
   334 							if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
       
   335 								if (flags & SF_GREEN) flags |= SF_GREEN2;
       
   336 								flags |= SF_GREEN;
       
   337 							}
       
   338 						}
       
   339 						continue;
       
   340 					}
       
   341 				}
       
   342 
       
   343 				for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { // test all possible exit directions
       
   344 					if (dir != enterdir && tracks & _enterdir_to_trackbits[dir]) { // any track incidating?
       
   345 						TileIndex newtile = tile + TileOffsByDiagDir(dir);  // new tile to check
       
   346 						DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from
       
   347 						if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL;
       
   348 					}
       
   349 				}
       
   350 
       
   351 				continue; // continue the while() loop
       
   352 				}
       
   353 
       
   354 			case MP_STATION:
       
   355 				if (!IsRailwayStation(tile)) continue;
       
   356 				if (GetTileOwner(tile) != owner) continue;
       
   357 				if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
       
   358 				if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
       
   359 
       
   360 				if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   361 				tile += TileOffsByDiagDir(exitdir);
       
   362 				break;
       
   363 
       
   364 			case MP_ROAD:
       
   365 				if (!IsLevelCrossing(tile)) continue;
       
   366 				if (GetTileOwner(tile) != owner) continue;
       
   367 				if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
       
   368 
       
   369 				if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   370 				tile += TileOffsByDiagDir(exitdir);
       
   371 				break;
       
   372 
       
   373 			case MP_TUNNELBRIDGE: {
       
   374 				if (GetTileOwner(tile) != owner) continue;
       
   375 				if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
       
   376 				DiagDirection dir = GetTunnelBridgeDirection(tile);
       
   377 
       
   378 				if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
       
   379 					if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   380 					enterdir = dir;
       
   381 					exitdir = ReverseDiagDir(dir);
       
   382 					tile += TileOffsByDiagDir(exitdir); // just skip to next tile
       
   383 				} else { // NOT incoming from the wormhole!
       
   384 					if (ReverseDiagDir(enterdir) != dir) continue;
       
   385 					if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
       
   386 					tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
       
   387 					enterdir = INVALID_DIAGDIR;
       
   388 					exitdir = INVALID_DIAGDIR;
       
   389 				}
       
   390 				}
       
   391 				break;
       
   392 
       
   393 			default:
       
   394 				continue; // continue the while() loop
       
   395 		}
       
   396 
       
   397 		if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL;
       
   398 	}
       
   399 
       
   400 	return flags;
       
   401 }
       
   402 
       
   403 
       
   404 /**
       
   405  * Update signals around segment in _tbuset
       
   406  *
       
   407  * @param flags info about segment
       
   408  */
       
   409 static void UpdateSignalsAroundSegment(SigFlags flags)
       
   410 {
       
   411 	TileIndex tile;
       
   412 	Trackdir trackdir;
       
   413 
       
   414 	while (_tbuset.Get(&tile, &trackdir)) {
       
   415 		assert(HasSignalOnTrackdir(tile, trackdir));
       
   416 
       
   417 		SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir));
       
   418 		SignalState newstate = SIGNAL_STATE_GREEN;
       
   419 
       
   420 		/* determine whether the new state is red */
       
   421 		if (flags & SF_TRAIN) {
       
   422 			/* train in the segment */
       
   423 			newstate = SIGNAL_STATE_RED;
       
   424 		} else {
       
   425  			/* is it a bidir combo? - then do not count its other signal direction as exit */
       
   426 			if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
       
   427 				/* at least one more exit */
       
   428 				if (flags & SF_EXIT2 &&
       
   429  						/* no green exit */
       
   430 						(!(flags & SF_GREEN) ||
       
   431 						/* only one green exit, and it is this one - so all other exits are red */
       
   432 						(!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) {
       
   433 					newstate = SIGNAL_STATE_RED;
       
   434 				}
       
   435 			} else { // entry, at least one exit, no green exit
       
   436 				if (sig & SIGTYPE_ENTRY && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED;
       
   437 			}
       
   438 		}
       
   439 
       
   440 		/* only when the state changes */
       
   441 		if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
       
   442 			if (sig & SIGTYPE_EXIT) {
       
   443 				/* for pre-signal exits, add block to the global set */
       
   444 				DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
       
   445 				_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
       
   446 			}
       
   447 			SetSignalStateByTrackdir(tile, trackdir, newstate);
       
   448 			MarkTileDirtyByTile(tile);
       
   449 		}
       
   450 	}
       
   451 
       
   452 }
       
   453 
       
   454 
       
   455 /** Reset all sets after one set overflowed */
       
   456 static inline void ResetSets()
       
   457 {
       
   458 	_tbuset.Reset();
       
   459 	_tbdset.Reset();
       
   460 	_globset.Reset();
       
   461 }
       
   462 
       
   463 
       
   464 /**
       
   465  * Updates blocks in _globset buffer
       
   466  *
       
   467  * @param owner player whose signals we are updating
       
   468  * @return false iff presignal entry would be green (needed for trains leaving depot)
       
   469  * @pre IsValidPlayer(owner)
       
   470  */
       
   471 static bool UpdateSignalsInBuffer(Owner owner)
       
   472 {
       
   473 	assert(IsValidPlayer(owner));
       
   474 
       
   475 	bool first = true;  // first block?
       
   476 	bool state = false; // value to return
       
   477 
       
   478 	TileIndex tile;
       
   479 	DiagDirection dir;
       
   480 
       
   481 	while (_globset.Get(&tile, &dir)) {
       
   482 		assert(_tbuset.IsEmpty());
       
   483 		assert(_tbdset.IsEmpty());
       
   484 
       
   485 		/* After updating signal, data stored are always MP_RAILWAY with signals.
       
   486 		 * Other situations happen when data are from outside functions -
       
   487 		 * modification of railbits (including both rail building and removal),
       
   488 		 * train entering/leaving block, train leaving depot...
       
   489 		 */
       
   490 		switch (GetTileType(tile)) {
       
   491 			case MP_TUNNELBRIDGE:
       
   492 				/* 'optimization assert' - do not try to update signals when it is not needed */
       
   493 				assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
       
   494 				assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
       
   495 				_tbdset.Add(tile, INVALID_DIAGDIR);  // we can safely start from wormhole centre
       
   496 				_tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
       
   497 				break;
       
   498 
       
   499 			case MP_RAILWAY:
       
   500 				if (IsRailDepot(tile)) {
       
   501 					/* 'optimization assert' do not try to update signals in other cases */
       
   502 					assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
       
   503 					_tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside
       
   504 					break;
       
   505 				}
       
   506 				/* FALLTHROUGH */
       
   507 			case MP_STATION:
       
   508 			case MP_ROAD:
       
   509 				if ((TrackBits)(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
       
   510  					/* only add to set when there is some 'interesting' track */
       
   511 					_tbdset.Add(tile, dir);
       
   512 					_tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
       
   513 					break;
       
   514 				}
       
   515 				/* FALLTHROUGH */
       
   516 			default:
       
   517 				/* jump to next tile */
       
   518 				tile = tile + TileOffsByDiagDir(dir);
       
   519 				dir = ReverseDiagDir(dir);
       
   520 				if ((TrackBits)(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
       
   521 					_tbdset.Add(tile, dir);
       
   522 					break;
       
   523 				}
       
   524 				/* happens when removing a rail that wasn't connected at one or both sides */
       
   525 				continue; // continue the while() loop
       
   526 		}
       
   527 
       
   528 		assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items
       
   529 		assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
       
   530 
       
   531 		SigFlags flags = ExploreSegment(owner);
       
   532 
       
   533 		if (first) {
       
   534 			first = false;
       
   535 			state = (flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL); // true iff train CAN'T leave the depot
       
   536 		}
       
   537 
       
   538 		/* do not do anything when some buffer was full */
       
   539 		if (flags & SF_FULL) {
       
   540 			ResetSets(); // free all sets
       
   541 			break;
       
   542 		}
       
   543 
       
   544 		UpdateSignalsAroundSegment(flags);
       
   545 	}
       
   546 
       
   547 	return state;
       
   548 }
       
   549 
       
   550 
       
   551 static Owner _last_owner = INVALID_OWNER; ///< last owner whose track was put into _globset
       
   552 
       
   553 
       
   554 /**
       
   555  * Update signals in buffer
       
   556  * Called from 'outside'
       
   557  */
       
   558 void UpdateSignalsInBuffer()
       
   559 {
       
   560 	if (!_globset.IsEmpty()) {
       
   561 		UpdateSignalsInBuffer(_last_owner);
       
   562 		_last_owner = INVALID_OWNER; // invalidate
       
   563 	}
       
   564 }
       
   565 
       
   566 
       
   567 /**
       
   568  * Add track to signal update buffer
       
   569  *
       
   570  * @param tile tile where we start
       
   571  * @param track track at which ends we will update signals
       
   572  * @param owner owner whose signals we will update
       
   573  */
       
   574 void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
       
   575 {
       
   576 	static const DiagDirection _search_dir_1[] = {
       
   577 		DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
       
   578 	};
       
   579 	static const DiagDirection _search_dir_2[] = {
       
   580 		DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
       
   581 	};
       
   582 
       
   583 	/* do not allow signal updates for two players in one run */
       
   584 	assert(_globset.IsEmpty() || owner == _last_owner);
       
   585 
       
   586 	_last_owner = owner;
       
   587 
       
   588 	_globset.Add(tile, _search_dir_1[track]);
       
   589 	_globset.Add(tile, _search_dir_2[track]);
       
   590 
       
   591 	if (_globset.Items() >= SIG_GLOB_UPDATE) {
       
   592 		/* too many items, force update */
       
   593 		UpdateSignalsInBuffer(_last_owner);
       
   594 		_last_owner = INVALID_OWNER;
       
   595 	}
       
   596 }
       
   597 
       
   598 
       
   599 /**
       
   600  * Add side of tile to signal update buffer
       
   601  *
       
   602  * @param tile tile where we start
       
   603  * @param side side of tile
       
   604  * @param owner owner whose signals we will update
       
   605  */
       
   606 void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
       
   607 {
       
   608 	/* do not allow signal updates for two players in one run */
       
   609 	assert(_globset.IsEmpty() || owner == _last_owner);
       
   610 
       
   611 	_last_owner = owner;
       
   612 
       
   613 	_globset.Add(tile, side);
       
   614 
       
   615 	if (_globset.Items() >= SIG_GLOB_UPDATE) {
       
   616 		/* too many items, force update */
       
   617 		UpdateSignalsInBuffer(_last_owner);
       
   618 		_last_owner = INVALID_OWNER;
       
   619 	}
       
   620 }
       
   621 
       
   622 /**
       
   623  * Update signals, starting at one side of a tile
       
   624  * Will check tile next to this at opposite side too
       
   625  *
       
   626  * @see UpdateSignalsInBuffer()
       
   627  * @param tile tile where we start
       
   628  * @param side side of tile
       
   629  * @param owner owner whose signals we will update
       
   630  * @return false iff train can leave depot
       
   631  */
       
   632 bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
       
   633 {
       
   634 	assert(_globset.IsEmpty());
       
   635 	_globset.Add(tile, side);
       
   636 
       
   637 	return UpdateSignalsInBuffer(owner);
       
   638 }
       
   639 
       
   640 
       
   641 /**
       
   642  * Update signals at segments that are at both ends of
       
   643  * given (existent or non-existent) track
       
   644  *
       
   645  * @see UpdateSignalsInBuffer()
       
   646  * @param tile tile where we start
       
   647  * @param track track at which ends we will update signals
       
   648  * @param owner owner whose signals we will update
       
   649  */
       
   650 void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
       
   651 {
       
   652 	assert(_globset.IsEmpty());
       
   653 
       
   654 	AddTrackToSignalBuffer(tile, track, owner);
       
   655 	UpdateSignalsInBuffer(owner);
       
   656 }