diff --git a/src/lang/english.txt b/src/lang/english.txt
index e478dc6e1c..336130877b 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1561,6 +1561,8 @@ STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES                         :Minimum company
 STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES_HELPTEXT                :Set the minimum age of a company for others to be able to buy and sell shares from them.
 STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE                         :Percentage of leg profit to pay in feeder systems: {STRING2}
 STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT                :Percentage of income given to the intermediate legs in feeder systems, giving more control over the income
+STR_CONFIG_SETTING_SIMULATE_SIGNALS                             :Simulate signals in tunnels, bridges every: {STRING2}
+STR_CONFIG_SETTING_SIMULATE_SIGNALS_VALUE                       :{COMMA} tile{P 0 "" s}
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY                         :When dragging, place signals every: {STRING2}
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_HELPTEXT                :Set the distance at which signals will be built on a track up to the next obstacle (signal, junction), if signals are dragged
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_VALUE                   :{COMMA} tile{P 0 "" s}
@@ -2708,8 +2710,10 @@ STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT                            :Ship depot
 # Industries come directly from their industry names
 
 STR_LAI_TUNNEL_DESCRIPTION_RAILROAD                             :Railway tunnel
+STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL                      :Railway tunnel with signal simulation
 STR_LAI_TUNNEL_DESCRIPTION_ROAD                                 :Road tunnel
 
+STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL                      :Railway bridge with signal simulation
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL                :Steel suspension rail bridge
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL                    :Steel girder rail bridge
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL                :Steel cantilever rail bridge
diff --git a/src/lang/russian.txt b/src/lang/russian.txt
index 4f98e7adee..d4600504b0 100644
--- a/src/lang/russian.txt
+++ b/src/lang/russian.txt
@@ -1712,6 +1712,8 @@ STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES                         :Мин. воз
 STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES_HELPTEXT                :Минимальный возраст, которого должна достичь компания для начала выпуска акций, которыми смогут торговать другие игроки.
 STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE                         :Процент дохода, начисляемый при частичной перевозке: {STRING}
 STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT                :Процент прибыли, начисляемый транспорту за частичную перевозку груза.
+STR_CONFIG_SETTING_SIMULATE_SIGNALS                             :Симуляция светофоров в туннелях и на мостах каждые: {STRING}
+STR_CONFIG_SETTING_SIMULATE_SIGNALS_VALUE                       :{COMMA} клет{P ку ки ок}
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY                         :При перетаскивании ставить сигналы каждые: {STRING}
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_HELPTEXT                :Настройка периодичности расстановки сигналов методом перетаскивания. Сигналы будут устанавливаться до первого встреченного препятствия (пересечения или другого сигнала).
 STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_VALUE                   :{COMMA} клет{P ку ки ок}
@@ -2887,8 +2889,10 @@ STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT                            :Верфь
 # Industries come directly from their industry names
 
 STR_LAI_TUNNEL_DESCRIPTION_RAILROAD                             :Железнодорожный туннель
+STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL                      :Железнодорожный туннель с симуляцией светофоров
 STR_LAI_TUNNEL_DESCRIPTION_ROAD                                 :Автомобильный туннель
 
+STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL                      :Железнодорожный мост с симуляцией светофоров
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL                :Стальной висячий ж/д мост
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL                    :Стальной балочный ж/д мост
 STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL                :Стальной консольный ж/д мост
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 2cfe4d9e4a..dbf225d785 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -370,7 +370,7 @@ protected:
 			if (IsTunnel(m_new_tile)) {
 				if (!m_is_tunnel) {
 					DiagDirection tunnel_enterdir = GetTunnelBridgeDirection(m_new_tile);
-					if (tunnel_enterdir != m_exitdir) {
+					if (tunnel_enterdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) {
 						m_err = EC_NO_WAY;
 						return false;
 					}
@@ -378,7 +378,7 @@ protected:
 			} else { // IsBridge(m_new_tile)
 				if (!m_is_bridge) {
 					DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
-					if (ramp_enderdir != m_exitdir) {
+					if (ramp_enderdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) {
 						m_err = EC_NO_WAY;
 						return false;
 					}
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 162fe97799..186c7b8228 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -1059,9 +1059,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
 	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
 	if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
 
-	/* You can only build signals on plain rail tiles, and the selected track must exist */
-	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
-			!HasTrack(tile, track)) {
+	/* You can only build signals on plain rail tiles or tunnel/bridges, and the selected track must exist */
+	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+		if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR;
+		CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track);
+		//if (ret.Failed()) return ret;
+	} else if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
 		return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
 	}
 	/* Protect against invalid signal copying */
@@ -1070,6 +1073,53 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
 	CommandCost ret = CheckTileOwnership(tile);
 	if (ret.Failed()) return ret;
 
+	CommandCost cost;
+	/* handle signals simulation on tunnel/bridge. */
+	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+		TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile);
+		cost = CommandCost();
+		if (!HasWormholeSignals(tile)) { // toggle signal zero costs.
+			if (p2 != 12) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2)); // minimal 1
+		}
+		if (flags & DC_EXEC) {
+			if (p2 == 0 && HasWormholeSignals(tile)){ // Toggle signal if already signals present.
+				if (IsTunnelBridgeEntrance (tile)) {
+					ClrBitTunnelBridgeSignal(tile);
+					ClrBitTunnelBridgeExit(tile_exit);
+					SetBitTunnelBridgeExit(tile);
+					SetBitTunnelBridgeSignal(tile_exit);
+				} else {
+					ClrBitTunnelBridgeSignal(tile_exit);
+					ClrBitTunnelBridgeExit(tile);
+					SetBitTunnelBridgeExit(tile_exit);
+					SetBitTunnelBridgeSignal(tile);
+				}
+			} else{
+				/* Create one direction tunnel/bridge if required. */
+				if (p2 == 0) {
+					SetBitTunnelBridgeSignal(tile);
+					SetBitTunnelBridgeExit(tile_exit);
+				} else if (p2 == 4 || p2 == 8) {
+					DiagDirection tbdir = GetTunnelBridgeDirection(tile);
+					/* If signal only on one side build accoringly one-way tunnel/bridge. */
+					if ((p2 == 8 && (tbdir == DIAGDIR_NE || tbdir == DIAGDIR_SE)) ||
+						(p2 == 4 && (tbdir == DIAGDIR_SW || tbdir == DIAGDIR_NW))) {
+						SetBitTunnelBridgeSignal(tile);
+						SetBitTunnelBridgeExit(tile_exit);
+					} else {
+						SetBitTunnelBridgeSignal(tile_exit);
+						SetBitTunnelBridgeExit(tile);
+					}
+				}
+			}
+			MarkTileDirtyByTile(tile);
+			MarkTileDirtyByTile(tile_exit);
+			AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company);
+			YapfNotifyTrackLayoutChange(tile, track);
+		}
+		return cost;
+	}
+
 	/* See if this is a valid track combination for signals (no overlap) */
 	if (TracksOverlap(GetTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
 
@@ -1079,7 +1129,6 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
 	/* you can not convert a signal if no signal is on track */
 	if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
 
-	CommandCost cost;
 	if (!HasSignalOnTrack(tile, track)) {
 		/* build new signals */
 		cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]);
@@ -1237,6 +1286,7 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal
 			return true;
 
 		case MP_TUNNELBRIDGE: {
+			if (!remove && HasWormholeSignals(tile)) return false;
 			TileIndex orig_tile = tile; // backup old value
 
 			if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
@@ -1348,7 +1398,8 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
 	bool had_success = false;
 	for (;;) {
 		/* only build/remove signals with the specified density */
-		if (remove || minimise_gaps || signal_ctr % signal_density == 0) {
+
+		if (remove || minimise_gaps || signal_ctr % signal_density == 0 || IsTileType(tile, MP_TUNNELBRIDGE)) {
 			uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3);
 			SB(p1, 3, 1, mode);
 			SB(p1, 4, 1, semaphores);
@@ -1384,13 +1435,20 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
 
 			/* Collect cost. */
 			if (!test_only) {
-				/* Be user-friendly and try placing signals as much as possible */
-				if (ret.Succeeded()) {
-					had_success = true;
-					total_cost.AddCost(ret);
-					last_used_ctr = last_suitable_ctr;
-					last_suitable_tile = INVALID_TILE;
+			/* Be user-friendly and try placing signals as much as possible */
+			if (ret.Succeeded()) {
+				had_success = true;
+				if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+					if ((!autofill && GetTunnelBridgeDirection(tile) == TrackdirToExitdir(trackdir)) ||
+							(autofill && GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir))) {
+						total_cost.AddCost(ret);
+					}
 				} else {
+					total_cost.AddCost(ret);
+				}
+				last_used_ctr = last_suitable_ctr;
+				last_suitable_tile = INVALID_TILE;
+			} else {
 					/* The "No railway" error is the least important one. */
 					if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
 							last_error.GetErrorMessage() == INVALID_STRING_ID) {
@@ -1461,22 +1519,48 @@ CommandCost CmdBuildSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1,
 CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Track track = Extract<Track, 0, 3>(p1);
+	Money cost = _price[PR_CLEAR_SIGNALS];
 
-	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
-		return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
-	}
-	if (!HasSignalOnTrack(tile, track)) {
-		return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
+	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+		TileIndex end = GetOtherTunnelBridgeEnd(tile);
+		if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
+		if (!HasWormholeSignals(tile)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
+
+		cost *= ((GetTunnelBridgeLength(tile, end) + 4) >> 2);
+
+		CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track);
+		//if (ret.Failed()) return ret;
+	} else {
+		if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
+			return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
+		}
+		if (!HasSignalOnTrack(tile, track)) {
+			return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
+		}
+		CommandCost ret = EnsureNoTrainOnTrack(tile, track);
+		//if (ret.Failed()) return ret;
 	}
 
 	/* Only water can remove signals from anyone */
 	if (_current_company != OWNER_WATER) {
-		CommandCost ret = CheckTileOwnership(tile);
-		if (ret.Failed()) return ret;
 	}
 
 	/* Do it? */
 	if (flags & DC_EXEC) {
+
+		if (HasWormholeSignals(tile)) { // handle tunnel/bridge signals.
+			TileIndex end = GetOtherTunnelBridgeEnd(tile);
+			ClrBitTunnelBridgeExit(tile);
+			ClrBitTunnelBridgeExit(end);
+			ClrBitTunnelBridgeSignal(tile);
+			ClrBitTunnelBridgeSignal(end);
+			_m[tile].m2 = 0;
+			_m[end].m2 = 0;
+			MarkTileDirtyByTile(tile);
+			MarkTileDirtyByTile(end);
+			return CommandCost(EXPENSES_CONSTRUCTION, cost);
+		}
+
 		Train *v = nullptr;
 		if (HasReservedTracks(tile, TrackToTrackBits(track))) {
 			v = GetTrainForReservation(tile, track);
@@ -1512,7 +1596,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
 		MarkTileDirtyByTile(tile);
 	}
 
-	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]);
+	return CommandCost(EXPENSES_CONSTRUCTION, cost);
 }
 
 /**
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 968be54f67..462d1a0e70 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1552,6 +1552,7 @@ static SettingsContainer &GetSettingsTree()
 			SettingsPage *construction = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION));
 			{
 				construction->Add(new SettingEntry("gui.link_terraform_toolbar"));
+				construction->Add(new SettingEntry("construction.simulated_wormhole_signals"));
 				construction->Add(new SettingEntry("gui.enable_signal_gui"));
 				construction->Add(new SettingEntry("gui.persistent_buildingtools"));
 				construction->Add(new SettingEntry("gui.quick_goto"));
diff --git a/src/settings_type.h b/src/settings_type.h
index fdb2b0f862..0cbc26d643 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -317,6 +317,7 @@ struct ConstructionSettings {
 	bool   freeform_edges;                   ///< allow terraforming the tiles at the map edges
 	uint8  extra_tree_placement;             ///< (dis)allow building extra trees in-game
 	uint8  command_pause_level;              ///< level/amount of commands that can't be executed while paused
+	byte   simulated_wormhole_signals;       ///< simulate signals in tunnel
 
 	uint32 terraform_per_64k_frames;         ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
 	uint16 terraform_frame_burst;            ///< how many tile heights may, over a short period, be terraformed?
diff --git a/src/signal.cpp b/src/signal.cpp
index 068ca6193b..b9627483ac 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -195,6 +195,14 @@ static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
 	return v;
 }
 
+/** Check whether there is a train only on ramp. */
+static Vehicle *TrainInWormholeTileEnum(Vehicle *v, void *data)
+{
+	/* Only look for front engine or last wagon. */
+	if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
+	if (*(TileIndex *)data != TileVirtXY(v->x_pos, v->y_pos)) return NULL;
+	return v;
+}
 
 /**
  * Perform some operations before adding data into Todo set
@@ -374,17 +382,39 @@ static SigFlags ExploreSegment(Owner owner)
 				if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
 				DiagDirection dir = GetTunnelBridgeDirection(tile);
 
-				if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
-					if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
-					enterdir = dir;
-					exitdir = ReverseDiagDir(dir);
-					tile += TileOffsByDiagDir(exitdir); // just skip to next tile
-				} else { // NOT incoming from the wormhole!
-					if (ReverseDiagDir(enterdir) != dir) continue;
-					if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
-					tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
-					enterdir = INVALID_DIAGDIR;
-					exitdir = INVALID_DIAGDIR;
+				if (HasWormholeSignals(tile)) {
+					if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
+						if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { // tunnel entrence is ignored
+							if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN;
+							if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN;
+						}
+						enterdir = dir;
+						exitdir = ReverseDiagDir(dir);
+						tile += TileOffsByDiagDir(exitdir); // just skip to next tile
+					} else { // NOT incoming from the wormhole!
+						if (ReverseDiagDir(enterdir) != dir) continue;
+						if (!(flags & SF_TRAIN)) {
+							if (HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN;
+							if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) {
+								if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN;
+							}
+						}
+						continue;
+					}
+				} else {
+					if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
+						if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
+						enterdir = dir;
+						exitdir = ReverseDiagDir(dir);
+						tile += TileOffsByDiagDir(exitdir); // just skip to next tile
+					} else { // NOT incoming from the wormhole!
+						if (ReverseDiagDir(enterdir) != dir) continue;
+						if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
+						tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
+						enterdir = INVALID_DIAGDIR;
+						exitdir = INVALID_DIAGDIR;
+					}
+
 				}
 				}
 				break;
@@ -492,7 +522,9 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
 				assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
 				assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
 				_tbdset.Add(tile, INVALID_DIAGDIR);  // we can safely start from wormhole centre
-				_tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
+				if (!HasWormholeSignals(tile)) {  // Don't worry with other side of tunnel.
+					_tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
+				}
 				break;
 
 			case MP_RAILWAY:
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 43bd3aec32..ab9a0336a5 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -541,6 +541,20 @@ str      = STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH
 strhelp  = STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT
 strval   = STR_CONFIG_SETTING_TILE_LENGTH
 
+[SDT_VAR]
+base     = GameSettings
+var      = construction.simulated_wormhole_signals
+type     = SLE_UINT8
+flags    = 0
+def      = 2
+min      = 1
+max      = 16
+str      = STR_CONFIG_SETTING_SIMULATE_SIGNALS
+strval   = STR_CONFIG_SETTING_SIMULATE_SIGNALS_VALUE
+proc     = RedrawScreen
+from     = SL_MIN_VERSION
+cat      = SC_BASIC
+
 # construction.longbridges
 [SDT_NULL]
 length   = 1
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 5d02764b55..1eddbf665e 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -1845,6 +1845,17 @@ void ReverseTrainDirection(Train *v)
 		return;
 	}
 
+	/* We are inside tunnel/bidge with signals, reversing will close the entrance. */
+	if (HasWormholeSignals(v->tile)) {
+		/* Flip signal on tunnel entrance tile red. */
+		SetBitTunnelBridgeExit(v->tile);
+		MarkTileDirtyByTile(v->tile);
+		/* Clear counters. */
+		v->wait_counter = 0;
+		v->load_unload_ticks = 0;
+		return;
+	}
+
 	/* VehicleExitDir does not always produce the desired dir for depots and
 	 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
 	DiagDirection dir = VehicleExitDir(v->direction, v->track);
@@ -2179,6 +2190,42 @@ static bool CheckTrainStayInDepot(Train *v)
 	return false;
 }
 
+static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDirection dir, bool free)
+{
+	if (IsBridge(end) && _m[end].m2 > 0){
+		/* Clearing last bridge signal. */
+		uint16 m = _m[end].m2;
+		byte i = 15;
+		while((m & 0x8000) == 0 && --i > 0) m <<= 1;
+		ClrBit(_m[end].m2, i);
+
+		uint x = TileX(end)* TILE_SIZE;
+		uint y = TileY(end)* TILE_SIZE;
+		uint distance = (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals) * ++i;
+		switch (dir) {
+			default: NOT_REACHED();
+			case DIAGDIR_NE: MarkTileDirtyByTile(TileVirtXY(x - distance, y)); break;
+			case DIAGDIR_SE: MarkTileDirtyByTile(TileVirtXY(x, y + distance)); break;
+			case DIAGDIR_SW: MarkTileDirtyByTile(TileVirtXY(x + distance, y)); break;
+			case DIAGDIR_NW: MarkTileDirtyByTile(TileVirtXY(x, y - distance)); break;
+		}
+		MarkTileDirtyByTile(tile);
+	}
+	if (free) {
+	/* Open up the wormhole and clear m2. */
+		_m[tile].m2 = 0;
+		_m[end].m2 = 0;
+
+		if (IsTunnelBridgeWithSignRed(end)) {
+			ClrBitTunnelBridgeExit(end);
+			if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(end);
+		} else if (IsTunnelBridgeWithSignRed(tile)) {
+			ClrBitTunnelBridgeExit(tile);
+			if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(tile);
+		}
+	}
+}
+
 /**
  * Clear the reservation of \a tile that was just left by a wagon on \a track_dir.
  * @param v %Train owning the reservation.
@@ -2194,7 +2241,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
 		if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
 			TileIndex end = GetOtherTunnelBridgeEnd(tile);
 
-			if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
+			bool free = TunnelBridgeIsFree(tile, end, v).Succeeded();
+			if (free) {
 				/* Free the reservation only if no other train is on the tiles. */
 				SetTunnelBridgeReservation(tile, false);
 				SetTunnelBridgeReservation(end, false);
