Initial support for rail custom bridge heads

This commit is contained in:
Jonathan G Rennison
2018-07-03 19:09:10 +01:00
parent 25af12814b
commit 77362b829a
25 changed files with 1055 additions and 235 deletions

View File

@@ -427,7 +427,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);
}
}
@@ -1568,17 +1568,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. */
@@ -1875,7 +1875,7 @@ void ReverseTrainDirection(Train *v)
/* TrainExitDir does not always produce the desired dir for depots and
* tunnels/bridges that is needed for UpdateSignalsOnSegment. */
DiagDirection dir = TrainExitDir(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
@@ -2216,17 +2216,15 @@ static bool CheckTrainStayInDepot(Train *v)
*/
static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
{
DiagDirection dir = TrackdirToExitdir(track_dir);
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
/* Are we just leaving a tunnel/bridge? */
if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
if (TrackdirExitsTunnelBridge(tile, track_dir)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
if (TunnelBridgeIsFree(tile, end, v, true).Succeeded()) {
/* Free the reservation only if no other train is on the tiles. */
SetTunnelBridgeReservation(tile, false);
SetTunnelBridgeReservation(end, false);
UnreserveAcrossRailTunnelBridge(tile);
UnreserveAcrossRailTunnelBridge(end);
if (_settings_client.gui.show_track_reservation) {
if (IsBridge(tile)) {
@@ -2237,8 +2235,11 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
}
}
}
} else if (TrackdirToExitdir(track_dir) != GetTunnelBridgeDirection(tile)) {
UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
}
} 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. */
@@ -2764,7 +2765,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;
}
@@ -2924,15 +2925,18 @@ static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
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 = GetAcrossBridgePossibleTrackBits(u->tile) & GetCustomBridgeHeadTrackBits(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));
}
}
}
@@ -2957,7 +2961,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);
UnreserveAcrossRailTunnelBridge(GetOtherTunnelBridgeEnd(v->tile));
}
}
@@ -3064,14 +3068,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 {
@@ -3116,14 +3120,37 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
Train *first = v->First();
Train *prev;
bool direction_changed = false; // has direction of any part changed?
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;
};
/* 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 */
@@ -3145,6 +3172,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* The new position is the end of the platform */
TrainEnterStation(v, 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. */
@@ -3153,9 +3181,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;
@@ -3165,7 +3195,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;
@@ -3240,9 +3274,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));
@@ -3264,7 +3298,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];
}
@@ -3284,7 +3318,7 @@ 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;
}
@@ -3315,12 +3349,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;
}
@@ -3343,6 +3372,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
} else {
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);
@@ -3416,6 +3449,11 @@ invalid_rail:
if (prev != NULL) error("Disconnecting train");
reverse_train_direction:
if ((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;
@@ -3426,6 +3464,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()
@@ -3437,12 +3490,8 @@ 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));
}
}
@@ -3479,18 +3528,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);
@@ -3509,7 +3554,7 @@ static void DeleteLastWagon(Train *v)
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
/* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
if ((orig_trackbits & TRACK_BIT_WORMHOLE) || IsRailDepotTile(tile)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
} else {
SetSignalsOnBothDir(tile, track, owner);
@@ -3533,7 +3578,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 {
@@ -3648,7 +3693,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;
@@ -3656,6 +3701,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) && TrainExitDir(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? */
@@ -4042,6 +4094,8 @@ 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));
} else if (this->track & TRACK_BIT_WORMHOLE) {
return TrackDirectionToTrackdir(FindFirstTrack(this->track & TRACK_BIT_MASK), this->direction);
}
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);