Update readme and changelog for v1.26.0
[openttd-joker.git] / src / signal.cpp
blob5944a2a1c8ab539f4ee677079c3a3a56f72166a6
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file signal.cpp functions related to rail signals updating */
12 #include "stdafx.h"
13 #include "debug.h"
14 #include "station_map.h"
15 #include "tunnelbridge_map.h"
16 #include "vehicle_func.h"
17 #include "viewport_func.h"
18 #include "train.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] = {
35 TRACK_BIT_3WAY_NE,
36 TRACK_BIT_3WAY_SE,
37 TRACK_BIT_3WAY_SW,
38 TRACK_BIT_3WAY_NW
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
49 /**
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>
55 struct SmallSet {
56 private:
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...
61 /** Element of set */
62 struct SSdata {
63 TileIndex tile;
64 Tdir dir;
65 } data[items];
67 public:
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 */
72 void Reset()
74 this->n = 0;
75 this->overflowed = false;
78 /**
79 * Returns value of 'overflowed'
80 * @return did we try to overflow the set?
82 bool Overflowed()
84 return this->overflowed;
87 /**
88 * Checks for empty set
89 * @return is the set empty?
91 bool IsEmpty()
93 return this->n == 0;
96 /**
97 * Checks for full set
98 * @return is the set full?
100 bool IsFull()
102 return this->n == lengthof(data);
106 * Reads the number of items
107 * @return current number of items
109 uint Items()
111 return this->n;
116 * Tries to remove first instance of given tile and dir
117 * @param tile tile
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];
126 return true;
130 return false;
134 * Tries to find given tile and dir in the set
135 * @param tile tile
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;
145 return false;
149 * Adds tile & dir into the set, checks for full set
150 * Sets the 'overflowed' flag if the set was full
151 * @param tile tile
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()) {
158 overflowed = true;
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;
165 this->n++;
167 return true;
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;
180 this->n--;
181 *tile = this->data[this->n].tile;
182 *dir = this->data[this->n].dir;
184 return true;
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;
198 return v;
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;
207 return v;
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;
232 return true;
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 */
258 enum SigFlags {
259 SF_NONE = 0,
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
276 * @return SigFlags
278 static SigFlags ExploreSegment(Owner owner)
280 SigFlags flags = SF_NONE;
282 TileIndex tile;
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)) {
290 case MP_RAILWAY: {
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);
299 break;
300 } else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot
301 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
302 continue;
303 } else {
304 continue;
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;
316 } else {
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)) {
332 flags |= SF_PBS;
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;
345 flags |= SF_GREEN;
349 continue;
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
364 case MP_STATION:
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);
372 break;
374 case MP_ROAD:
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);
381 break;
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;
397 enterdir = dir;
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)) {
404 flags |= SF_PBS;
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;
415 continue;
417 } else {
418 if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
419 if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
420 enterdir = dir;
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;
433 break;
435 default:
436 continue; // continue the while() loop
439 if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL;
442 return flags;
447 * Update signals around segment in _tbuset
449 * @param flags info about segment
451 static void UpdateSignalsAroundSegment(SigFlags flags)
453 TileIndex tile;
454 Trackdir trackdir;
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);
465 continue;
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;
480 } else {
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;
485 } else {
486 /* determine whether the new state is red */
487 if (flags & SF_TRAIN) {
488 /* train in the segment */
489 newstate = SIGNAL_STATE_RED;
490 } else {
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) &&
495 /* no green exit */
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()
528 _tbuset.Reset();
529 _tbdset.Reset();
530 _globset.Reset();
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
548 TileIndex tile;
549 DiagDirection dir;
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);
569 break;
571 case MP_RAILWAY:
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
576 break;
578 FALLTHROUGH;
580 case MP_STATION:
581 case MP_ROAD:
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));
586 break;
588 FALLTHROUGH;
590 default:
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);
596 break;
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);
607 if (first) {
608 first = false;
609 /* SIGSEG_FREE is set by default */
610 if (flags & SF_PBS) {
611 state = SIGSEG_PBS;
612 } else if ((flags & SF_TRAIN) || ((flags & SF_EXIT) && !(flags & SF_GREEN)) || (flags & SF_FULL)) {
613 state = SIGSEG_FULL;
617 /* do not do anything when some buffer was full */
618 if (flags & SF_FULL) {
619 ResetSets(); // free all sets
620 break;
623 UpdateSignalsAroundSegment(flags);
626 return state;
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);
665 _last_owner = 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);
690 _last_owner = 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);