@@ -2208,6 +2256,7 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
 					}
 				}
 			}
+			if (HasWormholeSignals(tile)) HandleLastTunnelBridgeSignals(tile, end, dir, free);
 		}
 	} else if (IsRailStationTile(tile)) {
 		TileIndex new_tile = TileAddByDiagDir(tile, dir);
@@ -3074,6 +3123,99 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
 	return t;
 }
 
+/** Find train in front and keep distance between trains in tunnel/bridge. */
+static Vehicle *FindSpaceBetweenTrainsEnum(Vehicle *v, void *data)
+{
+	/* Don't look at wagons between front and back of train. */
+	if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
+
+	const Vehicle *u = (Vehicle*)data;
+	int32 a, b = 0;
+
+	switch (u->direction) {
+		default: NOT_REACHED();
+		case DIR_NE: a = u->x_pos; b = v->x_pos; break;
+		case DIR_SE: a = v->y_pos; b = u->y_pos; break;
+		case DIR_SW: a = v->x_pos; b = u->x_pos; break;
+		case DIR_NW: a = u->y_pos; b = v->y_pos; break;
+	}
+
+	if (a > b && a <= (b + (int)(Train::From(u)->wait_counter)) + (int)(TILE_SIZE)) return v;
+	return NULL;
+}
+
+static bool IsToCloseBehindTrain(Vehicle *v, TileIndex tile, bool check_endtile)
+{
+	Train *t = (Train *)v;
+
+	if (t->force_proceed != TFP_NONE) return false;
+
+	if (HasVehicleOnPos(t->tile, v, &FindSpaceBetweenTrainsEnum)) {
+		/* Revert train if not going with tunnel direction. */
+		if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
+			v->cur_speed = 0;
+			ToggleBit(t->flags, VRF_REVERSING);
+		}
+		return true;
+	}
+    /* Cover blind spot at end of tunnel bridge. */
+	if (check_endtile){
+		if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), v, &FindSpaceBetweenTrainsEnum)) {
+			/* Revert train if not going with tunnel direction. */
+			if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
+				v->cur_speed = 0;
+				ToggleBit(t->flags, VRF_REVERSING);
+			}
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/** Simulate signals in tunnel - bridge. */
+static bool CheckTrainStayInWormHole(Train *t, TileIndex tile)
+{
+	if (t->force_proceed != TFP_NONE) return false;
+
+	/* When not exit reverse train. */
+	if (!IsTunnelBridgeExit(tile)) {
+		t->cur_speed = 0;
+		ToggleBit(t->flags, VRF_REVERSING);
+		return true;
+	}
+	SigSegState seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner);
+	if (seg_state == SIGSEG_FULL || (seg_state == SIGSEG_PBS && !TryPathReserve(t))) {
+		t->cur_speed = 0;
+		return true;
+	}
+
+	return false;
+}
+
+static void HandleSignalBehindTrain(Train *v, uint signal_number)
+{
+	TileIndex tile;
+	switch (v->direction) {
+		default: NOT_REACHED();
+		case DIR_NE: tile = TileVirtXY(v->x_pos + (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals), v->y_pos); break;
+		case DIR_SE: tile = TileVirtXY(v->x_pos, v->y_pos - (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals) ); break;
+		case DIR_SW: tile = TileVirtXY(v->x_pos - (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals), v->y_pos); break;
+		case DIR_NW: tile = TileVirtXY(v->x_pos, v->y_pos + (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals)); break;
+	}
+
+	if(tile == v->tile) {
+		/* Flip signal on ramp. */
+		if (IsTunnelBridgeWithSignRed(tile)) {
+			ClrBitTunnelBridgeExit(tile);
+			MarkTileDirtyByTile(tile);
+		}
+	} else if (IsBridge(v->tile) && signal_number <= 16) {
+		ClrBit(_m[v->tile].m2, signal_number);
+		MarkTileDirtyByTile(tile);
+	}
+}
+
 /**
  * Move a vehicle chain one movement stop forwards.
  * @param v First vehicle to move.
@@ -3259,6 +3401,23 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
 					goto invalid_rail;
 				}
 
+				if (HasWormholeSignals(gp.new_tile)) {
+					/* If red signal stop. */
+					if (v->IsFrontEngine() && v->force_proceed == TFP_NONE) {
+						if (IsTunnelBridgeWithSignRed(gp.new_tile)) {
+							v->cur_speed = 0;
+							return false;
+						}
+						if (IsTunnelBridgeExit(gp.new_tile)) {
+							v->cur_speed = 0;
+							goto invalid_rail;
+						}
+						/* Flip signal on tunnel entrance tile red. */
+						SetBitTunnelBridgeExit(gp.new_tile);
+						MarkTileDirtyByTile(gp.new_tile);
+					}
+				}
+
 				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
 					Track track = FindFirstTrack(chosen_track);
 					Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
