4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file signal.cpp functions related to rail signals updating */
15 #include "map/bridge.h"
16 #include "vehicle_func.h"
17 #include "viewport_func.h"
19 #include "company_base.h"
20 #include "signal_func.h"
21 #include "station_func.h"
22 #include "signal_map.h"
25 /** these are the maximums used for updating signal blocks */
26 static const uint SIG_TBU_SIZE
= 64; ///< number of signals entering to block
27 static const uint SIG_TBD_SIZE
= 256; ///< number of intersections - open nodes in current block
28 static const uint SIG_GLOB_SIZE
= 128; ///< number of open blocks (block can be opened more times until detected)
29 static const uint SIG_GLOB_UPDATE
= 64; ///< how many items need to be in _globset to force update
31 assert_compile(SIG_GLOB_UPDATE
<= SIG_GLOB_SIZE
);
33 /** incidating trackbits with given enterdir */
34 static const TrackBits _enterdir_to_trackbits
[DIAGDIR_END
] = {
41 /** incidating trackdirbits with given enterdir */
42 static const TrackdirBits _enterdir_to_trackdirbits
[DIAGDIR_END
] = {
43 TRACKDIR_BIT_X_SW
| TRACKDIR_BIT_UPPER_W
| TRACKDIR_BIT_RIGHT_S
,
44 TRACKDIR_BIT_Y_NW
| TRACKDIR_BIT_LOWER_W
| TRACKDIR_BIT_RIGHT_N
,
45 TRACKDIR_BIT_X_NE
| TRACKDIR_BIT_LOWER_E
| TRACKDIR_BIT_LEFT_N
,
46 TRACKDIR_BIT_Y_SE
| TRACKDIR_BIT_UPPER_E
| TRACKDIR_BIT_LEFT_S
50 * Set containing up to 'maxitems' items of 'T'
51 * No tree structure is used because it would cause
52 * slowdowns in most usual cases
54 template <typename T
, uint maxitems
>
57 uint n
; // actual number of units
58 bool overflowed
; // did we try to overflow the set?
59 const char *name
; // name, used for debugging purposes...
60 T data
[maxitems
]; // elements of set
63 /** Constructor - just set default values and 'name' */
64 SmallSet(const char *name
) : n(0), overflowed(false), name(name
) { }
66 /** Reset variables to default values */
70 this->overflowed
= false;
74 * Returns value of 'overflowed'
75 * @return did we try to overflow the set?
79 return this->overflowed
;
83 * Checks for empty set
84 * @return is the set empty?
93 * @return is the set full?
97 return this->n
== lengthof(data
);
101 * Reads the number of items
102 * @return current number of items
111 * Tries to remove first instance of given item
112 * @param item item to remove
113 * @return element was found and removed
115 bool Remove(const T
&item
)
117 for (uint i
= 0; i
< this->n
; i
++) {
118 if (this->data
[i
] == item
) {
119 this->data
[i
] = this->data
[--this->n
];
128 * Tries to find given item in the set
129 * @param item item to find
130 * @return true iff the item was found
132 bool IsIn(const T
&item
)
134 for (uint i
= 0; i
< this->n
; i
++) {
135 if (this->data
[i
] == item
) return true;
142 * Adds item into the set, checks for full set
143 * Sets the 'overflowed' flag if the set was full
144 * @param item item to add
145 * @return true iff the item could be added (set wasn't full)
147 bool Add(const T
&item
)
149 if (this->IsFull()) {
151 DEBUG(misc
, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name
, maxitems
);
152 return false; // set is full
155 this->data
[this->n
] = item
;
162 * Reads the last added element into the set
163 * @param item pointer where the item is written to
164 * @return false iff the set was empty
168 if (this->n
== 0) return false;
171 *item
= this->data
[this->n
];
182 bool operator == (const SignalPos
&other
) const {
183 return (tile
== other
.tile
) && (td
== other
.td
);
187 static SignalPos
SignalPosFrom(TileIndex tile
, Trackdir td
)
189 SignalPos pos
= { tile
, td
};
193 enum SignalSideEnum
{
194 SIDE_NE
= DIAGDIR_NE
,
195 SIDE_SE
= DIAGDIR_SE
,
196 SIDE_SW
= DIAGDIR_SW
,
197 SIDE_NW
= DIAGDIR_NW
,
205 /** Allow incrementing of SignalSideEnum variables */
206 DECLARE_POSTFIX_INCREMENT(SignalSideEnum
)
212 bool operator == (const SignalSide
&other
) const {
213 return (tile
== other
.tile
) && (side
== other
.side
);
217 static SignalSide
SignalSideFrom(TileIndex tile
, SignalSideEnum side
)
219 SignalSide ss
= { tile
, side
};
223 static SmallSet
<SignalPos
, SIG_TBU_SIZE
> _tbuset("_tbuset"); ///< set of signals that will be updated
224 static SmallSet
<SignalSide
, SIG_TBD_SIZE
> _tbdset("_tbdset"); ///< set of open nodes in current signal block
225 static SmallSet
<SignalSide
, SIG_GLOB_SIZE
> _globset("_globset"); ///< set of places to be updated in following runs
226 static Owner _owner
= INVALID_OWNER
; ///< owner of tracks in _globset, or INVALID_OWNER if empty
229 /** Check if there is a train on a tile, not in a depot. */
230 static bool HasTrainOnTile (TileIndex tile
)
232 VehicleTileFinder
iter (tile
);
233 while (!iter
.finished()) {
234 Vehicle
*v
= iter
.next();
235 if (v
->type
== VEH_TRAIN
&& Train::From(v
)->trackdir
!= TRACKDIR_DEPOT
) {
239 return iter
.was_found();
244 * Perform some operations before adding data into Todo set
245 * The new and reverse direction is removed from Global set, because we are sure
246 * it doesn't need to be checked again
247 * Also, remove reverse direction from Todo set
248 * This is the 'core' part so the graph searching won't enter any tile twice
250 * @param ss1 tile and side we are entering
251 * @param ss2 tile and side we are leaving
252 * @return false iff the Todo buffer would be overrun
254 static inline bool MaybeAddToTodoSet(const SignalSide
&ss1
, const SignalSide
&ss2
)
256 _globset
.Remove(ss1
); // it can be in Global but not in Todo
257 _globset
.Remove(ss2
); // remove in all cases
259 assert(!_tbdset
.IsIn(ss1
)); // it really shouldn't be there already
261 if (_tbdset
.Remove(ss2
)) return true;
263 return _tbdset
.Add(ss1
);
267 /** Current signal block state flags */
270 SF_TRAIN
= 1 << 0, ///< train found in segment
271 SF_EXIT
= 1 << 1, ///< exitsignal found
272 SF_EXIT2
= 1 << 2, ///< two or more exits found
273 SF_GREEN
= 1 << 3, ///< green exitsignal found
274 SF_GREEN2
= 1 << 4, ///< two or more green exits found
275 SF_FULL
= 1 << 5, ///< some of buffers was full, do not continue
276 SF_PBS
= 1 << 6, ///< pbs signal found
279 DECLARE_ENUM_AS_BIT_SET(SigFlags
)
283 * Search signal block
285 * @param owner owner whose signals we are updating
288 static SigFlags
ExploreSegment(Owner owner
)
290 SigFlags flags
= SF_NONE
;
294 while (_tbdset
.Get(&ss
)) {
296 newss
.tile
= ss
.tile
; // tile we will enter
298 switch (GetTileType(ss
.tile
)) {
300 if (GetTileOwner(ss
.tile
) != owner
) continue; // do not propagate signals on others' tiles (remove for tracksharing)
302 if (IsTileSubtype(ss
.tile
, TT_BRIDGE
)) {
303 DiagDirection dir
= GetTunnelBridgeDirection(ss
.tile
);
304 if (ss
.side
== (SignalSideEnum
)dir
) continue;
305 if (ss
.side
== SIDE_INTO_BRIDGE
) {
306 newss
.tile
= GetOtherBridgeEnd(ss
.tile
); // skip to exit tile
307 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTunnelBridgeMiddle(ss
.tile
, newss
.tile
).Failed()) flags
|= SF_TRAIN
;
308 newss
.side
= SIDE_FROM_BRIDGE
;
309 ss
.tile
= newss
.tile
;
310 ss
.side
= SIDE_INTO_BRIDGE
;
313 if (ss
.side
== SIDE_FROM_BRIDGE
) ss
.side
= (SignalSideEnum
)dir
;
316 assert(IsValidDiagDirection((DiagDirection
)ss
.side
));
318 TrackBits tracks
= GetTrackBits(ss
.tile
); // trackbits of tile
319 TrackBits tracks_masked
= (TrackBits
)(tracks
& _enterdir_to_trackbits
[ss
.side
]); // only incidating trackbits
321 if (tracks
== TRACK_BIT_HORZ
|| tracks
== TRACK_BIT_VERT
) { // there is exactly one incidating track, no need to check
322 tracks
= tracks_masked
;
324 if (tracks_masked
== TRACK_BIT_NONE
) continue; // no incidating track
327 assert((tracks_masked
& ~tracks
) == TRACK_BIT_NONE
); // tracks_masked must be a subset of tracks
328 assert(tracks_masked
!= TRACK_BIT_NONE
);
329 assert(tracks_masked
!= TRACK_BIT_HORZ
);
330 assert(tracks_masked
!= TRACK_BIT_VERT
);
331 assert(tracks
!= TRACK_BIT_HORZ
);
332 assert(tracks
!= TRACK_BIT_VERT
);
334 if (HasAtMostOneBit(tracks
)) { // only one track
335 Track track
= TrackBitsToTrack(tracks
); // get the only track
336 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTrackBits(ss
.tile
, tracks
).Failed()) flags
|= SF_TRAIN
;
338 // tile can only have signals if it only has one bit
339 if (HasSignalOnTrack(ss
.tile
, track
)) { // now check whole track, not trackdir
340 SignalType sig
= GetSignalType(ss
.tile
, track
);
341 Trackdir trackdir
= FindFirstTrackdir(TrackBitsToTrackdirBits(tracks
) & _enterdir_to_trackdirbits
[ss
.side
]);
342 Trackdir reversedir
= ReverseTrackdir(trackdir
);
343 /* add (tile, reversetrackdir) to 'to-be-updated' set when there is
344 * ANY conventional signal in REVERSE direction
345 * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
346 if (HasSignalOnTrackdir(ss
.tile
, reversedir
)) {
347 if (IsPbsSignal(sig
)) {
349 } else if (!_tbuset
.Add(SignalPosFrom(ss
.tile
, reversedir
))) {
350 return flags
| SF_FULL
;
353 if (HasSignalOnTrackdir(ss
.tile
, trackdir
) && !IsOnewaySignal(sig
)) flags
|= SF_PBS
;
355 /* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
356 if (!(flags
& SF_GREEN2
) && IsPresignalExit(sig
) && HasSignalOnTrackdir(ss
.tile
, trackdir
)) { // found presignal exit
357 if (flags
& SF_EXIT
) flags
|= SF_EXIT2
; // found two (or more) exits
358 flags
|= SF_EXIT
; // found at least one exit - allow for compiler optimizations
359 if (GetSignalStateByTrackdir(ss
.tile
, trackdir
) == SIGNAL_STATE_GREEN
) { // found green presignal exit
360 if (flags
& SF_GREEN
) flags
|= SF_GREEN2
;
367 } else { // tile has overlapping tracks
368 if (!(flags
& SF_TRAIN
) && HasTrainOnTile(ss
.tile
)) flags
|= SF_TRAIN
;
371 SignalSideEnum enterdir
= ss
.side
;
372 for (ss
.side
= (SignalSideEnum
)DIAGDIR_BEGIN
; ss
.side
< (SignalSideEnum
)DIAGDIR_END
; ss
.side
++) { // test all possible exit directions
373 if (ss
.side
!= enterdir
&& (tracks
& _enterdir_to_trackbits
[ss
.side
])) { // any track incidating?
374 if (IsTileSubtype(ss
.tile
, TT_BRIDGE
) && (ss
.side
== (SignalSideEnum
)GetTunnelBridgeDirection(ss
.tile
))) {
375 newss
.tile
= ss
.tile
;
376 newss
.side
= SIDE_INTO_BRIDGE
;
377 if (!MaybeAddToTodoSet(newss
, SignalSideFrom(ss
.tile
, SIDE_FROM_BRIDGE
))) return flags
| SF_FULL
;
379 newss
.tile
= ss
.tile
+ TileOffsByDiagDir((DiagDirection
)ss
.side
); // new tile to check
380 newss
.side
= (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
); // direction we are entering from
381 if (!MaybeAddToTodoSet(newss
, ss
)) return flags
| SF_FULL
;
386 continue; // continue the while() loop
390 if (GetTileOwner(ss
.tile
) != owner
) continue;
392 switch (GetTileSubtype(ss
.tile
)) {
395 case TT_MISC_CROSSING
:
396 assert(IsValidDiagDirection((DiagDirection
)ss
.side
));
397 if (DiagDirToAxis((DiagDirection
)ss
.side
) == GetCrossingRoadAxis(ss
.tile
)) continue; // different axis
398 if (!(flags
& SF_TRAIN
) && HasTrainOnTile(ss
.tile
)) flags
|= SF_TRAIN
;
399 newss
.side
= ss
.side
;
400 ss
.side
= (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
); // exitdir
401 newss
.tile
+= TileOffsByDiagDir((DiagDirection
)ss
.side
);
404 case TT_MISC_TUNNEL
: {
405 if (GetTunnelTransportType(ss
.tile
) != TRANSPORT_RAIL
) continue;
406 DiagDirection dir
= GetTunnelBridgeDirection(ss
.tile
);
408 if (ss
.side
== SIDE_INTO_TUNNEL
) { // going into the wormhole
409 newss
.tile
= GetOtherTunnelEnd(ss
.tile
); // skip to exit tile
410 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTunnelBridgeMiddle(ss
.tile
, newss
.tile
).Failed()) flags
|= SF_TRAIN
;
411 newss
.side
= SIDE_FROM_TUNNEL
;
412 ss
.tile
= newss
.tile
;
413 ss
.side
= SIDE_INTO_TUNNEL
;
414 } else if (ss
.side
== SIDE_FROM_TUNNEL
) { // incoming from the wormhole
415 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTrackBits(ss
.tile
, TRACK_BIT_ALL
).Failed()) flags
|= SF_TRAIN
;
416 if (maptile_has_tunnel_signals(ss
.tile
)) {
417 /* Only one-way signals supported in tunnels. */
418 assert(maptile_has_tunnel_signal(ss
.tile
, true) != maptile_has_tunnel_signal(ss
.tile
, false));
419 if (maptile_has_tunnel_signal(ss
.tile
, true)) {
420 /* Only normal signals supported into tunnels. */
421 assert(maptile_get_tunnel_signal_type(ss
.tile
) == SIGTYPE_NORMAL
);
422 if (!_tbuset
.Add(SignalPosFrom(ss
.tile
, DiagDirToDiagTrackdir(dir
)))) {
423 return flags
| SF_FULL
;
428 ss
.side
= (SignalSideEnum
)ReverseDiagDir(dir
); // exitdir
429 newss
.tile
+= TileOffsByDiagDir((DiagDirection
)ss
.side
); // just skip to next tile
430 newss
.side
= (SignalSideEnum
)dir
;
431 } else { // neither into nor from the wormhole
432 assert(IsValidDiagDirection((DiagDirection
)ss
.side
));
433 if (ss
.side
!= (SignalSideEnum
)ReverseDiagDir(dir
)) continue;
434 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTrackBits(ss
.tile
, TRACK_BIT_ALL
).Failed()) flags
|= SF_TRAIN
;
435 if (maptile_has_tunnel_signals(ss
.tile
)) {
436 /* Only one-way signals supported in tunnels. */
437 assert(maptile_has_tunnel_signal(ss
.tile
, true) != maptile_has_tunnel_signal(ss
.tile
, false));
438 if (maptile_has_tunnel_signal(ss
.tile
, false)) {
439 SignalType sig
= maptile_get_tunnel_signal_type(ss
.tile
);
440 /* Only normal and one-way path signals supported in tunnels. */
441 assert(sig
== SIGTYPE_NORMAL
|| sig
== SIGTYPE_PBS_ONEWAY
);
442 if (sig
!= SIGTYPE_NORMAL
) {
444 } else if (!_tbuset
.Add(SignalPosFrom(ss
.tile
, DiagDirToDiagTrackdir(ReverseDiagDir(dir
))))) {
445 return flags
| SF_FULL
;
450 ss
.side
= SIDE_FROM_TUNNEL
;
451 newss
.tile
= ss
.tile
;
452 newss
.side
= SIDE_INTO_TUNNEL
;
458 if (!IsRailDepot(ss
.tile
)) continue;
459 if (ss
.side
== SIDE_DEPOT
) { // from 'inside' - train just entered or left the depot
460 if (!(flags
& SF_TRAIN
) && HasTrainOnTile(ss
.tile
)) flags
|= SF_TRAIN
;
461 ss
.side
= (SignalSideEnum
)GetGroundDepotDirection(ss
.tile
); // exitdir
462 newss
.tile
+= TileOffsByDiagDir((DiagDirection
)ss
.side
);
463 newss
.side
= (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
);
465 } else if (ss
.side
== (SignalSideEnum
)GetGroundDepotDirection(ss
.tile
)) { // entered a depot
466 if (!(flags
& SF_TRAIN
) && HasTrainOnTile(ss
.tile
)) flags
|= SF_TRAIN
;
473 assert(IsValidDiagDirection((DiagDirection
)ss
.side
));
475 if (!HasStationRail(ss
.tile
)) continue;
476 if (GetTileOwner(ss
.tile
) != owner
) continue;
477 if (DiagDirToAxis((DiagDirection
)ss
.side
) != GetRailStationAxis(ss
.tile
)) continue; // different axis
478 if (IsStationTileBlocked(ss
.tile
)) continue; // 'eye-candy' station tile
480 if (!(flags
& SF_TRAIN
) && HasTrainOnTile(ss
.tile
)) flags
|= SF_TRAIN
;
481 newss
.side
= ss
.side
;
482 ss
.side
= (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
); // exitdir
483 newss
.tile
+= TileOffsByDiagDir((DiagDirection
)ss
.side
);
487 continue; // continue the while() loop
490 if (!MaybeAddToTodoSet(newss
, ss
)) return flags
| SF_FULL
;
498 * Determine the state for a signal heading into a tunnel when there is a train in it
499 * @param tile the tunnel tile
500 * @return the signal state to set
502 static SignalState
GetTunnelSignalState(TileIndex tile
)
504 assert(maptile_is_rail_tunnel(tile
));
506 /* signal is red if there is a train on the initial tile */
507 if (HasTrainOnTile(tile
)) return SIGNAL_STATE_RED
;
509 /* otherwise, signal is red iff there is a train near the entry */
510 TileIndex tile2
= TileAddByDiagDir(tile
, GetTunnelBridgeDirection(tile
));
511 VehicleTileFinder
iter (GetOtherTunnelEnd(tile
));
512 while (!iter
.finished()) {
513 Vehicle
*v
= iter
.next();
514 if (v
->type
== VEH_TRAIN
&& TileVirtXY(v
->x_pos
, v
->y_pos
) == tile2
) iter
.set_found();
516 return iter
.was_found() ? SIGNAL_STATE_RED
: SIGNAL_STATE_GREEN
;
520 * Update signals around segment in _tbuset
522 * @param flags info about segment
524 static void UpdateSignalsAroundSegment(SigFlags flags
)
528 while (_tbuset
.Get(&pos
)) {
529 if (!IsRailwayTile(pos
.tile
)) {
530 /* Special signals */
531 assert(maptile_is_rail_tunnel(pos
.tile
));
532 assert(maptile_get_tunnel_signal_type(pos
.tile
) == SIGTYPE_NORMAL
);
534 bool inwards
= pos
.td
== DiagDirToDiagTrackdir(GetTunnelBridgeDirection(pos
.tile
));
535 SignalState newstate
= !(flags
& SF_TRAIN
) ? SIGNAL_STATE_GREEN
:
536 inwards
? GetTunnelSignalState(pos
.tile
) : SIGNAL_STATE_RED
;
538 if (newstate
!= maptile_get_tunnel_signal_state(pos
.tile
, inwards
)) {
539 maptile_set_tunnel_signal_state(pos
.tile
, inwards
, newstate
);
540 MarkTileDirtyByTile(pos
.tile
);
546 assert(HasSignalOnTrackdir(pos
.tile
, pos
.td
));
548 SignalType sig
= GetSignalType(pos
.tile
, TrackdirToTrack(pos
.td
));
549 SignalState newstate
= SIGNAL_STATE_GREEN
;
551 /* determine whether the new state is red */
552 if (flags
& SF_TRAIN
) {
553 /* train in the segment */
554 newstate
= SIGNAL_STATE_RED
;
556 /* is it a bidir combo? - then do not count its other signal direction as exit */
557 if (sig
== SIGTYPE_COMBO
&& HasSignalOnTrackdir(pos
.tile
, ReverseTrackdir(pos
.td
))) {
558 /* at least one more exit */
559 if ((flags
& SF_EXIT2
) &&
561 (!(flags
& SF_GREEN
) ||
562 /* only one green exit, and it is this one - so all other exits are red */
563 (!(flags
& SF_GREEN2
) && GetSignalStateByTrackdir(pos
.tile
, ReverseTrackdir(pos
.td
)) == SIGNAL_STATE_GREEN
))) {
564 newstate
= SIGNAL_STATE_RED
;
566 } else { // entry, at least one exit, no green exit
567 if (IsPresignalEntry(sig
) && (flags
& SF_EXIT
) && !(flags
& SF_GREEN
)) newstate
= SIGNAL_STATE_RED
;
571 /* only when the state changes */
572 if (newstate
!= GetSignalStateByTrackdir(pos
.tile
, pos
.td
)) {
573 if (IsPresignalExit(sig
)) {
574 /* for pre-signal exits, add block to the global set */
575 DiagDirection exitdir
= TrackdirToExitdir(ReverseTrackdir(pos
.td
));
577 if (IsRailBridgeTile(pos
.tile
)) {
578 side
= (exitdir
== GetTunnelBridgeDirection(pos
.tile
)) ? SIDE_FROM_BRIDGE
: (SignalSideEnum
)exitdir
;
579 } else if (IsTunnelTile(pos
.tile
)) {
580 side
= (exitdir
== GetTunnelBridgeDirection(pos
.tile
)) ? SIDE_FROM_TUNNEL
: (SignalSideEnum
)exitdir
;
582 side
= (SignalSideEnum
)exitdir
;
584 _globset
.Add(SignalSideFrom(pos
.tile
, side
)); // do not check for full global set, first update all signals
586 SetSignalStateByTrackdir(pos
.tile
, pos
.td
, newstate
);
587 MarkTileDirtyByTile(pos
.tile
);
594 /** Reset all sets after one set overflowed */
595 static inline void ResetSets()
604 * Updates blocks in _globset buffer
606 * @param owner company whose signals we are updating
607 * @return state of the first block from _globset
608 * @pre _globset.IsEmpty() || Company::IsValidID(_owner)
610 SigSegState
UpdateSignalsInBuffer()
612 assert(_globset
.IsEmpty() || Company::IsValidID(_owner
));
614 SigSegState state
= SIGSEG_NONE
; // value to return
618 while (_globset
.Get(&ss
)) {
619 assert(_tbuset
.IsEmpty());
620 assert(_tbdset
.IsEmpty());
622 /* After updating signal, data stored are always railway with signals.
623 * Other situations happen when data are from outside functions -
624 * modification of railbits (including both rail building and removal),
625 * train entering/leaving block, train leaving depot...
627 switch (GetTileType(ss
.tile
)) {
629 if (IsTileSubtype(ss
.tile
, TT_TRACK
)) {
630 /* check if there was something here that got deleted */
631 if (!IsValidDiagDirection((DiagDirection
)ss
.side
)) continue;
634 assert(ss
.side
!= (SignalSideEnum
)GetTunnelBridgeDirection(ss
.tile
));
635 if (IsValidDiagDirection((DiagDirection
)ss
.side
)) goto check_track
;
636 assert(ss
.side
== SIDE_INTO_BRIDGE
|| ss
.side
== SIDE_FROM_BRIDGE
);
637 _tbdset
.Add(SignalSideFrom(ss
.tile
, SIDE_INTO_BRIDGE
));
638 _tbdset
.Add(SignalSideFrom(ss
.tile
, SIDE_FROM_BRIDGE
));
642 if (IsTunnelTile(ss
.tile
)) {
643 /* 'optimization assert' - do not try to update signals when it is not needed */
644 assert(GetTunnelTransportType(ss
.tile
) == TRANSPORT_RAIL
);
645 if (IsValidDiagDirection((DiagDirection
)ss
.side
)) {
646 assert(ss
.side
== (SignalSideEnum
)ReverseDiagDir(GetTunnelBridgeDirection(ss
.tile
)));
649 assert(ss
.side
== SIDE_INTO_TUNNEL
|| SIDE_FROM_TUNNEL
);
650 _tbdset
.Add(SignalSideFrom(ss
.tile
, SIDE_INTO_TUNNEL
));
651 _tbdset
.Add(SignalSideFrom(ss
.tile
, SIDE_FROM_TUNNEL
));
655 if (IsRailDepotTile(ss
.tile
)) {
656 /* 'optimization assert' do not try to update signals in other cases */
657 assert(ss
.side
== SIDE_DEPOT
|| ss
.side
== (SignalSideEnum
)GetGroundDepotDirection(ss
.tile
));
658 _tbdset
.Add(SignalSideFrom(ss
.tile
, SIDE_DEPOT
)); // start from depot inside
661 if (!IsLevelCrossingTile(ss
.tile
)) goto next_tile
;
665 assert(IsValidDiagDirection((DiagDirection
)ss
.side
));
666 if ((TrackStatusToTrackBits(GetTileRailwayStatus(ss
.tile
)) & _enterdir_to_trackbits
[ss
.side
]) != TRACK_BIT_NONE
) {
667 /* only add to set when there is some 'interesting' track */
669 _tbdset
.Add(SignalSideFrom(ss
.tile
+ TileOffsByDiagDir((DiagDirection
)ss
.side
), (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
)));
674 /* jump to next tile */
676 /* check if there was a bridge here but got deleted */
677 if (!IsValidDiagDirection((DiagDirection
)ss
.side
)) continue;
678 ss
.tile
= ss
.tile
+ TileOffsByDiagDir((DiagDirection
)ss
.side
);
679 ss
.side
= (SignalSideEnum
)ReverseDiagDir((DiagDirection
)ss
.side
);
680 if ((TrackStatusToTrackBits(GetTileRailwayStatus(ss
.tile
)) & _enterdir_to_trackbits
[ss
.side
]) != TRACK_BIT_NONE
) {
684 /* happens when removing a rail that wasn't connected at one or both sides */
685 continue; // continue the while() loop
688 assert(!_tbdset
.Overflowed()); // it really shouldn't overflow by these one or two items
689 assert(!_tbdset
.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
691 SigFlags flags
= ExploreSegment(_owner
);
693 if (state
== SIGSEG_NONE
) {
694 if (flags
& SF_PBS
) {
696 } else if ((flags
& SF_TRAIN
) || ((flags
& SF_EXIT
) && !(flags
& SF_GREEN
)) || (flags
& SF_FULL
)) {
703 /* do not do anything when some buffer was full */
704 if (flags
& SF_FULL
) {
705 ResetSets(); // free all sets
709 UpdateSignalsAroundSegment(flags
);
712 _owner
= INVALID_OWNER
;
719 * Check if signal buffer is empty
720 * @returns whether the signal buffer is empty
722 bool IsSignalBufferEmpty()
724 return _globset
.IsEmpty();
728 * Set signal buffer owner
730 static inline void SetBufferOwner(Owner owner
)
732 /* do not allow signal updates for two companies in one run */
733 assert(_globset
.IsEmpty() || owner
== _owner
);
738 * Update signals in buffer if it has too many items
740 static inline void UpdateSignalsInBufferAuto()
742 if (_globset
.Items() >= SIG_GLOB_UPDATE
) {
743 /* too many items, force update */
744 UpdateSignalsInBuffer();
750 * Add track to signal update buffer
752 * @param tile tile where we start
753 * @param track track at which ends we will update signals
754 * @param owner owner whose signals we will update
756 void AddTrackToSignalBuffer(TileIndex tile
, Track track
, Owner owner
)
758 SetBufferOwner(owner
);
760 if (IsRailBridgeTile(tile
)) {
761 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
763 side
= TrackdirToExitdir(TrackToTrackdir(track
));
764 _globset
.Add(SignalSideFrom(tile
, (side
== dir
) ? SIDE_FROM_BRIDGE
: (SignalSideEnum
)side
));
765 side
= TrackdirToExitdir(ReverseTrackdir(TrackToTrackdir(track
)));
766 _globset
.Add(SignalSideFrom(tile
, (side
== dir
) ? SIDE_FROM_BRIDGE
: (SignalSideEnum
)side
));
767 } else if (IsTunnelTile(tile
)) {
768 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
769 assert(track
== DiagDirToDiagTrack(dir
));
770 _globset
.Add(SignalSideFrom(tile
, (SignalSideEnum
)ReverseDiagDir(dir
)));
771 _globset
.Add(SignalSideFrom(tile
, SIDE_FROM_TUNNEL
));
773 _globset
.Add(SignalSideFrom(tile
, (SignalSideEnum
)TrackdirToExitdir(TrackToTrackdir(track
))));
774 _globset
.Add(SignalSideFrom(tile
, (SignalSideEnum
)TrackdirToExitdir(ReverseTrackdir(TrackToTrackdir(track
)))));
777 UpdateSignalsInBufferAuto();
782 * Add side of tile to signal update buffer
784 * @param tile tile where we start
785 * @param side side of tile
786 * @param owner owner whose signals we will update
788 void AddSideToSignalBuffer(TileIndex tile
, DiagDirection side
, Owner owner
)
790 SetBufferOwner(owner
);
792 assert(IsValidDiagDirection(side
));
793 _globset
.Add(SignalSideFrom(tile
, (SignalSideEnum
)side
));
795 UpdateSignalsInBufferAuto();
799 * Add depot tile to signal update buffer
801 * @param tile tile to add
802 * @param owner owner whose signals we will update
804 void AddDepotToSignalBuffer(TileIndex tile
, Owner owner
)
806 SetBufferOwner(owner
);
808 assert(IsDepotTile(tile
));
809 _globset
.Add(SignalSideFrom(tile
, SIDE_DEPOT
));
811 UpdateSignalsInBufferAuto();
815 * Add bridge tile to signal update buffer
817 * @param tile tile to add
818 * @param owner owner whose signals we will update
820 void AddBridgeToSignalBuffer(TileIndex tile
, Owner owner
)
822 SetBufferOwner(owner
);
824 assert(IsRailBridgeTile(tile
));
825 _globset
.Add(SignalSideFrom(tile
, SIDE_FROM_BRIDGE
));
827 UpdateSignalsInBufferAuto();
831 * Add tunnel tile to signal update buffer
833 * @param tile tile to add
834 * @param owner owner whose signals we will update
836 void AddTunnelToSignalBuffer(TileIndex tile
, Owner owner
)
838 SetBufferOwner(owner
);
840 assert(maptile_is_rail_tunnel(tile
));
841 _globset
.Add(SignalSideFrom(tile
, SIDE_FROM_TUNNEL
));
843 UpdateSignalsInBufferAuto();
848 * Add a pathfinder position to signal update buffer
850 * @param pos position to add
851 * @param owner owner whose signals we will update
853 void AddPosToSignalBuffer(const RailPathPos
&pos
, Owner owner
)
855 SetBufferOwner(owner
);
857 if (pos
.in_wormhole()) {
858 _globset
.Add(SignalSideFrom(pos
.wormhole
, IsRailwayTile(pos
.wormhole
) ? SIDE_INTO_BRIDGE
: SIDE_INTO_TUNNEL
));
859 } else if (IsRailDepotTile(pos
.tile
)) {
860 _globset
.Add(SignalSideFrom(pos
.tile
, SIDE_DEPOT
));
862 DiagDirection exitdir
= TrackdirToExitdir(pos
.td
);
864 if (IsRailBridgeTile(pos
.tile
)) {
865 side
= (exitdir
== GetTunnelBridgeDirection(pos
.tile
)) ? SIDE_FROM_BRIDGE
: (SignalSideEnum
)exitdir
;
866 } else if (IsTunnelTile(pos
.tile
)) {
867 side
= (exitdir
== GetTunnelBridgeDirection(pos
.tile
)) ? SIDE_FROM_TUNNEL
: (SignalSideEnum
)exitdir
;
869 side
= (SignalSideEnum
)exitdir
;
871 _globset
.Add(SignalSideFrom(pos
.tile
, side
));
874 UpdateSignalsInBufferAuto();