rubidium@9724: /* $Id$ */ rubidium@9724: rubidium@9724: /** @file signal.cpp functions related to rail signals updating */ rubidium@9724: rubidium@9724: #include "stdafx.h" rubidium@9724: #include "openttd.h" rubidium@9724: #include "debug.h" rubidium@9724: #include "tile_cmd.h" rubidium@9724: #include "rail_map.h" rubidium@9724: #include "road_map.h" rubidium@9724: #include "station_map.h" rubidium@9724: #include "tunnelbridge_map.h" rubidium@9724: #include "vehicle_func.h" rubidium@9724: #include "train.h" rubidium@9724: #include "newgrf_station.h" rubidium@9724: #include "functions.h" rubidium@9724: #include "track_type.h" rubidium@9724: #include "track_func.h" rubidium@9724: #include "signal_func.h" rubidium@9724: #include "player_func.h" rubidium@9724: rubidium@9724: rubidium@9724: /** these are the maximums used for updating signal blocks */ rubidium@9724: enum { rubidium@9724: SIG_TBU_SIZE = 64, ///< number of signals entering to block rubidium@9724: SIG_TBD_SIZE = 256, ///< number of intersections - open nodes in current block rubidium@9724: SIG_GLOB_SIZE = 128, ///< number of open blocks (block can be opened more times until detected) rubidium@9724: SIG_GLOB_UPDATE = 64, ///< how many items need to be in _globset to force update rubidium@9724: }; rubidium@9724: rubidium@9724: /* need to typecast to compile with MorphOS */ rubidium@9724: assert_compile((int)SIG_GLOB_UPDATE <= (int)SIG_GLOB_SIZE); rubidium@9724: rubidium@9724: /** incidating trackbits with given enterdir */ rubidium@9724: static const TrackBitsByte _enterdir_to_trackbits[DIAGDIR_END] = { rubidium@9724: {TRACK_BIT_3WAY_NE}, rubidium@9724: {TRACK_BIT_3WAY_SE}, rubidium@9724: {TRACK_BIT_3WAY_SW}, rubidium@9724: {TRACK_BIT_3WAY_NW} rubidium@9724: }; rubidium@9724: rubidium@9724: /** incidating trackdirbits with given enterdir */ rubidium@9724: static const TrackdirBitsShort _enterdir_to_trackdirbits[DIAGDIR_END] = { rubidium@9724: {TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S}, rubidium@9724: {TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N}, rubidium@9724: {TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N}, rubidium@9724: {TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S} rubidium@9724: }; rubidium@9724: rubidium@9724: /** rubidium@9724: * Set containing 'items' items of 'tile and Tdir' rubidium@9724: * No tree structure is used because it would cause rubidium@9724: * slowdowns in most usual cases rubidium@9724: */ rubidium@9724: template rubidium@9724: struct SmallSet { rubidium@9724: private: rubidium@9724: uint n; // actual number of units rubidium@9724: bool overflowed; // did we try to oveflow the set? rubidium@9724: const char *name; // name, used for debugging purposes... rubidium@9724: rubidium@9724: /** Element of set */ rubidium@9724: struct SSdata { rubidium@9724: TileIndex tile; rubidium@9724: Tdir dir; rubidium@9724: } data[items]; rubidium@9724: rubidium@9724: public: rubidium@9724: /** Constructor - just set default values and 'name' */ rubidium@9724: SmallSet(const char *name) : n(0), overflowed(false), name(name) { } rubidium@9724: rubidium@9724: /** Reset variables to default values */ rubidium@9724: void Reset() rubidium@9724: { rubidium@9724: this->n = 0; rubidium@9724: this->overflowed = false; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Returns value of 'oveflowed' rubidium@9724: * @return did we try to overflow the set? rubidium@9724: */ rubidium@9724: bool Overflowed() rubidium@9724: { rubidium@9724: return this->overflowed; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Checks for empty set rubidium@9724: * @return is the set empty? rubidium@9724: */ rubidium@9724: bool IsEmpty() rubidium@9724: { rubidium@9724: return this->n == 0; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Checks for full set rubidium@9724: * @return is the set full? rubidium@9724: */ rubidium@9724: bool IsFull() rubidium@9724: { rubidium@9724: return this->n == lengthof(data); rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Reads the number of items rubidium@9724: * @return current number of items rubidium@9724: */ rubidium@9724: uint Items() rubidium@9724: { rubidium@9724: return this->n; rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Tries to remove first instance of given tile and dir rubidium@9724: * @param tile tile rubidium@9724: * @param dir and dir to remove rubidium@9724: * @return element was found and removed rubidium@9724: */ rubidium@9724: bool Remove(TileIndex tile, Tdir dir) rubidium@9724: { rubidium@9724: for (uint i = 0; i < this->n; i++) { rubidium@9724: if (this->data[i].tile == tile && this->data[i].dir == dir) { rubidium@9724: this->data[i] = this->data[--this->n]; rubidium@9724: return true; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: return false; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Tries to find given tile and dir in the set rubidium@9724: * @param tile tile rubidium@9724: * @param dir and dir to find rubidium@9724: * @return true iff the tile & dir elemnt was found rubidium@9724: */ rubidium@9724: bool IsIn(TileIndex tile, Tdir dir) rubidium@9724: { rubidium@9724: for (uint i = 0; i < this->n; i++) { rubidium@9724: if (this->data[i].tile == tile && this->data[i].dir == dir) return true; rubidium@9724: } rubidium@9724: rubidium@9724: return false; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Adds tile & dir into the set, checks for full set rubidium@9724: * Sets the 'overflowed' flag if the set was full rubidium@9724: * @param tile tile rubidium@9724: * @param dir and dir to add rubidium@9724: * @return true iff the item could be added (set wasn't full) rubidium@9724: */ rubidium@9724: bool Add(TileIndex tile, Tdir dir) rubidium@9724: { rubidium@9724: if (this->IsFull()) { rubidium@9724: overflowed = true; rubidium@9724: DEBUG(misc, 0, "SignalSegment too complex. Set %s is full (maximum %d)", name, items); rubidium@9724: return false; // set is full rubidium@9724: } rubidium@9724: rubidium@9724: this->data[this->n].tile = tile; rubidium@9724: this->data[this->n].dir = dir; rubidium@9724: this->n++; rubidium@9724: rubidium@9724: return true; rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Reads the last added element into the set rubidium@9724: * @param tile pointer where tile is written to rubidium@9724: * @param dir pointer where dir is written to rubidium@9724: * @return false iff the set was empty rubidium@9724: */ rubidium@9724: bool Get(TileIndex *tile, Tdir *dir) rubidium@9724: { rubidium@9724: if (this->n == 0) return false; rubidium@9724: rubidium@9724: this->n--; rubidium@9724: *tile = this->data[this->n].tile; rubidium@9724: *dir = this->data[this->n].dir; rubidium@9724: rubidium@9724: return true; rubidium@9724: } rubidium@9724: }; rubidium@9724: rubidium@9724: static SmallSet _tbuset("_tbuset"); ///< set of signals that will be updated rubidium@9724: static SmallSet _tbdset("_tbdset"); ///< set of open nodes in current signal block rubidium@9724: static SmallSet _globset("_globset"); ///< set of places to be updated in following runs rubidium@9724: rubidium@9724: rubidium@9724: /** Check whether there is a train on rail, not in a depot */ rubidium@9724: static void *TrainOnTileEnum(Vehicle *v, void *) rubidium@9724: { rubidium@9724: if (v->type != VEH_TRAIN || v->u.rail.track == TRACK_BIT_DEPOT) return NULL; rubidium@9724: rubidium@9724: return v; rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Perform some operations before adding data into Todo set rubidium@9724: * The new and reverse direction is removed from _globset, because we are sure rubidium@9724: * it doesn't need to be checked again rubidium@9724: * Also, remove reverse direction from _tbdset rubidium@9724: * This is the 'core' part so the graph seaching won't enter any tile twice rubidium@9724: * rubidium@9724: * @param t1 tile we are entering rubidium@9724: * @param d1 direction (tile side) we are entering rubidium@9724: * @param t2 tile we are leaving rubidium@9724: * @param d2 direction (tile side) we are leaving rubidium@9724: * @return false iff reverse direction was in Todo set rubidium@9724: */ rubidium@9724: static inline bool CheckAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2) rubidium@9724: { rubidium@9724: _globset.Remove(t1, d1); // it can be in Global but not in Todo rubidium@9724: _globset.Remove(t2, d2); // remove in all cases rubidium@9724: rubidium@9724: assert(!_tbdset.IsIn(t1, d1)); // it really shouldn't be there already rubidium@9724: rubidium@9724: if (_tbdset.Remove(t2, d2)) return false; rubidium@9724: rubidium@9724: return true; rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Perform some operations before adding data into Todo set rubidium@9724: * The new and reverse direction is removed from Global set, because we are sure rubidium@9724: * it doesn't need to be checked again rubidium@9724: * Also, remove reverse direction from Todo set rubidium@9724: * This is the 'core' part so the graph seaching won't enter any tile twice rubidium@9724: * rubidium@9724: * @param t1 tile we are entering rubidium@9724: * @param d1 direction (tile side) we are entering rubidium@9724: * @param t2 tile we are leaving rubidium@9724: * @param d2 direction (tile side) we are leaving rubidium@9724: * @return false iff the Todo buffer would be overrun rubidium@9724: */ rubidium@9724: static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t2, DiagDirection d2) rubidium@9724: { rubidium@9724: if (!CheckAddToTodoSet(t1, d1, t2, d2)) return true; rubidium@9724: rubidium@9724: return _tbdset.Add(t1, d1); rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** Current signal block state flags */ rubidium@9724: enum SigFlags { rubidium@9724: SF_NONE = 0, rubidium@9724: SF_TRAIN = 1 << 0, ///< train found in segment rubidium@9724: SF_EXIT = 1 << 1, ///< exitsignal found rubidium@9724: SF_EXIT2 = 1 << 2, ///< two or more exits found rubidium@9724: SF_GREEN = 1 << 3, ///< green exitsignal found rubidium@9724: SF_GREEN2 = 1 << 4, ///< two or more green exits found rubidium@9724: SF_FULL = 1 << 5, ///< some of buffers was full, do not continue rubidium@9724: }; rubidium@9724: rubidium@9724: DECLARE_ENUM_AS_BIT_SET(SigFlags) rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Search signal block rubidium@9724: * rubidium@9724: * @param owner owner whose signals we are updating rubidium@9724: * @return SigFlags rubidium@9724: */ rubidium@9724: static SigFlags ExploreSegment(Owner owner) rubidium@9724: { rubidium@9724: SigFlags flags = SF_NONE; rubidium@9724: rubidium@9724: TileIndex tile; rubidium@9724: DiagDirection enterdir; rubidium@9724: rubidium@9724: while (_tbdset.Get(&tile, &enterdir)) { rubidium@9724: TileIndex oldtile = tile; // tile we are leaving rubidium@9724: DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir); // expected new exit direction (for straight line) rubidium@9724: rubidium@9724: switch (GetTileType(tile)) { rubidium@9724: case MP_RAILWAY: { rubidium@9724: if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing) rubidium@9724: rubidium@9724: if (IsRailDepot(tile)) { rubidium@9724: if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: exitdir = GetRailDepotDirection(tile); rubidium@9724: tile += TileOffsByDiagDir(exitdir); rubidium@9724: enterdir = ReverseDiagDir(exitdir); rubidium@9724: break; rubidium@9724: } else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: continue; rubidium@9724: } else { rubidium@9724: continue; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) { rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: tile += TileOffsByDiagDir(exitdir); rubidium@9724: /* enterdir and exitdir stay the same */ rubidium@9724: break; rubidium@9724: } rubidium@9724: rubidium@9724: TrackBits tracks = GetTrackBits(tile); // trackbits of tile rubidium@9724: TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits rubidium@9724: rubidium@9724: if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check rubidium@9724: tracks = tracks_masked; rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, &tracks, &EnsureNoTrainOnTrackProc)) flags |= SF_TRAIN; rubidium@9724: } else { rubidium@9724: if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: } rubidium@9724: rubidium@9724: if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile rubidium@9724: Track track = TrackBitsToTrack(tracks_masked); // mask TRACK_BIT_X and Y too rubidium@9724: if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir rubidium@9724: SignalType sig = GetSignalType(tile, track); rubidium@9724: Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]); rubidium@9724: Trackdir reversedir = ReverseTrackdir(trackdir); rubidium@9724: /* add (tile, reversetrackdir) to 'to-be-updated' set when there is rubidium@9724: * ANY signal in REVERSE direction rubidium@9724: * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */ rubidium@9724: if (HasSignalOnTrackdir(tile, reversedir)) { rubidium@9724: if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL; rubidium@9724: } rubidium@9724: /* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */ rubidium@9724: if (!(flags & SF_GREEN2) && (sig & SIGTYPE_EXIT) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit rubidium@9724: if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits rubidium@9724: flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations rubidium@9724: if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit rubidium@9724: if (flags & SF_GREEN) flags |= SF_GREEN2; rubidium@9724: flags |= SF_GREEN; rubidium@9724: } rubidium@9724: } rubidium@9724: continue; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { // test all possible exit directions rubidium@9724: if (dir != enterdir && tracks & _enterdir_to_trackbits[dir]) { // any track incidating? rubidium@9724: TileIndex newtile = tile + TileOffsByDiagDir(dir); // new tile to check rubidium@9724: DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from rubidium@9724: if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: continue; // continue the while() loop rubidium@9724: } rubidium@9724: rubidium@9724: case MP_STATION: rubidium@9724: if (!IsRailwayStation(tile)) continue; rubidium@9724: if (GetTileOwner(tile) != owner) continue; rubidium@9724: if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis rubidium@9724: if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile rubidium@9724: rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: tile += TileOffsByDiagDir(exitdir); rubidium@9724: break; rubidium@9724: rubidium@9724: case MP_ROAD: rubidium@9724: if (!IsLevelCrossing(tile)) continue; rubidium@9724: if (GetTileOwner(tile) != owner) continue; rubidium@9724: if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis rubidium@9724: rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: tile += TileOffsByDiagDir(exitdir); rubidium@9724: break; rubidium@9724: rubidium@9724: case MP_TUNNELBRIDGE: { rubidium@9724: if (GetTileOwner(tile) != owner) continue; rubidium@9724: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; rubidium@9724: DiagDirection dir = GetTunnelBridgeDirection(tile); rubidium@9724: rubidium@9724: if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: enterdir = dir; rubidium@9724: exitdir = ReverseDiagDir(dir); rubidium@9724: tile += TileOffsByDiagDir(exitdir); // just skip to next tile rubidium@9724: } else { // NOT incoming from the wormhole! rubidium@9724: if (ReverseDiagDir(enterdir) != dir) continue; rubidium@9724: if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; rubidium@9724: tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile rubidium@9724: enterdir = INVALID_DIAGDIR; rubidium@9724: exitdir = INVALID_DIAGDIR; rubidium@9724: } rubidium@9724: } rubidium@9724: break; rubidium@9724: rubidium@9724: default: rubidium@9724: continue; // continue the while() loop rubidium@9724: } rubidium@9724: rubidium@9724: if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL; rubidium@9724: } rubidium@9724: rubidium@9724: return flags; rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Update signals around segment in _tbuset rubidium@9724: * rubidium@9724: * @param flags info about segment rubidium@9724: */ rubidium@9724: static void UpdateSignalsAroundSegment(SigFlags flags) rubidium@9724: { rubidium@9724: TileIndex tile; rubidium@9724: Trackdir trackdir; rubidium@9724: rubidium@9724: while (_tbuset.Get(&tile, &trackdir)) { rubidium@9724: assert(HasSignalOnTrackdir(tile, trackdir)); rubidium@9724: rubidium@9724: SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir)); rubidium@9724: SignalState newstate = SIGNAL_STATE_GREEN; rubidium@9724: rubidium@9724: /* determine whether the new state is red */ rubidium@9724: if (flags & SF_TRAIN) { rubidium@9724: /* train in the segment */ rubidium@9724: newstate = SIGNAL_STATE_RED; rubidium@9724: } else { rubidium@10249: /* is it a bidir combo? - then do not count its other signal direction as exit */ rubidium@9724: if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) { rubidium@9724: /* at least one more exit */ rubidium@9724: if (flags & SF_EXIT2 && rubidium@10249: /* no green exit */ rubidium@9724: (!(flags & SF_GREEN) || rubidium@9724: /* only one green exit, and it is this one - so all other exits are red */ rubidium@9724: (!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) { rubidium@9724: newstate = SIGNAL_STATE_RED; rubidium@9724: } rubidium@9724: } else { // entry, at least one exit, no green exit rubidium@9724: if (sig & SIGTYPE_ENTRY && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: /* only when the state changes */ rubidium@9724: if (newstate != GetSignalStateByTrackdir(tile, trackdir)) { rubidium@9724: if (sig & SIGTYPE_EXIT) { rubidium@9724: /* for pre-signal exits, add block to the global set */ rubidium@9724: DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir)); rubidium@9724: _globset.Add(tile, exitdir); // do not check for full global set, first update all signals rubidium@9724: } rubidium@9724: SetSignalStateByTrackdir(tile, trackdir, newstate); rubidium@9724: MarkTileDirtyByTile(tile); rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** Reset all sets after one set overflowed */ rubidium@9724: static inline void ResetSets() rubidium@9724: { rubidium@9724: _tbuset.Reset(); rubidium@9724: _tbdset.Reset(); rubidium@9724: _globset.Reset(); rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Updates blocks in _globset buffer rubidium@9724: * rubidium@9724: * @param owner player whose signals we are updating rubidium@10715: * @return state of the first block from _globset rubidium@9724: * @pre IsValidPlayer(owner) rubidium@9724: */ glx@10645: static SigSegState UpdateSignalsInBuffer(Owner owner) rubidium@9724: { rubidium@9724: assert(IsValidPlayer(owner)); rubidium@9724: rubidium@9724: bool first = true; // first block? glx@10645: SigSegState state = SIGSEG_FREE; // value to return rubidium@9724: rubidium@9724: TileIndex tile; rubidium@9724: DiagDirection dir; rubidium@9724: rubidium@9724: while (_globset.Get(&tile, &dir)) { rubidium@9724: assert(_tbuset.IsEmpty()); rubidium@9724: assert(_tbdset.IsEmpty()); rubidium@9724: rubidium@9724: /* After updating signal, data stored are always MP_RAILWAY with signals. rubidium@9724: * Other situations happen when data are from outside functions - rubidium@9724: * modification of railbits (including both rail building and removal), rubidium@9724: * train entering/leaving block, train leaving depot... rubidium@9724: */ rubidium@9724: switch (GetTileType(tile)) { rubidium@9724: case MP_TUNNELBRIDGE: rubidium@9724: /* 'optimization assert' - do not try to update signals when it is not needed */ rubidium@9724: assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL); rubidium@9724: assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile))); rubidium@9724: _tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre rubidium@9724: _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); rubidium@9724: break; rubidium@9724: rubidium@9724: case MP_RAILWAY: rubidium@9724: if (IsRailDepot(tile)) { rubidium@9724: /* 'optimization assert' do not try to update signals in other cases */ rubidium@9724: assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile)); rubidium@9724: _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside rubidium@9724: break; rubidium@9724: } rubidium@9724: /* FALLTHROUGH */ rubidium@9724: case MP_STATION: rubidium@9724: case MP_ROAD: glx@9732: if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) { rubidium@10249: /* only add to set when there is some 'interesting' track */ rubidium@9724: _tbdset.Add(tile, dir); rubidium@9724: _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir)); rubidium@9724: break; rubidium@9724: } rubidium@9724: /* FALLTHROUGH */ rubidium@9724: default: rubidium@9724: /* jump to next tile */ rubidium@9724: tile = tile + TileOffsByDiagDir(dir); rubidium@9724: dir = ReverseDiagDir(dir); glx@9732: if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) { rubidium@9724: _tbdset.Add(tile, dir); rubidium@9724: break; rubidium@9724: } rubidium@9724: /* happens when removing a rail that wasn't connected at one or both sides */ rubidium@9724: continue; // continue the while() loop rubidium@9724: } rubidium@9724: rubidium@9724: assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items rubidium@9724: assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too rubidium@9724: rubidium@9724: SigFlags flags = ExploreSegment(owner); rubidium@9724: rubidium@9724: if (first) { rubidium@9724: first = false; glx@10645: if ((flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL)) { glx@10645: /* SIGSEG_FREE is set by default */ glx@10645: state = SIGSEG_FULL; glx@10645: } rubidium@9724: } rubidium@9724: rubidium@9724: /* do not do anything when some buffer was full */ rubidium@9724: if (flags & SF_FULL) { rubidium@9724: ResetSets(); // free all sets rubidium@9724: break; rubidium@9724: } rubidium@9724: rubidium@9724: UpdateSignalsAroundSegment(flags); rubidium@9724: } rubidium@9724: rubidium@9724: return state; rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: static Owner _last_owner = INVALID_OWNER; ///< last owner whose track was put into _globset rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Update signals in buffer rubidium@9724: * Called from 'outside' rubidium@9724: */ rubidium@9724: void UpdateSignalsInBuffer() rubidium@9724: { rubidium@9724: if (!_globset.IsEmpty()) { rubidium@9724: UpdateSignalsInBuffer(_last_owner); rubidium@9724: _last_owner = INVALID_OWNER; // invalidate rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Add track to signal update buffer rubidium@9724: * rubidium@9724: * @param tile tile where we start rubidium@9724: * @param track track at which ends we will update signals rubidium@9724: * @param owner owner whose signals we will update rubidium@9724: */ rubidium@9724: void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner) rubidium@9724: { rubidium@9724: static const DiagDirection _search_dir_1[] = { rubidium@9724: DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE rubidium@9724: }; rubidium@9724: static const DiagDirection _search_dir_2[] = { rubidium@9724: DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE rubidium@9724: }; rubidium@9724: rubidium@9724: /* do not allow signal updates for two players in one run */ rubidium@9724: assert(_globset.IsEmpty() || owner == _last_owner); rubidium@9724: rubidium@9724: _last_owner = owner; rubidium@9724: rubidium@9724: _globset.Add(tile, _search_dir_1[track]); rubidium@9724: _globset.Add(tile, _search_dir_2[track]); rubidium@9724: rubidium@9724: if (_globset.Items() >= SIG_GLOB_UPDATE) { rubidium@9724: /* too many items, force update */ rubidium@9724: UpdateSignalsInBuffer(_last_owner); rubidium@9724: _last_owner = INVALID_OWNER; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Add side of tile to signal update buffer rubidium@9724: * rubidium@9724: * @param tile tile where we start rubidium@9724: * @param side side of tile rubidium@9724: * @param owner owner whose signals we will update rubidium@9724: */ rubidium@9724: void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner) rubidium@9724: { rubidium@9724: /* do not allow signal updates for two players in one run */ rubidium@9724: assert(_globset.IsEmpty() || owner == _last_owner); rubidium@9724: rubidium@9724: _last_owner = owner; rubidium@9724: rubidium@9724: _globset.Add(tile, side); rubidium@9724: rubidium@9724: if (_globset.Items() >= SIG_GLOB_UPDATE) { rubidium@9724: /* too many items, force update */ rubidium@9724: UpdateSignalsInBuffer(_last_owner); rubidium@9724: _last_owner = INVALID_OWNER; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Update signals, starting at one side of a tile rubidium@9724: * Will check tile next to this at opposite side too rubidium@9724: * rubidium@9724: * @see UpdateSignalsInBuffer() rubidium@9724: * @param tile tile where we start rubidium@9724: * @param side side of tile rubidium@9724: * @param owner owner whose signals we will update rubidium@10715: * @return the state of the signal segment rubidium@9724: */ glx@10645: SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner) rubidium@9724: { rubidium@9724: assert(_globset.IsEmpty()); rubidium@9724: _globset.Add(tile, side); rubidium@9724: rubidium@9724: return UpdateSignalsInBuffer(owner); rubidium@9724: } rubidium@9724: rubidium@9724: rubidium@9724: /** rubidium@9724: * Update signals at segments that are at both ends of rubidium@9724: * given (existent or non-existent) track rubidium@9724: * rubidium@9724: * @see UpdateSignalsInBuffer() rubidium@9724: * @param tile tile where we start rubidium@9724: * @param track track at which ends we will update signals rubidium@9724: * @param owner owner whose signals we will update rubidium@9724: */ rubidium@9724: void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner) rubidium@9724: { rubidium@9724: assert(_globset.IsEmpty()); rubidium@9724: rubidium@9724: AddTrackToSignalBuffer(tile, track, owner); rubidium@9724: UpdateSignalsInBuffer(owner); rubidium@9724: }