@@ -3311,6 +3470,64 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
 				}
 			}
 		} else {
+			/* Handle signal simulation on tunnel/bridge. */
+			TileIndex old_tile = TileVirtXY(v->x_pos, v->y_pos);
+			if (old_tile != gp.new_tile && HasWormholeSignals(v->tile) && (v->IsFrontEngine() || v->Next() == NULL)){
+				if (old_tile == v->tile) {
+					if (v->IsFrontEngine() && v->force_proceed == TFP_NONE && IsTunnelBridgeExit(v->tile)) goto invalid_rail;
+					/* Entered wormhole set counters. */
+					v->wait_counter = (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals) - TILE_SIZE;
+					v->load_unload_ticks = 0;
+				}
+
+				uint distance = v->wait_counter;
+				bool leaving = false;
+				if (distance == 0) v->wait_counter = (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals);
+
+				if (v->IsFrontEngine()) {
+					/* Check if track in front is free and see if we can leave wormhole. */
+					int z = GetSlopePixelZ(gp.x, gp.y) - v->z_pos;
+					if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) &&	!(abs(z) > 2)) {
+						if (CheckTrainStayInWormHole(v, gp.new_tile)) return false;
+						leaving = true;
+					} else {
+						if (IsToCloseBehindTrain(v, gp.new_tile, distance == 0)) {
+							if (distance == 0) v->wait_counter = 0;
+							v->cur_speed = 0;
+							return false;
+						}
+						/* flip signal in front to red on bridges*/
+						if (distance == 0 && v->load_unload_ticks <= 15 && IsBridge(v->tile)){
+							SetBit(_m[v->tile].m2, v->load_unload_ticks);
+							MarkTileDirtyByTile(gp.new_tile);
+						}
+					}
+				}
+				if (v->Next() == NULL) {
+					if (v->load_unload_ticks > 0 && v->load_unload_ticks <= 16 && distance == (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals) - TILE_SIZE) HandleSignalBehindTrain(v, v->load_unload_ticks - 2);
+					if (old_tile == v->tile) {
+						/* We left ramp into wormhole. */
+						v->x_pos = gp.x;
+						v->y_pos = gp.y;
+						UpdateSignalsOnSegment(old_tile, INVALID_DIAGDIR, v->owner);
+					}
+				}
+				if (distance == 0) v->load_unload_ticks++;
+				v->wait_counter -= TILE_SIZE;
+
+				if (leaving) { // Reset counters.
+					v->force_proceed = TFP_NONE;
+					v->wait_counter = 0;
+					v->load_unload_ticks = 0;
+					v->x_pos = gp.x;
+					v->y_pos = gp.y;
+					v->UpdatePosition();
+					v->UpdateViewport(false, false);
+					UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
+					continue;
+				}
+			}
+
 			if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
 				/* Perform look-ahead on tunnel exit. */
 				if (v->IsFrontEngine()) {
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index c5e2f8d9bb..b693caee51 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -28,6 +28,7 @@
 #include "date_func.h"
 #include "clear_func.h"
 #include "vehicle_func.h"
+#include "vehicle_gui.h"
 #include "sound_func.h"
 #include "tunnelbridge.h"
 #include "cheat_type.h"
@@ -1226,6 +1227,103 @@ static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int off
 	}
 }
 
