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