diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 6cdf4c7e43..80b9b8b984 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -364,7 +364,7 @@ protected: if (IsTunnel(m_new_tile)) { if (!m_is_tunnel) { DiagDirection tunnel_enterdir = GetTunnelBridgeDirection(m_new_tile); - if (tunnel_enterdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { + if (tunnel_enterdir != m_exitdir) { m_err = EC_NO_WAY; return false; } @@ -372,7 +372,7 @@ protected: } else { // IsBridge(m_new_tile) if (!m_is_bridge) { DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile); - if (ramp_enderdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { + if (ramp_enderdir != m_exitdir) { m_err = EC_NO_WAY; return false; } diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 6b07125f8c..daab4d3779 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -943,6 +943,10 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) break; } } + if (IsTileType(dst_tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(dst_tile) && DiagDirToDiagTrackdir(GetTunnelBridgeDirection(dst_tile)) == dst_trackdir) { + /* Entering a signalled bridge/tunnel from the wrong side, equivalent to encountering a one-way signal from the wrong side */ + break; + } { /* We've found ourselves a neighbour :-) */ AyStarNode *neighbour = &aystar->neighbours[i]; diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index f249ede245..fe375ec1a3 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -360,6 +360,10 @@ public: } } } + if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(tile) && DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile)) == trackdir) { + /* Entering a signalled bridge/tunnel from the wrong side, equivalent to encountering a one-way signal from the wrong side */ + n.m_segment->m_end_segment_reason |= ESRB_DEAD_END; + } return cost; } diff --git a/src/pbs.cpp b/src/pbs.cpp index a4eb1096c5..2cb447a89c 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -143,6 +143,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetTunnelBridgeReservationTrackBits(tile)) { SetTunnelBridgeReservation(tile, true); + if (IsTunnelBridgeExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitGreen(tile, true); return true; } break; @@ -195,7 +196,10 @@ void UnreserveRailTrack(TileIndex tile, Track t) break; case MP_TUNNELBRIDGE: - if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) SetTunnelBridgeReservation(tile, false); + if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) { + SetTunnelBridgeReservation(tile, false); + if (IsTunnelBridgeExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitGreen(tile, false); + } break; default: @@ -264,6 +268,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra if (IsRailDepotTile(tile)) break; /* Non-pbs signal? Reservation can't continue. */ if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break; + if (IsTileType(tile, MP_TUNNELBRIDGE) && HasWormholeSignals(tile)) break; } return PBSTileInfo(tile, trackdir, false); @@ -332,7 +337,7 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res) if (ftoti.best != NULL) *train_on_res = ftoti.best->First(); } } - if (*train_on_res == NULL && IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) { + if (*train_on_res == NULL && IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE) && !HasWormholeSignals(ftoti.res.tile)) { /* The target tile is a bridge/tunnel, also check the other end tile. */ FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum); if (ftoti.best != NULL) *train_on_res = ftoti.best->First(); @@ -447,6 +452,12 @@ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bo if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true; } + if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) { + if (IsTunnelBridgeEntrance(tile)) { + return true; + } + } + /* Check next tile. For performance reasons, we check for 90 degree turns ourself. */ CFollowTrackRail ft(v, GetRailTypeInfo(v->railtype)->compatible_railtypes); @@ -507,6 +518,7 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo /* Not reserved and depot or not a pbs signal -> free. */ if (IsRailDepotTile(tile)) return true; if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true; + if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && IsTunnelBridgeEntrance(tile)) return true; /* Check the next tile, if it's a PBS signal, it has to be free as well. */ CFollowTrackRail ft(v, GetRailTypeInfo(v->railtype)->compatible_railtypes); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index f410452b5b..37b9098c46 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1058,21 +1058,46 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, if (IsTileType(tile, MP_TUNNELBRIDGE)) { TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile); cost = CommandCost(); + bool flip_variant = false; + bool is_pbs = (sigtype == SIGTYPE_PBS) || (sigtype == SIGTYPE_PBS_ONEWAY); if (!HasWormholeSignals(tile)) { // toggle signal zero costs. + if (convert_signal) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); if (p2 != 12) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2)); // minimal 1 + } else { + if (HasBit(p1, 17)) return CommandCost(); + if ((p2 != 0 && (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)) || + (convert_signal && (ctrl_pressed || (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)))) { + flip_variant = true; + cost = CommandCost(EXPENSES_CONSTRUCTION, (_price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_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); + if (convert_signal) { + if (flip_variant) { + SetTunnelBridgeSemaphore(tile, !IsTunnelBridgeSemaphore(tile)); + SetTunnelBridgeSemaphore(tile_exit, IsTunnelBridgeSemaphore(tile)); + } + if (!ctrl_pressed) { + SetTunnelBridgePBS(tile, is_pbs); + SetTunnelBridgePBS(tile_exit, is_pbs); + } + } else if (ctrl_pressed) { + SetTunnelBridgePBS(tile, !IsTunnelBridgePBS(tile)); + SetTunnelBridgePBS(tile_exit, IsTunnelBridgePBS(tile)); } else { - ClrBitTunnelBridgeSignal(tile_exit); - ClrBitTunnelBridgeExit(tile); - SetBitTunnelBridgeExit(tile_exit); - SetBitTunnelBridgeSignal(tile); + 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. */ @@ -1084,14 +1109,24 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, /* 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))) { + ClrBitTunnelBridgeExit(tile); + ClrBitTunnelBridgeSignal(tile_exit); SetBitTunnelBridgeSignal(tile); SetBitTunnelBridgeExit(tile_exit); } else { + ClrBitTunnelBridgeSignal(tile); + ClrBitTunnelBridgeExit(tile_exit); SetBitTunnelBridgeSignal(tile_exit); SetBitTunnelBridgeExit(tile); } } + SetTunnelBridgeSemaphore(tile, sigvar == SIG_SEMAPHORE); + SetTunnelBridgeSemaphore(tile_exit, sigvar == SIG_SEMAPHORE); + SetTunnelBridgePBS(tile, is_pbs); + SetTunnelBridgePBS(tile_exit, is_pbs); } + if (IsTunnelBridgeExit(tile) && IsTunnelBridgePBS(tile) && !HasTunnelBridgeReservation(tile)) SetTunnelBridgeExitGreen(tile, false); + if (IsTunnelBridgeExit(tile_exit) && IsTunnelBridgePBS(tile_exit) && !HasTunnelBridgeReservation(tile_exit)) SetTunnelBridgeExitGreen(tile_exit, false); MarkBridgeOrTunnelDirty(tile); AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile)); AddSideToSignalBuffer(tile_exit, INVALID_DIAGDIR, GetTileOwner(tile)); diff --git a/src/rail_map.h b/src/rail_map.h index 584524394d..ee6415037e 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -18,6 +18,7 @@ #include "track_func.h" #include "tile_map.h" #include "signal_type.h" +#include "tunnelbridge_map.h" /** Different types of Rail-related tiles */ @@ -479,8 +480,15 @@ static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td) */ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) { - return IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && - !HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td)); + if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && + !HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td))) { + return true; + } + if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(tile) && + DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile)) == td) { + return true; + } + return false; } /** diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d0a02c08cf..107ab06249 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3181,6 +3181,18 @@ bool AfterLoadGame() } } + if (SlXvIsFeaturePresent(XSLFI_SIG_TUNNEL_BRIDGE, 1, 1)) { + /* set the semaphore bit to match what it would have been in v1 */ + /* clear the PBS bit, update the end signal state */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL && HasWormholeSignals(t)) { + SetTunnelBridgeSemaphore(t, _cur_year < _settings_client.gui.semaphore_build_before); + SetTunnelBridgePBS(t, false); + UpdateSignalsOnSegment(t, INVALID_DIAGDIR, GetTileOwner(t)); + } + } + } + /* Station acceptance is some kind of cache */ if (IsSavegameVersionBefore(127)) { Station *st; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 147dd7cc43..41f8bf905e 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -54,7 +54,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", NULL, NULL, NULL }, { XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 1, 1, "timetable_start_ticks", NULL, NULL, NULL }, { XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 1, 1, "town_cargo_adj", NULL, NULL, NULL }, - { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 1, 1, "signal_tunnel_bridge", NULL, NULL, NULL }, + { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 2, 2, "signal_tunnel_bridge", NULL, NULL, NULL }, { XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 3, 3, "improved_breakdowns", NULL, NULL, NULL }, { XSLFI_TT_WAIT_IN_DEPOT, XSCF_NULL, 1, 1, "tt_wait_in_depot", NULL, NULL, NULL }, { XSLFI_AUTO_TIMETABLE, XSCF_NULL, 3, 3, "auto_timetables", NULL, NULL, NULL }, diff --git a/src/signal.cpp b/src/signal.cpp index f42dd8d09a..8a73e31f84 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -414,11 +414,23 @@ static SigInfo ExploreSegment(Owner owner) if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN; if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN; } + if (IsTunnelBridgeExit(tile) && !_tbuset.Add(tile, INVALID_TRACKDIR)) { + info.flags |= SF_FULL; + return info; + } 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 (IsTunnelBridgeExit(tile)) { + if (IsTunnelBridgePBS(tile)) { + info.flags |= SF_PBS; + } else if (!_tbuset.Add(tile, INVALID_TRACKDIR)) { + info.flags |= SF_FULL; + return info; + } + } if (!(info.flags & SF_TRAIN)) { if (HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN; if (!(info.flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { @@ -469,6 +481,17 @@ static void UpdateSignalsAroundSegment(SigInfo info) Track track; while (_tbuset.Get(&tile, &trackdir)) { + if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(tile)) { + if (IsTunnelBridgePBS(tile)) continue; + bool old_state = IsTunnelBridgeExitGreen(tile); + bool new_state = !(info.flags & SF_TRAIN); + if (old_state != new_state) { + SetTunnelBridgeExitGreen(tile, new_state); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + } + continue; + } + assert(HasSignalOnTrackdir(tile, trackdir)); track = TrackdirToTrack(trackdir); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 119ab8e961..211cfe6519 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2376,6 +2376,12 @@ static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDir } } +static void UnreserveBridgeTunnelTile(TileIndex tile) +{ + SetTunnelBridgeReservation(tile, false); + if (IsTunnelBridgeExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitGreen(tile, false); +} + /** * Clear the reservation of \a tile that was just left by a wagon on \a track_dir. * @param v %Train owning the reservation. @@ -2392,10 +2398,16 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ TileIndex end = GetOtherTunnelBridgeEnd(tile); bool free = TunnelBridgeIsFree(tile, end, v).Succeeded(); - if (free) { + if (HasWormholeSignals(tile)) { + UnreserveBridgeTunnelTile(tile); + HandleLastTunnelBridgeSignals(tile, end, dir, free); + if (_settings_client.gui.show_track_reservation) { + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + } + } else if (free) { /* Free the reservation only if no other train is on the tiles. */ - SetTunnelBridgeReservation(tile, false); - SetTunnelBridgeReservation(end, false); + UnreserveBridgeTunnelTile(tile); + UnreserveBridgeTunnelTile(end); if (_settings_client.gui.show_track_reservation) { if (IsBridge(tile)) { @@ -2406,7 +2418,12 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ } } } - if (HasWormholeSignals(tile)) HandleLastTunnelBridgeSignals(tile, end, dir, free); + } else if (GetTunnelBridgeDirection(tile) == dir && HasWormholeSignals(tile)) { + /* cancelling reservation of entry ramp, due to reverse */ + UnreserveBridgeTunnelTile(tile); + if (_settings_client.gui.show_track_reservation) { + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + } } } else if (IsRailStationTile(tile)) { TileIndex new_tile = TileAddByDiagDir(tile, dir); @@ -2935,6 +2952,12 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay) } } + if (IsTileType(v->tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(v->tile) && + DiagDirToDiagTrackBits(GetTunnelBridgeDirection(v->tile)) == v->track) { + // prevent any attempt to reserve the wrong way onto a tunnel/bridge exit + return false; + } + Vehicle *other_train = NULL; PBSTileInfo origin = FollowTrainReservation(v, &other_train); /* The path we are driving on is already blocked by some other train. @@ -3167,6 +3190,11 @@ static TrainMovedChangeSignalEnum TrainMovedChangeSignal(Train* v, TileIndex til if (HasLongReservePbsSignalOnTrackdir(v, tile, trackdir)) return CHANGED_LR_PBS; } } + if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeExit(tile) && GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { + if (UpdateSignalsOnSegment(tile, dir, GetTileOwner(tile)) == SIGSEG_PBS) { + return CHANGED_NORMAL_TO_PBS_BLOCK; + } + } return CHANGED_NOTHING; } @@ -3208,7 +3236,7 @@ uint Train::Crash(bool flooded) if (IsTileType(v->tile, MP_TUNNELBRIDGE)) { /* ClearPathReservation will not free the wormhole exit * if the train has just entered the wormhole. */ - SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(v->tile), false); + UnreserveBridgeTunnelTile(GetOtherTunnelBridgeEnd(v->tile)); } } @@ -3402,6 +3430,23 @@ static bool IsToCloseBehindTrain(Vehicle *v, TileIndex tile, bool check_endtile) return false; } +static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile) +{ + TileIndex veh_orig = t->tile; + t->tile = tile; + CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes); + if (ft.Follow(tile, DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))))) { + TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile)); + if (reserved == TRACKDIR_BIT_NONE) { + /* next tile is not reserved, so reserve the exit tile */ + SetTunnelBridgeReservation(tile, true); + } + } + bool ok = TryPathReserve(t); + t->tile = veh_orig; + return ok; +} + /** Simulate signals in tunnel - bridge. */ static bool CheckTrainStayInWormHole(Train *t, TileIndex tile) { @@ -3413,8 +3458,20 @@ static bool CheckTrainStayInWormHole(Train *t, TileIndex tile) 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))) { + SigSegState seg_state = (_settings_game.pf.reserve_paths || IsTunnelBridgePBS(tile)) ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner); + if (seg_state != SIGSEG_PBS) { + CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes); + if (ft.Follow(tile, DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))))) { + if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) { + Trackdir td = FindFirstTrackdir(ft.m_new_td_bits); + if (HasPbsSignalOnTrackdir(ft.m_new_tile, td)) { + /* immediately after the exit, there is a PBS signal, switch to PBS mode */ + seg_state = SIGSEG_PBS; + } + } + } + } + if (seg_state == SIGSEG_FULL || (seg_state == SIGSEG_PBS && !CheckTrainStayInWormHolePathReserve(t, tile))) { t->vehstatus |= VS_TRAIN_SLOWING; t->cur_speed = 0; return true; @@ -3724,7 +3781,7 @@ 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 != gp.new_tile && HasWormholeSignals(v->tile) && (v->IsFrontEngine() || v->Next() == NULL)) { if (old_tile == v->tile) { if (v->IsFrontEngine() && v->force_proceed == 0 && IsTunnelBridgeExit(v->tile)) goto invalid_rail; /* Entered wormhole set counters. */ @@ -3763,6 +3820,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) v->x_pos = gp.x; v->y_pos = gp.y; UpdateSignalsOnSegment(old_tile, INVALID_DIAGDIR, v->owner); + UnreserveBridgeTunnelTile(old_tile); } } if (distance == 0) v->load_unload_ticks++; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 5835457078..a2c671d814 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -1167,27 +1167,44 @@ static void DrawTunnelBridgeRampSignal(const TileInfo *ti) 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; + SignalType type = SIGTYPE_NORMAL; + + bool is_green; + bool show_exit; + if (IsTunnelBridgeExit(ti->tile)) { + is_green = IsTunnelBridgeExitGreen(ti->tile); + show_exit = true; + position ^= 1; + if (IsTunnelBridgePBS(ti->tile)) type = SIGTYPE_PBS_ONEWAY; + } else { + is_green = IsTunnelBridgeWithSignGreen(ti->tile); + show_exit = false; + } + + uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side != show_exit][position ^ show_exit].x; + uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side != show_exit][position ^ show_exit].y; uint z = ti->z; + if (ti->tileh == SLOPE_FLAT && side == show_exit && dir == DIAGDIR_SE) z += 2; + if (ti->tileh == SLOPE_FLAT && side != show_exit && dir == DIAGDIR_SW) z += 2; + 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); + SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC; SpriteID sprite; - if (variant == SIG_ELECTRIC) { + if (variant == SIG_ELECTRIC && type == SIGTYPE_NORMAL) { /* Normal electric signals are picked from original sprites. */ - sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + IsTunnelBridgeWithSignGreen(ti->tile)); + sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + is_green); } 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)); + sprite = SPR_SIGNALS_BASE + ((type - 1) * 16 + variant * 64 + (position << 1) + is_green) + (IsSignalSpritePBS(type) ? 64 : 0); } 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) +static void DrawBrigeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_start_tile, uint z) { uint bridge_signal_position = 0; @@ -1222,7 +1239,7 @@ static void DrawBrigeSignalOnMiddelPart(const TileInfo *ti, TileIndex bridge_sta 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); + SignalVariant variant = IsTunnelBridgeSemaphore(bridge_start_tile) ? SIG_SEMAPHORE : SIG_ELECTRIC; SpriteID sprite; @@ -1355,7 +1372,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti) 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); + if (HasWormholeSignals(ti->tile)) DrawTunnelBridgeRampSignal(ti); DrawBridgeMiddle(ti); } else { // IsBridge(ti->tile) @@ -1601,7 +1618,8 @@ void DrawBridgeMiddle(const TileInfo *ti) } } - if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES) && HasTunnelBridgeReservation(rampnorth)) { + if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES) && HasTunnelBridgeReservation(rampnorth) + && !HasWormholeSignals(rampnorth)) { if (rti->UsesOverlay()) { SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); AddSortableSpriteToDraw(overlay + RTO_X + axis, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, bridge_z, IsTransparencySet(TO_BRIDGES)); @@ -1616,7 +1634,7 @@ void DrawBridgeMiddle(const TileInfo *ti) DrawCatenaryOnBridge(ti); } if (HasWormholeSignals(rampsouth)) { - IsTunnelBridgeExit(rampsouth) ? DrawBrigeSignalOnMiddelPart(ti, rampnorth, z): DrawBrigeSignalOnMiddelPart(ti, rampsouth, z); + IsTunnelBridgeExit(rampsouth) ? DrawBrigeSignalOnMiddlePart(ti, rampnorth, z): DrawBrigeSignalOnMiddlePart(ti, rampsouth, z); } } diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 57f338b8be..2196f794f1 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -213,6 +213,40 @@ static inline bool IsTunnelBridgeExit(TileIndex t) return !HasBit(_m[t].m5, 5) && HasBit(_m[t].m5, 6); } +static inline bool IsTunnelBridgeExitGreen(TileIndex t) +{ + assert(IsTunnelBridgeExit(t)); + return HasBit(_me[t].m6, 0); +} +static inline void SetTunnelBridgeExitGreen(TileIndex t, bool green) +{ + assert(IsTunnelBridgeExit(t)); + SB(_me[t].m6, 0, 1, green ? 1 : 0); +} + +static inline bool IsTunnelBridgeSemaphore(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE) && HasWormholeSignals(t)); + return HasBit(_me[t].m6, 1); +} + +static inline void SetTunnelBridgeSemaphore(TileIndex t, bool is_semaphore) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE) && HasWormholeSignals(t)); + SB(_me[t].m6, 1, 1, is_semaphore ? 1 : 0); +} + +static inline bool IsTunnelBridgePBS(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE) && HasWormholeSignals(t)); + return HasBit(_me[t].m6, 6); +} + +static inline void SetTunnelBridgePBS(TileIndex t, bool is_pbs) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE) && HasWormholeSignals(t)); + SB(_me[t].m6, 6, 1, is_pbs ? 1 : 0); +} #endif /* TUNNELBRIDGE_MAP_H */