Merge branch 'custom_bridgeheads' into jgrpp

# Conflicts:
#	docs/landscape.html
#	src/bridge_map.h
#	src/pbs.cpp
#	src/saveload/company_sl.cpp
#	src/saveload/extended_ver_sl.cpp
#	src/settings_type.h
#	src/signal.cpp
#	src/train_cmd.cpp
#	src/tunnel_map.h
#	src/tunnelbridge_cmd.cpp
#	src/tunnelbridge_map.h
#	src/vehicle.cpp
#	src/viewport.cpp
This commit is contained in:
Jonathan G Rennison
2018-07-08 23:04:30 +01:00
29 changed files with 1313 additions and 362 deletions

View File

@@ -591,7 +591,7 @@ int Train::GetCurrentMaxSpeed() const
}
/* Vehicle is on the middle part of a bridge. */
if (u->track == TRACK_BIT_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) {
if (u->track & TRACK_BIT_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) {
max_speed = min(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
}
}
@@ -1792,17 +1792,17 @@ static void UpdateStatusAfterSwap(Train *v)
if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction);
/* Call the proper EnterTile function unless we are in a wormhole. */
if (v->track != TRACK_BIT_WORMHOLE) {
if (!(v->track & TRACK_BIT_WORMHOLE)) {
VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
} else {
/* VehicleEnter_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
/* VehicleEnter_TunnelBridge() may set TRACK_BIT_WORMHOLE when the vehicle
* is on the last bit of the bridge head (frame == TILE_SIZE - 1).
* If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
* when we shouldn't have. Check if this is the case. */
TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
if (IsTileType(vt, MP_TUNNELBRIDGE)) {
VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
if (!(v->track & TRACK_BIT_WORMHOLE) && IsBridgeTile(v->tile)) {
/* We have just left the wormhole, possibly set the
* "goingdown" bit. UpdateInclination() can be used
* because we are at the border of the tile. */
@@ -1814,7 +1814,7 @@ static void UpdateStatusAfterSwap(Train *v)
}
v->UpdatePosition();
if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
if (v->track & TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
v->UpdateViewport(true, true);
}
@@ -2182,7 +2182,7 @@ void ReverseTrainDirection(Train *v)
}
/* We are inside tunnel/bridge with signals, reversing will close the entrance. */
if (IsTunnelBridgeWithSignalSimulation(v->tile)) {
if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationEntrance(v->tile)) {
/* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(v->tile, SIGNAL_STATE_RED);
MarkTileDirtyByTile(v->tile);
@@ -2195,7 +2195,7 @@ void ReverseTrainDirection(Train *v)
/* 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);
if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR;
if (IsRailDepotTile(v->tile) || (IsTileType(v->tile, MP_TUNNELBRIDGE) && (v->track & TRACK_BIT_WORMHOLE || dir == GetTunnelBridgeDirection(v->tile)))) dir = INVALID_DIAGDIR;
if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
/* If we are currently on a tile with conventional signals, we can't treat the
@@ -2597,7 +2597,7 @@ static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDir
static void UnreserveBridgeTunnelTile(TileIndex tile)
{
SetTunnelBridgeReservation(tile, false);
UnreserveAcrossRailTunnelBridge(tile);
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
}
@@ -2610,27 +2610,36 @@ static void UnreserveBridgeTunnelTile(TileIndex tile)
*/
static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir, bool tunbridge_clear_unsignaled_other_end = false)
{
DiagDirection dir = TrackdirToExitdir(track_dir);
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UnreserveBridgeTunnelTile(tile);
if (IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(track_dir))) {
UnreserveBridgeTunnelTile(tile);
if (IsTunnelBridgeWithSignalSimulation(tile)) {
/* Are we just leaving a tunnel/bridge? */
if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
bool free = TunnelBridgeIsFree(tile, end, v).Succeeded();
HandleLastTunnelBridgeSignals(tile, end, dir, free);
if (IsTunnelBridgeWithSignalSimulation(tile)) {
/* Are we just leaving a tunnel/bridge? */
if (TrackdirExitsTunnelBridge(tile, track_dir)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
bool free = TunnelBridgeIsFree(tile, end, v, true).Succeeded();
HandleLastTunnelBridgeSignals(tile, end, ReverseDiagDir(GetTunnelBridgeDirection(tile)), free);
}
} else if (tunbridge_clear_unsignaled_other_end) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UnreserveAcrossRailTunnelBridge(end);
if (_settings_client.gui.show_track_reservation) {
MarkTileDirtyByTile(end, ZOOM_LVL_DRAW_MAP);
}
}
} else if (tunbridge_clear_unsignaled_other_end) {
UnreserveBridgeTunnelTile(end);
}
if (_settings_client.gui.show_track_reservation) {
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
MarkTileDirtyByTile(end, ZOOM_LVL_DRAW_MAP);
if (_settings_client.gui.show_track_reservation) {
MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
}
} else {
UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
if (_settings_client.gui.show_track_reservation) {
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
}
}
} else if (IsRailStationTile(tile)) {
DiagDirection dir = TrackdirToExitdir(track_dir);
TileIndex new_tile = TileAddByDiagDir(tile, dir);
/* If the new tile is not a further tile of the same station, we
* clear the reservation for the whole platform. */
@@ -2696,11 +2705,10 @@ void FreeTrainTrackReservation(const Train *v, TileIndex origin, Trackdir orig_t
} else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
break;
}
} else if (IsTunnelBridgeWithSignalSimulation(tile)) {
} else if (IsTunnelBridgeWithSignalSimulation(tile) && TrackdirExitsTunnelBridge(tile, td)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
bool free = TunnelBridgeIsFree(tile, end, v).Succeeded();
if (!free && GetTunnelBridgeDirection(tile) == ReverseDiagDir(TrackdirToExitdir(td))) break;
bool free = TunnelBridgeIsFree(tile, end, v, true).Succeeded();
if (!free) break;
}
/* Don't free first station/bridge/tunnel if we are on it. */
@@ -3189,7 +3197,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
}
if (IsTileType(v->tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(v->tile) &&
DiagDirToDiagTrackBits(GetTunnelBridgeDirection(v->tile)) == v->track) {
(GetAcrossTunnelBridgeTrackBits(v->tile) & v->track)) {
// prevent any attempt to reserve the wrong way onto a tunnel/bridge exit
return false;
}
@@ -3246,7 +3254,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
static bool CheckReverseTrain(const Train *v)
{
if (_settings_game.difficulty.line_reverse_mode != 0 ||
v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
v->track == TRACK_BIT_DEPOT || v->track & TRACK_BIT_WORMHOLE ||
!(v->direction & 1)) {
return false;
}
@@ -3441,15 +3449,18 @@ static TrainMovedChangeSignalEnum TrainMovedChangeSignal(Train* v, TileIndex til
void Train::ReserveTrackUnderConsist() const
{
for (const Train *u = this; u != NULL; u = u->Next()) {
switch (u->track) {
case TRACK_BIT_WORMHOLE:
if (u->track & TRACK_BIT_WORMHOLE) {
if (IsRailCustomBridgeHeadTile(u->tile)) {
/* reserve the first available track */
TrackBits bits = GetAcrossTunnelBridgeTrackBits(u->tile);
Track first_track = RemoveFirstTrack(&bits);
assert(IsValidTrack(first_track));
TryReserveRailTrack(u->tile, first_track);
} else {
TryReserveRailTrack(u->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile)));
break;
case TRACK_BIT_DEPOT:
break;
default:
TryReserveRailTrack(u->tile, TrackBitsToTrack(u->track));
break;
}
} else if (u->track != TRACK_BIT_DEPOT) {
TryReserveRailTrack(u->tile, TrackBitsToTrack(u->track));
}
}
}
@@ -3578,14 +3589,14 @@ static bool CheckTrainCollision(Train *v)
/* can't collide in depot */
if (v->track == TRACK_BIT_DEPOT) return false;
assert(v->track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
assert(v->track & TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
TrainCollideChecker tcc;
tcc.v = v;
tcc.num = 0;
/* find colliding vehicles */
if (v->track == TRACK_BIT_WORMHOLE) {
if (v->track & TRACK_BIT_WORMHOLE) {
FindVehicleOnPos(v->tile, &tcc, FindTrainCollideEnum);
FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &tcc, FindTrainCollideEnum);
} else {
@@ -3621,7 +3632,7 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
struct FindSpaceBetweenTrainsChecker {
int32 pos;
uint16 distance;
DirectionByte direction;
DiagDirectionByte direction;
};
/** Find train in front and keep distance between trains in tunnel/bridge. */
@@ -3630,15 +3641,20 @@ 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;
if (!IsDiagonalDirection(v->direction)) {
/* Check for vehicles on non-across track pieces of custom bridge head */
if ((GetAcrossTunnelBridgeTrackBits(v->tile) & Train::From(v)->track & TRACK_BIT_ALL) == TRACK_BIT_NONE) return NULL;
}
const FindSpaceBetweenTrainsChecker *checker = (FindSpaceBetweenTrainsChecker*) data;
int32 a, b = 0;
switch (checker->direction) {
default: NOT_REACHED();
case DIR_NE: a = checker->pos; b = v->x_pos; break;
case DIR_SE: a = v->y_pos; b = checker->pos; break;
case DIR_SW: a = v->x_pos; b = checker->pos; break;
case DIR_NW: a = checker->pos; b = v->y_pos; break;
case DIAGDIR_NE: a = checker->pos; b = v->x_pos; break;
case DIAGDIR_SE: a = v->y_pos; b = checker->pos; break;
case DIAGDIR_SW: a = v->x_pos; b = checker->pos; break;
case DIAGDIR_NW: a = checker->pos; b = v->y_pos; break;
}
if (a > b && a <= (b + (int)(checker->distance)) + (int)(TILE_SIZE) - 1) return v;
@@ -3653,18 +3669,18 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
FindSpaceBetweenTrainsChecker checker;
checker.distance = distance;
checker.direction = t->direction;
checker.direction = DirToDiagDirAlongAxis(t->direction, DiagDirToAxis(GetTunnelBridgeDirection(t->tile)));
switch (checker.direction) {
default: NOT_REACHED();
case DIR_NE: checker.pos = (TileX(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
case DIR_SE: checker.pos = (TileY(tile) * TILE_SIZE); break;
case DIR_SW: checker.pos = (TileX(tile) * TILE_SIZE); break;
case DIR_NW: checker.pos = (TileY(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
case DIAGDIR_NE: checker.pos = (TileX(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
case DIAGDIR_SE: checker.pos = (TileY(tile) * TILE_SIZE); break;
case DIAGDIR_SW: checker.pos = (TileX(tile) * TILE_SIZE); break;
case DIAGDIR_NW: checker.pos = (TileY(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
}
if (HasVehicleOnPos(t->tile, &checker, &FindSpaceBetweenTrainsEnum)) {
/* Revert train if not going with tunnel direction. */
if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
if (checker.direction != GetTunnelBridgeDirection(t->tile)) {
SetBit(t->flags, VRF_REVERSING);
}
return true;
@@ -3673,7 +3689,7 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
if (check_endtile){
if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), &checker, &FindSpaceBetweenTrainsEnum)) {
/* Revert train if not going with tunnel direction. */
if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
if (checker.direction != GetTunnelBridgeDirection(t->tile)) {
SetBit(t->flags, VRF_REVERSING);
}
return true;
@@ -3685,19 +3701,30 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
{
TileIndex veh_orig = t->tile;
Trackdir td = TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)), ReverseDiagDir(GetTunnelBridgeDirection(tile)));
TileIndex veh_orig_tile = t->tile;
TrackBits veh_orig_track = t->track;
Direction veh_orig_direction = t->direction;
t->tile = tile;
t->track = TRACK_BIT_WORMHOLE;
t->direction = TrackdirToDirection(td);
CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes);
if (ft.Follow(tile, DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))))) {
if (ft.Follow(tile, td)) {
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);
if (IsBridge(tile)) {
TryReserveRailBridgeHead(tile, FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)));
} else {
SetTunnelReservation(tile, true);
}
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
}
}
bool ok = TryPathReserve(t);
t->tile = veh_orig;
t->tile = veh_orig_tile;
t->track = veh_orig_track;
t->direction = veh_orig_direction;
if (ok && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
return ok;
}
@@ -3715,7 +3742,7 @@ static bool CheckTrainStayInWormHole(Train *t, TileIndex tile)
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.Follow(tile, TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)), 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)) {
@@ -3779,6 +3806,18 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
Train *first = v->First();
Train *prev;
bool direction_changed = false; // has direction of any part changed?
bool update_signal_tunbridge_exit = false;
Direction old_direction = INVALID_DIR;
TrackBits old_trackbits = INVALID_TRACK_BIT;
auto notify_direction_changed = [&](Direction old_direction, Direction new_direction) {
if (prev == NULL && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
const AccelerationSlowdownParams *asp = &_accel_slowdown[GetRailTypeInfo(v->railtype)->acceleration_type];
DirDiff diff = DirDifference(old_direction, new_direction);
v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
}
direction_changed = true;
};
if (reverse && v->reverse_distance == 1) {
goto reverse_train_direction;
@@ -3791,11 +3830,24 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* For every vehicle after and including the given vehicle */
for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) {
old_direction = v->direction;
old_trackbits = v->track;
DiagDirection enterdir = DIAGDIR_BEGIN;
bool update_signals_crossing = false; // will we update signals or crossing state?
GetNewVehiclePosResult gp = GetNewVehiclePos(v);
if (v->track != TRACK_BIT_WORMHOLE) {
if (!(v->track & TRACK_BIT_WORMHOLE) && gp.old_tile != gp.new_tile &&
IsRailBridgeHeadTile(gp.old_tile) && DiagdirBetweenTiles(gp.old_tile, gp.new_tile) == GetTunnelBridgeDirection(gp.old_tile)) {
/* left a bridge headtile into a wormhole */
Direction old_direction = v->direction;
uint32 r = VehicleEnterTile(v, gp.old_tile, gp.x, gp.y); // NB: old tile, the bridge head which the train just left
if (HasBit(r, VETS_CANNOT_ENTER)) {
goto invalid_rail;
}
if (old_direction != v->direction) notify_direction_changed(old_direction, v->direction);
}
if (!(v->track & TRACK_BIT_WORMHOLE)) {
/* Not inside tunnel */
if (gp.old_tile == gp.new_tile) {
/* Staying in the old tile */
@@ -3818,6 +3870,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* The new position is the end of the platform */
TrainEnterStation(v->First(), r >> VETS_STATION_ID_OFFSET);
}
if (old_direction != v->direction) notify_direction_changed(old_direction, v->direction);
}
} else {
/* A new tile is about to be entered. */
@@ -3826,9 +3879,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
assert(IsValidDiagDirection(enterdir));
enter_new_tile:
/* Get the status of the tracks in the new tile and mask
* away the bits that aren't reachable. */
TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, ReverseDiagDir(enterdir));
TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, (v->track & TRACK_BIT_WORMHOLE) ? INVALID_DIAGDIR : ReverseDiagDir(enterdir));
TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
@@ -3838,7 +3893,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (_settings_game.pf.forbid_90_deg && prev == NULL) {
/* We allow wagons to make 90 deg turns, because forbid_90_deg
* can be switched on halfway a turn */
bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
if (!(v->track & TRACK_BIT_WORMHOLE)) {
bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
} else if (v->track & TRACK_BIT_MASK) {
bits &= ~TrackCrossesTracks(FindFirstTrack(v->track & TRACK_BIT_MASK));
}
}
if (bits == TRACK_BIT_NONE) goto invalid_rail;
@@ -3926,9 +3985,9 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
} else {
/* The wagon is active, simply follow the prev vehicle. */
if (prev->tile == gp.new_tile) {
if (TileVirtXY(prev->x_pos, prev->y_pos) == gp.new_tile) {
/* Choose the same track as prev */
if (prev->track == TRACK_BIT_WORMHOLE) {
if (prev->track & TRACK_BIT_WORMHOLE) {
/* Vehicles entering tunnels enter the wormhole earlier than for bridges.
* However, just choose the track into the wormhole. */
assert(IsTunnel(prev->tile));
@@ -3950,7 +4009,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
{TRACK_BIT_NONE, TRACK_BIT_RIGHT, TRACK_BIT_X, TRACK_BIT_UPPER},
{TRACK_BIT_RIGHT, TRACK_BIT_NONE, TRACK_BIT_LOWER, TRACK_BIT_Y }
};
DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, TileVirtXY(prev->x_pos, prev->y_pos));
assert(IsValidDiagDirection(exitdir));
chosen_track = _connecting_track[enterdir][exitdir];
}
@@ -3970,12 +4029,12 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
Direction chosen_dir = (Direction)b[2];
/* Call the landscape function and tell it that the vehicle entered the tile */
uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
uint32 r = (v->track & TRACK_BIT_WORMHOLE) ? 0 : VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
if (HasBit(r, VETS_CANNOT_ENTER)) {
goto invalid_rail;
}
if (IsTunnelBridgeWithSignalSimulation(gp.new_tile)) {
if (!(v->track & TRACK_BIT_WORMHOLE) && IsTunnelBridgeWithSignalSimulation(gp.new_tile) && (GetAcrossTunnelBridgeTrackBits(gp.new_tile) & chosen_track)) {
/* If red signal stop. */
if (v->IsFrontEngine() && v->force_proceed == 0) {
if (IsTunnelBridgeSignalSimulationEntrance(gp.new_tile) && GetTunnelBridgeEntranceSignalState(gp.new_tile) == SIGNAL_STATE_RED) {
@@ -4008,7 +4067,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
/* Clear any track reservation when the last vehicle leaves the tile */
if (v->Next() == NULL) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir(), true);
if (v->Next() == NULL && !(v->track & TRACK_BIT_WORMHOLE)) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir(), true);
v->tile = gp.new_tile;
@@ -4025,12 +4084,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
update_signals_crossing = true;
if (chosen_dir != v->direction) {
if (prev == NULL && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
const AccelerationSlowdownParams *asp = &_accel_slowdown[GetRailTypeInfo(v->railtype)->acceleration_type];
DirDiff diff = DirDifference(v->direction, chosen_dir);
v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
}
direction_changed = true;
notify_direction_changed(v->direction, chosen_dir);
v->direction = chosen_dir;
}
@@ -4106,19 +4160,16 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
v->force_proceed = 0;
v->wait_counter = 0;
v->tunnel_bridge_signal_num = 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;
update_signal_tunbridge_exit = true;
}
}
if (old_tile == gp.new_tile && IsTunnelBridgeWithSignalSimulation(v->tile) && v->IsFrontEngine()) {
TileIndex next_tile = old_tile + TileOffsByDir(v->direction);
Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(v->tile));
DiagDirection axial_dir = DirToDiagDirAlongAxis(v->direction, axis);
TileIndex next_tile = old_tile + TileOffsByDiagDir(axial_dir);
bool is_exit = false;
if (IsTileType(next_tile, MP_TUNNELBRIDGE) && IsTunnelBridgeWithSignalSimulation(next_tile) &&
ReverseDiagDir(GetTunnelBridgeDirection(next_tile)) == DirToDiagDir(v->direction)) {
ReverseDiagDir(GetTunnelBridgeDirection(next_tile)) == axial_dir) {
if (IsBridge(next_tile) && IsBridge(v->tile)) {
// bridge ramp facing towards us
is_exit = true;
@@ -4140,6 +4191,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
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 (IsRailCustomBridgeHeadTile(gp.new_tile)) {
enterdir = ReverseDiagDir(GetTunnelBridgeDirection(gp.new_tile));
goto enter_new_tile;
}
if (v->IsFrontEngine()) {
TryReserveRailTrack(gp.new_tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(gp.new_tile)));
CheckNextTrainTile(v);
@@ -4163,6 +4218,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
}
if (v->IsDrawn()) v->Vehicle::UpdateViewport(true);
if (update_signal_tunbridge_exit) {
UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
update_signal_tunbridge_exit = false;
}
continue;
}
}
@@ -4185,6 +4244,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
AffectSpeedByZChange(v, old_z);
}
if (update_signal_tunbridge_exit) {
UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
update_signal_tunbridge_exit = false;
}
if (update_signals_crossing) {
if (v->IsFrontEngine()) {
switch (TrainMovedChangeSignal(v, gp.new_tile, enterdir)) {
@@ -4264,6 +4328,11 @@ invalid_rail:
if (prev != NULL) error("Disconnecting train");
reverse_train_direction:
if (old_trackbits != INVALID_TRACK_BIT && (v->track ^ old_trackbits) & TRACK_BIT_WORMHOLE) {
/* Entering/exiting wormhole failed/aborted, back out changes to vehicle direction and track */
v->track = old_trackbits;
v->direction = old_direction;
}
if (reverse) {
v->wait_counter = 0;
v->cur_speed = 0;
@@ -4274,6 +4343,21 @@ reverse_train_direction:
return false;
}
static TrackBits GetTrackbitsFromCrashedVehicle(Train *t)
{
TrackBits train_tbits = t->track;
if (train_tbits & TRACK_BIT_WORMHOLE) {
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
train_tbits = GetAcrossTunnelBridgeReservationTrackBits(t->tile);
if (train_tbits != TRACK_BIT_NONE) return train_tbits;
/* Pick the first available tunnel/bridge head track which could be reserved */
train_tbits = GetAcrossTunnelBridgeTrackBits(t->tile);
return train_tbits ^ KillFirstBit(train_tbits);
} else {
return train_tbits;
}
}
/**
* Collect trackbits of all crashed train vehicles on a tile
* @param v Vehicle passed from Find/HasVehicleOnPos()
@@ -4285,18 +4369,34 @@ static Vehicle *CollectTrackbitsFromCrashedVehiclesEnum(Vehicle *v, void *data)
TrackBits *trackbits = (TrackBits *)data;
if (v->type == VEH_TRAIN && (v->vehstatus & VS_CRASHED) != 0) {
TrackBits train_tbits = Train::From(v)->track;
if (train_tbits == TRACK_BIT_WORMHOLE) {
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
*trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(v->tile));
} else if (train_tbits != TRACK_BIT_DEPOT) {
*trackbits |= train_tbits;
if (Train::From(v)->track != TRACK_BIT_DEPOT) {
*trackbits |= GetTrackbitsFromCrashedVehicle(Train::From(v));
}
}
return NULL;
}
static void SetSignalledBridgeTunnelGreenIfClear(TileIndex tile, TileIndex end)
{
if (TunnelBridgeIsFree(tile, end, nullptr, true).Succeeded()) {
auto process_tile = [](TileIndex t) {
if (IsTunnelBridgeSignalSimulationEntrance(t)) {
if (IsBridge(t)) {
SetAllBridgeEntranceSimulatedSignalsGreen(t);
MarkBridgeDirty(t, ZOOM_LVL_DRAW_MAP);
}
if (IsTunnelBridgeSignalSimulationEntrance(t) && GetTunnelBridgeEntranceSignalState(t) == SIGNAL_STATE_RED) {
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP);
}
}
};
process_tile(tile);
process_tile(end);
}
}
/**
* Deletes/Clears the last wagon of a crashed train. It takes the engine of the
* train, then goes to the last wagon and deletes that. Each call to this function
@@ -4327,18 +4427,14 @@ static void DeleteLastWagon(Train *v)
}
/* 'v' shouldn't be accessed after it has been deleted */
TrackBits trackbits = v->track;
TileIndex tile = v->tile;
Owner owner = v->owner;
const TrackBits orig_trackbits = v->track;
TrackBits trackbits = GetTrackbitsFromCrashedVehicle(v);
const TileIndex tile = v->tile;
const Owner owner = v->owner;
delete v;
v = NULL; // make sure nobody will try to read 'v' anymore
if (trackbits == TRACK_BIT_WORMHOLE) {
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile));
}
Track track = TrackBitsToTrack(trackbits);
if (HasReservedTracks(tile, trackbits)) {
UnreserveRailTrack(tile, track);
@@ -4357,29 +4453,12 @@ static void DeleteLastWagon(Train *v)
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
/* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
if (IsTunnelBridgeWithSignalSimulation(tile)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UpdateSignalsOnSegment(end, INVALID_DIAGDIR, owner);
if (TunnelBridgeIsFree(tile, end, nullptr).Succeeded()) {
auto process_tile = [](TileIndex t) {
if (IsTunnelBridgeSignalSimulationEntrance(t)) {
if (IsBridge(t)) {
SetAllBridgeEntranceSimulatedSignalsGreen(t);
MarkBridgeDirty(t, ZOOM_LVL_DRAW_MAP);
}
if (IsTunnelBridgeSignalSimulationEntrance(t) && GetTunnelBridgeEntranceSignalState(t) == SIGNAL_STATE_RED) {
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP);
}
}
};
process_tile(tile);
process_tile(end);
}
}
} else if (IsRailDepotTile(tile)) {
if (IsTunnelBridgeWithSignalSimulation(tile)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UpdateSignalsOnSegment(end, INVALID_DIAGDIR, owner);
SetSignalledBridgeTunnelGreenIfClear(tile, end);
}
if ((orig_trackbits & TRACK_BIT_WORMHOLE) || IsRailDepotTile(tile)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
} else {
SetSignalsOnBothDir(tile, track, owner);
@@ -4403,7 +4482,7 @@ static void ChangeTrainDirRandomly(Train *v)
/* Refrain from updating the z position of the vehicle when on
* a bridge, because UpdateInclination() will put the vehicle under
* the bridge in that case */
if (v->track != TRACK_BIT_WORMHOLE) {
if (!(v->track & TRACK_BIT_WORMHOLE)) {
v->UpdatePosition();
v->UpdateInclination(false, true);
} else {
@@ -4518,7 +4597,7 @@ static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
static bool TrainCanLeaveTile(const Train *v)
{
/* Exit if inside a tunnel/bridge or a depot */
if (v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
if (v->track & TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
TileIndex tile = v->tile;
@@ -4526,6 +4605,13 @@ static bool TrainCanLeaveTile(const Train *v)
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (DiagDirToDir(dir) == v->direction) return false;
if (IsRailCustomBridgeHeadTile(tile) && VehicleExitDir(v->direction, v->track) == dir) {
if (_settings_game.pf.forbid_90_deg && v->Previous() == NULL && GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) == 0) {
/* Check for 90 degree turn on zero-length bridge span */
if (!(GetCustomBridgeHeadTrackBits(tile) & ~TrackCrossesTracks(FindFirstTrack(v->track)))) return true;
}
return false;
}
}
/* entering a depot? */
@@ -4926,7 +5012,13 @@ Trackdir Train::GetVehicleTrackdir() const
if (this->track == TRACK_BIT_WORMHOLE) {
/* train in tunnel or on bridge, so just use his direction and assume a diagonal track */
return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
TrackBits tracks = GetAcrossTunnelBridgeReservationTrackBits(this->tile);
if (!tracks) tracks = GetAcrossTunnelBridgeTrackBits(this->tile);
Trackdir td = TrackExitdirToTrackdir(FindFirstTrack(tracks), GetTunnelBridgeDirection(this->tile));
if (GetTunnelBridgeDirection(this->tile) != DirToDiagDir(this->direction)) td = ReverseTrackdir(td);
return td;
} else if (this->track & TRACK_BIT_WORMHOLE) {
return TrackDirectionToTrackdir(FindFirstTrack(this->track & TRACK_BIT_MASK), this->direction);
}
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
@@ -4957,23 +5049,28 @@ void DeleteVisibleTrain(Train *v)
/* 'u' shouldn't be accessed after it has been deleted */
TileIndex tile = u->tile;
TrackBits trackbits = u->track;
bool in_wormhole = trackbits & TRACK_BIT_WORMHOLE;
delete u;
if (trackbits == TRACK_BIT_WORMHOLE) {
if (in_wormhole) {
/* Vehicle is inside a wormhole, u->track contains no useful value then. */
trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile));
if (IsTunnelBridgeWithSignalSimulation(tile)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UpdateSignalsOnSegment(end, INVALID_DIAGDIR, GetTileOwner(tile));
SetSignalledBridgeTunnelGreenIfClear(tile, end);
}
} else {
Track track = TrackBitsToTrack(trackbits);
if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track);
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
}
Track track = TrackBitsToTrack(trackbits);
if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track);
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
/* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
if (in_wormhole || IsRailDepotTile(tile)) {
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
} else {
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
AddTrackToSignalBuffer(tile, TrackBitsToTrack(trackbits), GetTileOwner(tile));
}
} while (prev != NULL);