+/* Draws a signal on tunnel / bridge entrance tile. */
+static void DrawTunnelBridgeRampSignal(const TileInfo *ti)
+{
+	bool side = (_settings_game.vehicle.road_side != 0) &&_settings_game.construction.train_signal_side;
+
+	static const Point SignalPositions[2][4] = {
+		{   /*  X         X         Y         Y     Signals on the left side */
+			{13,  3}, { 2, 13}, { 3,  4}, {13, 14}
+		}, {/*  X         X         Y         Y     Signals on the right side */
+			{14, 13}, { 3,  3}, {13,  2}, { 3, 13}
+		}
+	};
+
+	uint position;
+	DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
+
+	switch (dir) {
+		default: NOT_REACHED();
+		case DIAGDIR_NE: position = 0; break;
+		case DIAGDIR_SE: position = 2; break;
+		case DIAGDIR_SW: position = 1; break;
+		case DIAGDIR_NW: position = 3; break;
+	}
+
+	uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x;
+	uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y;
+	uint z = ti->z;
+
+	if (ti->tileh != SLOPE_FLAT && IsBridge(ti->tile)) z += 8; // sloped bridge head
+	SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC);
+
+	SpriteID sprite;
+	if (variant == SIG_ELECTRIC) {
+		/* Normal electric signals are picked from original sprites. */
+		sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + IsTunnelBridgeWithSignGreen(ti->tile));
+	} else {
+		/* All other signals are picked from add on sprites. */
+		sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + IsTunnelBridgeWithSignGreen(ti->tile));
+	}
+
+	AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
+}
+
+/* Draws a signal on tunnel / bridge entrance tile. */
+static void DrawBrigeSignalOnMiddelPart(const TileInfo *ti, TileIndex bridge_start_tile, uint z)
+{
+
+	uint bridge_signal_position = 0;
+	int m2_position = 0;
+
+	uint bridge_section = GetTunnelBridgeLength(ti->tile, bridge_start_tile) + 1;
+
+	while (bridge_signal_position <= bridge_section) {
+		bridge_signal_position += _settings_game.construction.simulated_wormhole_signals;
+		if (bridge_signal_position == bridge_section) {
+			bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side;
+
+			static const Point SignalPositions[2][4] = {
+				{   /*  X         X         Y         Y     Signals on the left side */
+					{11,  3}, { 4, 13}, { 3,  4}, {11, 13}
+				}, {/*  X         X         Y         Y     Signals on the right side */
+					{11, 13}, { 4,  3}, {13,  4}, { 3, 11}
+				}
+			};
+
+			uint position;
+
+			switch (GetTunnelBridgeDirection(bridge_start_tile)) {
+				default: NOT_REACHED();
+				case DIAGDIR_NE: position = 0; break;
+				case DIAGDIR_SE: position = 2; break;
+				case DIAGDIR_SW: position = 1; break;
+				case DIAGDIR_NW: position = 3; break;
+			}
+
+			uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x;
+			uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y;
+			z += 5;
+
+			SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC);
+
+			SpriteID sprite;
+
+			if (variant == SIG_ELECTRIC) {
+				/* Normal electric signals are picked from original sprites. */
+				sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + !HasBit(_m[bridge_start_tile].m2, m2_position));
+			} else {
+				/* All other signals are picked from add on sprites. */
+				sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + !HasBit(_m[bridge_start_tile].m2, m2_position));
+			}
+
+			AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
+		}
+		m2_position++;
+	}
+}
+
 /**
  * Draws a tunnel of bridge tile.
  * For tunnels, this is rather simple, as you only need to draw the entrance.
@@ -1357,6 +1455,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
 		AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x,              ti->y,              BB_data[6], BB_data[7], TILE_HEIGHT, ti->z);
 		AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x + BB_data[4], ti->y + BB_data[5], BB_data[6], BB_data[7], TILE_HEIGHT, ti->z);
 
+		/* Draw signals for tunnel. */
+		if (IsTunnelBridgeEntrance(ti->tile)) DrawTunnelBridgeRampSignal(ti);
+
 		DrawBridgeMiddle(ti);
 	} else { // IsBridge(ti->tile)
 		const PalSpriteID *psid;
@@ -1462,6 +1563,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
 			}
 		}
 
