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 */
14 #include "station_map.h"
15 #include "tunnelbridge_map.h"
16 #include "vehicle_func.h"
17 #include "viewport_func.h"
19 #include "company_base.h"
20 #include "logic_signals.h"
22 #include "safeguards.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 'items' items of 'tile and Tdir'
51 * No tree structure is used because it would cause
52 * slowdowns in most usual cases
54 template <typename Tdir
, uint items
>
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...
68 /** Constructor - just set default values and 'name' */
69 SmallSet(const char *name
) : n(0), overflowed(false), name(name
) { }
71 /** Reset variables to default values */
75 this->overflowed
= false;
79 * Returns value of 'overflowed'
80 * @return did we try to overflow the set?
84 return this->overflowed
;
88 * Checks for empty set
89 * @return is the set empty?
98 * @return is the set full?
102 return this->n
== lengthof(data
);
106 * Reads the number of items
107 * @return current number of items
116 * Tries to remove first instance of given tile and dir
118 * @param dir and dir to remove
119 * @return element was found and removed
121 bool Remove(TileIndex tile
, Tdir dir
)
123 for (uint i
= 0; i
< this->n
; i
++) {
124 if (this->data
[i
].tile
== tile
&& this->data
[i
].dir
== dir
) {
125 this->data
[i
] = this->data
[--this->n
];
134 * Tries to find given tile and dir in the set
136 * @param dir and dir to find
137 * @return true iff the tile & dir element was found
139 bool IsIn(TileIndex tile
, Tdir dir
)
141 for (uint i
= 0; i
< this->n
; i
++) {
142 if (this->data
[i
].tile
== tile
&& this->data
[i
].dir
== dir
) return true;
149 * Adds tile & dir into the set, checks for full set
150 * Sets the 'overflowed' flag if the set was full
152 * @param dir and dir to add
153 * @return true iff the item could be added (set wasn't full)
155 bool Add(TileIndex tile
, Tdir dir
)
157 if (this->IsFull()) {
159 DEBUG(misc
, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name
, items
);
160 return false; // set is full
163 this->data
[this->n
].tile
= tile
;
164 this->data
[this->n
].dir
= dir
;
171 * Reads the last added element into the set
172 * @param tile pointer where tile is written to
173 * @param dir pointer where dir is written to
174 * @return false iff the set was empty
176 bool Get(TileIndex
*tile
, Tdir
*dir
)
178 if (this->n
== 0) return false;
181 *tile
= this->data
[this->n
].tile
;
182 *dir
= this->data
[this->n
].dir
;
188 static SmallSet
<Trackdir
, SIG_TBU_SIZE
> _tbuset("_tbuset"); ///< set of signals that will be updated
189 static SmallSet
<DiagDirection
, SIG_TBD_SIZE
> _tbdset("_tbdset"); ///< set of open nodes in current signal block
190 static SmallSet
<DiagDirection
, SIG_GLOB_SIZE
> _globset("_globset"); ///< set of places to be updated in following runs
193 /** Check whether there is a train on rail, not in a depot */
194 static Vehicle
*TrainOnTileEnum(Vehicle
*v
, void *)
196 if (v
->type
!= VEH_TRAIN
|| Train::From(v
)->track
== TRACK_BIT_DEPOT
) return nullptr;
201 /** Check whether there is a train only on ramp. */
202 static Vehicle
*TrainInWormholeTileEnum(Vehicle
*v
, void *data
)
204 /* Only look for front engine or last wagon. */
205 if (v
->type
!= VEH_TRAIN
|| (v
->Previous() != nullptr && v
->Next() != nullptr)) return nullptr;
206 if (*(TileIndex
*)data
!= TileVirtXY(v
->x_pos
, v
->y_pos
)) return nullptr;
211 * Perform some operations before adding data into Todo set
212 * The new and reverse direction is removed from _globset, because we are sure
213 * it doesn't need to be checked again
214 * Also, remove reverse direction from _tbdset
215 * This is the 'core' part so the graph searching won't enter any tile twice
217 * @param t1 tile we are entering
218 * @param d1 direction (tile side) we are entering
219 * @param t2 tile we are leaving
220 * @param d2 direction (tile side) we are leaving
221 * @return false iff reverse direction was in Todo set
223 static inline bool CheckAddToTodoSet(TileIndex t1
, DiagDirection d1
, TileIndex t2
, DiagDirection d2
)
225 _globset
.Remove(t1
, d1
); // it can be in Global but not in Todo
226 _globset
.Remove(t2
, d2
); // remove in all cases
228 assert(!_tbdset
.IsIn(t1
, d1
)); // it really shouldn't be there already
230 if (_tbdset
.Remove(t2
, d2
)) return false;
237 * Perform some operations before adding data into Todo set
238 * The new and reverse direction is removed from Global set, because we are sure
239 * it doesn't need to be checked again
240 * Also, remove reverse direction from Todo set
241 * This is the 'core' part so the graph searching won't enter any tile twice
243 * @param t1 tile we are entering
244 * @param d1 direction (tile side) we are entering
245 * @param t2 tile we are leaving
246 * @param d2 direction (tile side) we are leaving
247 * @return false iff the Todo buffer would be overrun
249 static inline bool MaybeAddToTodoSet(TileIndex t1
, DiagDirection d1
, TileIndex t2
, DiagDirection d2
)
251 if (!CheckAddToTodoSet(t1
, d1
, t2
, d2
)) return true;
253 return _tbdset
.Add(t1
, d1
);
257 /** Current signal block state flags */
260 SF_TRAIN
= 1 << 0, ///< train found in segment
261 SF_EXIT
= 1 << 1, ///< exitsignal found
262 SF_EXIT2
= 1 << 2, ///< two or more exits found
263 SF_GREEN
= 1 << 3, ///< green exitsignal found
264 SF_GREEN2
= 1 << 4, ///< two or more green exits found
265 SF_FULL
= 1 << 5, ///< some of buffers was full, do not continue
266 SF_PBS
= 1 << 6, ///< pbs signal found
269 DECLARE_ENUM_AS_BIT_SET(SigFlags
)
273 * Search signal block
275 * @param owner owner whose signals we are updating
278 static SigFlags
ExploreSegment(Owner owner
)
280 SigFlags flags
= SF_NONE
;
283 DiagDirection enterdir
;
285 while (_tbdset
.Get(&tile
, &enterdir
)) {
286 TileIndex oldtile
= tile
; // tile we are leaving
287 DiagDirection exitdir
= enterdir
== INVALID_DIAGDIR
? INVALID_DIAGDIR
: ReverseDiagDir(enterdir
); // expected new exit direction (for straight line)
289 switch (GetTileType(tile
)) {
291 if (GetTileOwner(tile
) != owner
) continue; // do not propagate signals on others' tiles (remove for tracksharing)
293 if (IsRailDepot(tile
)) {
294 if (enterdir
== INVALID_DIAGDIR
) { // from 'inside' - train just entered or left the depot
295 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
296 exitdir
= GetRailDepotDirection(tile
);
297 tile
+= TileOffsByDiagDir(exitdir
);
298 enterdir
= ReverseDiagDir(exitdir
);
300 } else if (enterdir
== GetRailDepotDirection(tile
)) { // entered a depot
301 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
308 assert(IsValidDiagDirection(enterdir
));
309 TrackBits tracks
= GetTrackBits(tile
); // trackbits of tile
310 TrackBits tracks_masked
= (TrackBits
)(tracks
& _enterdir_to_trackbits
[enterdir
]); // only incidating trackbits
312 if (tracks
== TRACK_BIT_HORZ
|| tracks
== TRACK_BIT_VERT
) { // there is exactly one incidating track, no need to check
313 tracks
= tracks_masked
;
314 /* If no train detected yet, and there is not no train -> there is a train -> set the flag */
315 if (!(flags
& SF_TRAIN
) && EnsureNoTrainOnTrackBits(tile
, tracks
).Failed()) flags
|= SF_TRAIN
;
317 if (tracks_masked
== TRACK_BIT_NONE
) continue; // no incidating track
318 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
321 if (HasSignals(tile
)) { // there is exactly one track - not zero, because there is exit from this tile
322 Track track
= TrackBitsToTrack(tracks_masked
); // mask TRACK_BIT_X and Y too
323 if (HasSignalOnTrack(tile
, track
)) { // now check whole track, not trackdir
324 SignalType sig
= GetSignalType(tile
, track
);
325 Trackdir trackdir
= (Trackdir
)FindFirstBit((tracks
* 0x101) & _enterdir_to_trackdirbits
[enterdir
]);
326 Trackdir reversedir
= ReverseTrackdir(trackdir
);
327 /* add (tile, reversetrackdir) to 'to-be-updated' set when there is
328 * ANY conventional signal in REVERSE direction
329 * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
330 if (HasSignalOnTrackdir(tile
, reversedir
)) {
331 if (IsPbsSignal(sig
)) {
333 } else if (!_tbuset
.Add(tile
, reversedir
)) {
334 return flags
| SF_FULL
;
337 if (HasSignalOnTrackdir(tile
, trackdir
) && !IsOnewaySignal(tile
, track
)) flags
|= SF_PBS
;
339 /* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
340 if (!(flags
& SF_GREEN2
) && IsPresignalExit(tile
, track
) && HasSignalOnTrackdir(tile
, trackdir
)) { // found presignal exit
341 if (flags
& SF_EXIT
) flags
|= SF_EXIT2
; // found two (or more) exits
342 flags
|= SF_EXIT
; // found at least one exit - allow for compiler optimizations
343 if (GetSignalStateByTrackdir(tile
, trackdir
) == SIGNAL_STATE_GREEN
) { // found green presignal exit
344 if (flags
& SF_GREEN
) flags
|= SF_GREEN2
;
353 for (DiagDirection dir
= DIAGDIR_BEGIN
; dir
< DIAGDIR_END
; dir
++) { // test all possible exit directions
354 if (dir
!= enterdir
&& (tracks
& _enterdir_to_trackbits
[dir
])) { // any track incidating?
355 TileIndex newtile
= tile
+ TileOffsByDiagDir(dir
); // new tile to check
356 DiagDirection newdir
= ReverseDiagDir(dir
); // direction we are entering from
357 if (!MaybeAddToTodoSet(newtile
, newdir
, tile
, dir
)) return flags
| SF_FULL
;
361 continue; // continue the while() loop
365 if (!HasStationRail(tile
)) continue;
366 if (GetTileOwner(tile
) != owner
) continue;
367 if (DiagDirToAxis(enterdir
) != GetRailStationAxis(tile
)) continue; // different axis
368 if (IsStationTileBlocked(tile
)) continue; // 'eye-candy' station tile
370 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
371 tile
+= TileOffsByDiagDir(exitdir
);
375 if (!IsLevelCrossing(tile
)) continue;
376 if (GetTileOwner(tile
) != owner
) continue;
377 if (DiagDirToAxis(enterdir
) == GetCrossingRoadAxis(tile
)) continue; // different axis
379 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
380 tile
+= TileOffsByDiagDir(exitdir
);
383 case MP_TUNNELBRIDGE
: {
384 if (GetTileOwner(tile
) != owner
) continue;
385 if (GetTunnelBridgeTransportType(tile
) != TRANSPORT_RAIL
) continue;
386 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
388 if (IsTunnelBridgeWithSignalSimulation(tile
)) {
389 if (enterdir
== INVALID_DIAGDIR
) { // incoming from the wormhole
390 if (!(flags
& SF_TRAIN
) && IsTunnelBridgeSignalSimulationExit(tile
)) { // tunnel entrence is ignored
391 if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile
), &tile
, &TrainInWormholeTileEnum
)) flags
|= SF_TRAIN
;
392 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, &tile
, &TrainInWormholeTileEnum
)) flags
|= SF_TRAIN
;
394 if (IsTunnelBridgeSignalSimulationExit(tile
) && !_tbuset
.Add(tile
, INVALID_TRACKDIR
)) {
395 return flags
| SF_FULL
;
398 exitdir
= ReverseDiagDir(dir
);
399 tile
+= TileOffsByDiagDir(exitdir
); // just skip to next tile
400 } else { // NOT incoming from the wormhole!
401 if (ReverseDiagDir(enterdir
) != dir
) continue;
402 if (IsTunnelBridgeSignalSimulationExit(tile
)) {
403 if (IsTunnelBridgePBS(tile
)) {
405 } else if (!_tbuset
.Add(tile
, INVALID_TRACKDIR
)) {
406 return flags
| SF_FULL
;
409 if (!(flags
& SF_TRAIN
)) {
410 if (HasVehicleOnPos(tile
, &tile
, &TrainInWormholeTileEnum
)) flags
|= SF_TRAIN
;
411 if (!(flags
& SF_TRAIN
) && IsTunnelBridgeSignalSimulationExit(tile
)) {
412 if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile
), &tile
, &TrainInWormholeTileEnum
)) flags
|= SF_TRAIN
;
418 if (enterdir
== INVALID_DIAGDIR
) { // incoming from the wormhole
419 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
421 exitdir
= ReverseDiagDir(dir
);
422 tile
+= TileOffsByDiagDir(exitdir
); // just skip to next tile
423 } else { // NOT incoming from the wormhole!
424 if (ReverseDiagDir(enterdir
) != dir
) continue;
425 if (!(flags
& SF_TRAIN
) && HasVehicleOnPos(tile
, nullptr, &TrainOnTileEnum
)) flags
|= SF_TRAIN
;
426 tile
= GetOtherTunnelBridgeEnd(tile
); // just skip to exit tile
427 enterdir
= INVALID_DIAGDIR
;
428 exitdir
= INVALID_DIAGDIR
;
436 continue; // continue the while() loop
439 if (!MaybeAddToTodoSet(tile
, enterdir
, oldtile
, exitdir
)) return flags
| SF_FULL
;
447 * Update signals around segment in _tbuset
449 * @param flags info about segment
451 static void UpdateSignalsAroundSegment(SigFlags flags
)
456 while (_tbuset
.Get(&tile
, &trackdir
)) {
457 if (IsTileType(tile
, MP_TUNNELBRIDGE
) && IsTunnelBridgeSignalSimulationExit(tile
)) {
458 if (IsTunnelBridgePBS(tile
)) continue;
459 SignalState old_state
= GetTunnelBridgeSignalState(tile
);
460 SignalState new_state
= (flags
& SF_TRAIN
) ? SIGNAL_STATE_RED
: SIGNAL_STATE_GREEN
;
461 if (old_state
!= new_state
) {
462 SetTunnelBridgeSignalState(tile
, new_state
);
463 MarkTileDirtyByTile(tile
);
468 assert(HasSignalOnTrackdir(tile
, trackdir
));
470 SignalType sig
= GetSignalType(tile
, TrackdirToTrack(trackdir
));
471 SignalState newstate
= SIGNAL_STATE_GREEN
;
473 if (sig
== SIGTYPE_LOGIC
) {
474 SignalProgram
*program
= FindSignalProgram(tile
, TrackdirToTrack(trackdir
));
476 if (flags
& SF_TRAIN
) {
477 /* Blocked by a train */
478 newstate
= SIGNAL_STATE_RED
;
479 program
->blocked_by_train
= true;
481 /* Cleared by a train, we need to re-evaluate the state based on logic */
482 newstate
= program
->Evaluate();
483 program
->blocked_by_train
= false;
486 /* determine whether the new state is red */
487 if (flags
& SF_TRAIN
) {
488 /* train in the segment */
489 newstate
= SIGNAL_STATE_RED
;
491 /* is it a bidir combo? - then do not count its other signal direction as exit */
492 if (sig
== SIGTYPE_COMBO
&& HasSignalOnTrackdir(tile
, ReverseTrackdir(trackdir
))) {
493 /* at least one more exit */
494 if ((flags
& SF_EXIT2
) &&
496 (!(flags
& SF_GREEN
) ||
497 /* only one green exit, and it is this one - so all other exits are red */
498 (!(flags
& SF_GREEN2
) && GetSignalStateByTrackdir(tile
, ReverseTrackdir(trackdir
)) == SIGNAL_STATE_GREEN
))) {
499 newstate
= SIGNAL_STATE_RED
;
501 } else { // entry, at least one exit, no green exit
502 if (IsPresignalEntry(tile
, TrackdirToTrack(trackdir
)) && (flags
& SF_EXIT
) && !(flags
& SF_GREEN
)) newstate
= SIGNAL_STATE_RED
;
507 /* only when the state changes */
508 if (newstate
!= GetSignalStateByTrackdir(tile
, trackdir
)) {
509 if (IsPresignalExit(tile
, TrackdirToTrack(trackdir
))) {
510 /* for pre-signal exits, add block to the global set */
511 DiagDirection exitdir
= TrackdirToExitdir(ReverseTrackdir(trackdir
));
512 _globset
.Add(tile
, exitdir
); // do not check for full global set, first update all signals
514 SetSignalStateByTrackdir(tile
, trackdir
, newstate
);
515 MarkTileDirtyByTile(tile
, ZOOM_LVL_DRAW_MAP
);
517 /* notify logic signals of the state change */
518 SignalStateChanged(tile
, TrackdirToTrack(trackdir
), 1);
525 /** Reset all sets after one set overflowed */
526 static inline void ResetSets()
535 * Updates blocks in _globset buffer
537 * @param owner company whose signals we are updating
538 * @return state of the first block from _globset
539 * @pre Company::IsValidID(owner)
541 static SigSegState
UpdateSignalsInBuffer(Owner owner
)
543 assert(Company::IsValidID(owner
));
545 bool first
= true; // first block?
546 SigSegState state
= SIGSEG_FREE
; // value to return
551 while (_globset
.Get(&tile
, &dir
)) {
552 assert(_tbuset
.IsEmpty());
553 assert(_tbdset
.IsEmpty());
555 /* After updating signal, data stored are always MP_RAILWAY with signals.
556 * Other situations happen when data are from outside functions -
557 * modification of railbits (including both rail building and removal),
558 * train entering/leaving block, train leaving depot...
560 switch (GetTileType(tile
)) {
561 case MP_TUNNELBRIDGE
:
562 /* 'optimization assert' - do not try to update signals when it is not needed */
563 assert(GetTunnelBridgeTransportType(tile
) == TRANSPORT_RAIL
);
564 assert(dir
== INVALID_DIAGDIR
|| dir
== ReverseDiagDir(GetTunnelBridgeDirection(tile
)));
565 _tbdset
.Add(tile
, INVALID_DIAGDIR
); // we can safely start from wormhole centre
566 if (!IsTunnelBridgeWithSignalSimulation(tile
)) { // Don't worry with other side of tunnel.
567 _tbdset
.Add(GetOtherTunnelBridgeEnd(tile
), INVALID_DIAGDIR
);
572 if (IsRailDepot(tile
)) {
573 /* 'optimization assert' do not try to update signals in other cases */
574 assert(dir
== INVALID_DIAGDIR
|| dir
== GetRailDepotDirection(tile
));
575 _tbdset
.Add(tile
, INVALID_DIAGDIR
); // start from depot inside
582 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile
, TRANSPORT_RAIL
, 0)) & _enterdir_to_trackbits
[dir
]) != TRACK_BIT_NONE
) {
583 /* only add to set when there is some 'interesting' track */
584 _tbdset
.Add(tile
, dir
);
585 _tbdset
.Add(tile
+ TileOffsByDiagDir(dir
), ReverseDiagDir(dir
));
591 /* jump to next tile */
592 tile
= tile
+ TileOffsByDiagDir(dir
);
593 dir
= ReverseDiagDir(dir
);
594 if ((TrackStatusToTrackBits(GetTileTrackStatus(tile
, TRANSPORT_RAIL
, 0)) & _enterdir_to_trackbits
[dir
]) != TRACK_BIT_NONE
) {
595 _tbdset
.Add(tile
, dir
);
598 /* happens when removing a rail that wasn't connected at one or both sides */
599 continue; // continue the while() loop
602 assert(!_tbdset
.Overflowed()); // it really shouldn't overflow by these one or two items
603 assert(!_tbdset
.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
605 SigFlags flags
= ExploreSegment(owner
);
609 /* SIGSEG_FREE is set by default */
610 if (flags
& SF_PBS
) {
612 } else if ((flags
& SF_TRAIN
) || ((flags
& SF_EXIT
) && !(flags
& SF_GREEN
)) || (flags
& SF_FULL
)) {
617 /* do not do anything when some buffer was full */
618 if (flags
& SF_FULL
) {
619 ResetSets(); // free all sets
623 UpdateSignalsAroundSegment(flags
);
630 static Owner _last_owner
= INVALID_OWNER
; ///< last owner whose track was put into _globset
634 * Update signals in buffer
635 * Called from 'outside'
637 void UpdateSignalsInBuffer()
639 if (!_globset
.IsEmpty()) {
640 UpdateSignalsInBuffer(_last_owner
);
641 _last_owner
= INVALID_OWNER
; // invalidate
647 * Add track to signal update buffer
649 * @param tile tile where we start
650 * @param track track at which ends we will update signals
651 * @param owner owner whose signals we will update
653 void AddTrackToSignalBuffer(TileIndex tile
, Track track
, Owner owner
)
655 static const DiagDirection _search_dir_1
[] = {
656 DIAGDIR_NE
, DIAGDIR_SE
, DIAGDIR_NE
, DIAGDIR_SE
, DIAGDIR_SW
, DIAGDIR_SE
658 static const DiagDirection _search_dir_2
[] = {
659 DIAGDIR_SW
, DIAGDIR_NW
, DIAGDIR_NW
, DIAGDIR_SW
, DIAGDIR_NW
, DIAGDIR_NE
662 /* do not allow signal updates for two companies in one run */
663 assert(_globset
.IsEmpty() || owner
== _last_owner
);
667 _globset
.Add(tile
, _search_dir_1
[track
]);
668 _globset
.Add(tile
, _search_dir_2
[track
]);
670 if (_globset
.Items() >= SIG_GLOB_UPDATE
) {
671 /* too many items, force update */
672 UpdateSignalsInBuffer(_last_owner
);
673 _last_owner
= INVALID_OWNER
;
679 * Add side of tile to signal update buffer
681 * @param tile tile where we start
682 * @param side side of tile
683 * @param owner owner whose signals we will update
685 void AddSideToSignalBuffer(TileIndex tile
, DiagDirection side
, Owner owner
)
687 /* do not allow signal updates for two companies in one run */
688 assert(_globset
.IsEmpty() || owner
== _last_owner
);
692 _globset
.Add(tile
, side
);
694 if (_globset
.Items() >= SIG_GLOB_UPDATE
) {
695 /* too many items, force update */
696 UpdateSignalsInBuffer(_last_owner
);
697 _last_owner
= INVALID_OWNER
;
702 * Update signals, starting at one side of a tile
703 * Will check tile next to this at opposite side too
705 * @see UpdateSignalsInBuffer()
706 * @param tile tile where we start
707 * @param side side of tile
708 * @param owner owner whose signals we will update
709 * @return the state of the signal segment
711 SigSegState
UpdateSignalsOnSegment(TileIndex tile
, DiagDirection side
, Owner owner
)
713 assert(_globset
.IsEmpty());
714 _globset
.Add(tile
, side
);
716 return UpdateSignalsInBuffer(owner
);
721 * Update signals at segments that are at both ends of
722 * given (existent or non-existent) track
724 * @see UpdateSignalsInBuffer()
725 * @param tile tile where we start
726 * @param track track at which ends we will update signals
727 * @param owner owner whose signals we will update
729 void SetSignalsOnBothDir(TileIndex tile
, Track track
, Owner owner
)
731 assert(_globset
.IsEmpty());
733 AddTrackToSignalBuffer(tile
, track
, owner
);
734 UpdateSignalsInBuffer(owner
);