+		/* Draw signals for bridge. */
+		if (HasWormholeSignals(ti->tile)) DrawTunnelBridgeRampSignal(ti);
+
 		DrawBridgeMiddle(ti);
 	}
 }
@@ -1603,6 +1707,9 @@ void DrawBridgeMiddle(const TileInfo *ti)
 		if (HasRailCatenaryDrawn(GetRailType(rampsouth))) {
 			DrawRailCatenaryOnBridge(ti);
 		}
+		if (HasWormholeSignals(rampsouth)) {
+			IsTunnelBridgeExit(rampsouth) ? DrawBrigeSignalOnMiddelPart(ti, rampnorth, z): DrawBrigeSignalOnMiddelPart(ti, rampsouth, z);
+		}
 	}
 
 	/* draw roof, the component of the bridge which is logically between the vehicle and the camera */
@@ -1691,9 +1798,9 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
 	TransportType tt = GetTunnelBridgeTransportType(tile);
 
 	if (IsTunnel(tile)) {
-		td->str = (tt == TRANSPORT_RAIL) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
+		td->str = (tt == TRANSPORT_RAIL) ? HasWormholeSignals(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
 	} else { // IsBridge(tile)
-		td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
+		td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : HasWormholeSignals(tile) ? STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
 	}
 	td->owner[0] = GetTileOwner(tile);
 
@@ -1776,6 +1883,25 @@ static void TileLoop_TunnelBridge(TileIndex tile)
 	}
 }
 
+static bool ClickTile_TunnelBridge(TileIndex tile)
+{
+	/* Show vehicles found in tunnel. */
+	if (IsTunnelTile(tile)) {
+		int count = 0;
+		TileIndex tile_end = GetOtherTunnelBridgeEnd(tile);
+		for (Train *t : Train::Iterate()) {
+			if (!t->IsFrontEngine()) continue;
+			if (tile == t->tile || tile_end == t->tile) {
+				ShowVehicleViewWindow(t);
+				count++;
+			}
+			if (count > 19) break;  // no more than 20 windows open
+		}
+		if (count > 0) return true;
+	}
+	return false;
+}
+
 static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
 {
 	TransportType transport_type = GetTunnelBridgeTransportType(tile);
@@ -2029,8 +2155,8 @@ extern const TileTypeProcs _tile_type_tunnelbridge_procs = {
 	nullptr,                            // add_accepted_cargo_proc
 	GetTileDesc_TunnelBridge,        // get_tile_desc_proc
 	GetTileTrackStatus_TunnelBridge, // get_tile_track_status_proc
-	nullptr,                            // click_tile_proc
-	nullptr,                            // animate_tile_proc
+	ClickTile_TunnelBridge,          // click_tile_proc
+	nullptr,                         // animate_tile_proc
 	TileLoop_TunnelBridge,           // tile_loop_proc
 	ChangeTileOwner_TunnelBridge,    // change_tile_owner_proc
 	nullptr,                            // add_produced_cargo_proc
diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
index 62d3c14b2d..fa029dcf4d 100644
--- a/src/tunnelbridge_map.h
+++ b/src/tunnelbridge_map.h
@@ -119,4 +119,98 @@ static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t)
 	return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE;
 }
 
+/**
+ * Declare tunnel/bridge with signal simulation.
+ * @param t the tunnel/bridge tile.
+ */
+static inline void SetBitTunnelBridgeSignal(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	SetBit(_m[t].m5, 5);
+}
+
+/**
+ * Remove tunnel/bridge with signal simulation.
+ * @param t the tunnel/bridge tile.
+ */
+static inline void ClrBitTunnelBridgeSignal(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	ClrBit(_m[t].m5, 5);
+}
+
+/**
+ * Declare tunnel/bridge exit.
+ * @param t the tunnel/bridge tile.
+ */
+static inline void SetBitTunnelBridgeExit(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	SetBit(_m[t].m5, 6);
+}
+
+/**
+ * Remove tunnel/bridge exit declaration.
+ * @param t the tunnel/bridge tile.
+ */
+static inline void ClrBitTunnelBridgeExit(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	ClrBit(_m[t].m5, 6);
+}
+
+/**
+ * Is this a tunnel/bridge pair with signal simulation?
+ * On tunnel/bridge pair minimal one of the two bits is set.
+ * @param t the tile that might be a tunnel/bridge.
+ * @return true if and only if this tile is a tunnel/bridge with signal simulation.
+ */
+static inline bool HasWormholeSignals(TileIndex t)
+{
+	return IsTileType(t, MP_TUNNELBRIDGE) && (HasBit(_m[t].m5, 5) || HasBit(_m[t].m5, 6)) ;
+}
+
+/**
+ * Is this a tunnel/bridge with sign on green?
+ * @param t the tile that might be a tunnel/bridge with sign set green.
+ * @pre IsTileType(t, MP_TUNNELBRIDGE)
+ * @return true if and only if this tile is a tunnel/bridge entrance.
+ */
+static inline bool IsTunnelBridgeWithSignGreen(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	return HasBit(_m[t].m5, 5) && !HasBit(_m[t].m5, 6);
+}
+
+static inline bool IsTunnelBridgeWithSignRed(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	return HasBit(_m[t].m5, 5) && HasBit(_m[t].m5, 6);
+}
+
+/**
+ * Is this a tunnel/bridge entrance tile with signal?
+ * Tunnel bridge signal simulation has allways bit 5 on at entrance.
+ * @param t the tile that might be a tunnel/bridge.
+ * @return true if and only if this tile is a tunnel/bridge entrance.
+ */
+static inline bool IsTunnelBridgeEntrance(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	return HasBit(_m[t].m5, 5) ;
+}
+
+/**
+ * Is this a tunnel/bridge exit?
+ * @param t the tile that might be a tunnel/bridge.
+ * @return true if and only if this tile is a tunnel/bridge exit.
+ */
+static inline bool IsTunnelBridgeExit(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	return !HasBit(_m[t].m5, 5) && HasBit(_m[t].m5, 6);
+}
+
+
+
 #endif /* TUNNELBRIDGE_MAP_H */