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

@@ -59,6 +59,7 @@ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from
/** Z position of the bridge sprites relative to bridge height (downwards) */
static const int BRIDGE_Z_START = 3;
extern void DrawTrackBits(TileInfo *ti, TrackBits track);
extern void DrawRoadBits(TileInfo *ti);
extern const RoadBits _invalid_tileh_slopes_road[2][15];
@@ -102,6 +103,21 @@ void MarkBridgeOrTunnelDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zooml
}
}
/**
* Mark bridge or tunnel tiles dirty on tunnel/bridge head reservation change
* @param tile Bridge head or tunnel entrance.
*/
void MarkBridgeOrTunnelDirtyOnReservationChange(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below)
{
if (IsTunnelBridgeWithSignalSimulation(tile)) {
MarkTileDirtyByTile(tile, mark_dirty_if_zoomlevel_is_below);
} else if (IsBridge(tile)) {
MarkBridgeDirty(tile, mark_dirty_if_zoomlevel_is_below);
} else {
MarkTileDirtyByTile(tile, mark_dirty_if_zoomlevel_is_below);
}
}
/**
* Get number of signals on bridge or tunnel with signal simulation.
* @param begin The begin of the tunnel or bridge.
@@ -515,9 +531,9 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
switch (transport_type) {
case TRANSPORT_RAIL:
/* Add to company infrastructure count if required. */
if (is_new_owner && c != NULL) c->infrastructure.rail[railtype] += (bridge_len + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
MakeRailBridgeRamp(tile_start, owner, bridge_type, dir, railtype, is_upgrade);
MakeRailBridgeRamp(tile_end, owner, bridge_type, ReverseDiagDir(dir), railtype, is_upgrade);
if (is_new_owner && c != NULL) c->infrastructure.rail[railtype] += (bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile_start) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile_end);
break;
case TRANSPORT_ROAD: {
@@ -982,7 +998,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
Owner owner = GetTileOwner(tile);
Train *v = NULL;
if (HasTunnelBridgeReservation(tile)) {
if (HasTunnelReservation(tile)) {
v = GetTrainForReservation(tile, track);
if (v != NULL) FreeTrainTrackReservation(v);
}
@@ -1066,25 +1082,49 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM, flags);
}
CommandCost cost(EXPENSES_CONSTRUCTION);
const bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
TrackBits tile_tracks = TRACK_BIT_NONE;
TrackBits endtile_tracks = TRACK_BIT_NONE;
if (rail) {
tile_tracks = GetCustomBridgeHeadTrackBits(tile);
endtile_tracks = GetCustomBridgeHeadTrackBits(endtile);
cost.AddCost(RailClearCost(GetRailType(tile)) * (CountBits(tile_tracks) + CountBits(endtile_tracks) - 2));
}
Money base_cost = (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) ? _price[PR_CLEAR_BRIDGE] : _price[PR_CLEAR_AQUEDUCT];
uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
uint middle_len = GetTunnelBridgeLength(tile, endtile);
uint len = middle_len + 2; // Don't forget the end tiles.
cost.AddCost(len * base_cost);
if (flags & DC_EXEC) {
/* read this value before actual removal of bridge */
bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
Owner owner = GetTileOwner(tile);
int height = GetBridgeHeight(tile);
Train *v = NULL;
SmallVector<Train *, 2> vehicles_affected;
if (rail && HasTunnelBridgeReservation(tile)) {
v = GetTrainForReservation(tile, DiagDirToDiagTrack(direction));
if (v != NULL) FreeTrainTrackReservation(v);
if (rail) {
auto find_train_reservations = [&vehicles_affected](TileIndex tile) {
TrackBits reserved = GetBridgeReservationTrackBits(tile);
Track track;
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
Train *v = GetTrainForReservation(tile, track);
if (v != NULL) {
FreeTrainTrackReservation(v);
*vehicles_affected.Append() = v;
}
}
};
find_train_reservations(tile);
find_train_reservations(endtile);
}
/* Update company infrastructure counts. */
if (rail) {
if (Company::IsValidID(owner)) {
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= (middle_len * TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(endtile);
if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals.
Company::Get(GetTileOwner(tile))->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile);
}
@@ -1113,18 +1153,28 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
if (rail) {
/* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
AddSideToSignalBuffer(tile, ReverseDiagDir(direction), owner);
AddSideToSignalBuffer(endtile, direction, owner);
Track track = DiagDirToDiagTrack(direction);
YapfNotifyTrackLayoutChange(tile, track);
YapfNotifyTrackLayoutChange(endtile, track);
auto notify_track_change = [owner](TileIndex tile, DiagDirection direction, TrackBits tracks) {
auto check_dir = [&](DiagDirection d) {
if (DiagdirReachesTracks(d) & tracks) AddSideToSignalBuffer(tile, d, owner);
};
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT));
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_REVERSE));
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_90LEFT));
while (tracks != TRACK_BIT_NONE) {
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
}
};
notify_track_change(tile, direction, tile_tracks);
notify_track_change(endtile, ReverseDiagDir(direction), endtile_tracks);
if (v != NULL) TryPathReserve(v, true);
for (uint i = 0; i < vehicles_affected.Length(); ++i) {
TryPathReserve(vehicles_affected[i], true);
}
}
}
return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost);
return cost;
}
/**
@@ -1489,7 +1539,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
}
/* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelReservation(ti->tile)) {
if (rti->UsesOverlay()) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
DrawGroundSprite(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH);
@@ -1531,6 +1581,52 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
DrawBridgeMiddle(ti);
return;
}
if (transport_type == TRANSPORT_RAIL && IsRailCustomBridgeHead(ti->tile)) {
DrawTrackBits(ti, GetCustomBridgeHeadTrackBits(ti->tile));
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) {
DrawRailCatenary(ti);
}
if (IsTunnelBridgeWithSignalSimulation(ti->tile)) {
extern void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition,
SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted);
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
Track t = FindFirstTrack(GetAcrossTunnelBridgeTrackBits(ti->tile));
auto draw_signals = [&](uint position, SignalOffsets image, DiagDirection towards) {
if (dir == towards) {
/* flip signal directions */
position ^= 1;
image = (SignalOffsets)(image ^ 1);
}
if (IsTunnelBridgeSignalSimulationEntrance(ti->tile)) {
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, false);
}
if (IsTunnelBridgeSignalSimulationExit(ti->tile)) {
SignalType type = SIGTYPE_NORMAL;
if (IsTunnelBridgePBS(ti->tile)) {
type = IsTunnelBridgeSignalSimulationEntrance(ti->tile) ? SIGTYPE_PBS : SIGTYPE_PBS_ONEWAY;
}
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeExitSignalState(ti->tile), (SignalOffsets)(image ^ 1), position ^ 1, type, variant, false);
}
};
switch (t) {
default: NOT_REACHED();
case TRACK_X: draw_signals( 8, SIGNAL_TO_SOUTHWEST, DIAGDIR_SW); break;
case TRACK_Y: draw_signals(10, SIGNAL_TO_SOUTHEAST, DIAGDIR_NW); break;
case TRACK_UPPER: draw_signals( 4, SIGNAL_TO_WEST, DIAGDIR_NW); break;
case TRACK_LOWER: draw_signals( 6, SIGNAL_TO_WEST, DIAGDIR_SW); break;
case TRACK_LEFT: draw_signals( 0, SIGNAL_TO_NORTH, DIAGDIR_NW); break;
case TRACK_RIGHT: draw_signals( 2, SIGNAL_TO_NORTH, DIAGDIR_NE); break;
}
}
DrawBridgeMiddle(ti);
return;
}
const PalSpriteID *psid;
int base_offset;
@@ -1614,7 +1710,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
}
/* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && GetBridgeReservationTrackBits(ti->tile) != TRACK_BIT_NONE) {
if (rti->UsesOverlay()) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
@@ -1782,8 +1878,8 @@ void DrawBridgeMiddle(const TileInfo *ti)
}
}
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES) && HasTunnelBridgeReservation(rampnorth)
&& !IsTunnelBridgeWithSignalSimulation(rampnorth)) {
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES)
&& !IsTunnelBridgeWithSignalSimulation(rampnorth) && (HasAcrossBridgeReservation(rampnorth) || HasAcrossBridgeReservation(rampsouth))) {
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));
@@ -1852,7 +1948,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
/* In the tunnel entrance? */
if (5 <= pos && pos <= 10) return z;
} else { // IsBridge(tile)
if (IsRoadCustomBridgeHeadTile(tile)) {
if (IsCustomBridgeHeadTile(tile)) {
return z + TILE_HEIGHT + (IsSteepSlope(tileh) ? TILE_HEIGHT : 0);
}
@@ -1883,7 +1979,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh)
{
if (IsRoadCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED;
if (IsCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED;
return IsTunnel(tile) ? FOUNDATION_NONE : GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(tile)));
}
@@ -1997,15 +2093,14 @@ static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (side != INVALID_DIAGDIR && side == dir) return 0;
if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) {
if (side != INVALID_DIAGDIR && side == dir) return 0;
TrackBits bits = TRACK_BIT_NONE;
if (sub_mode & ROADTYPES_TRAM) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)];
if (sub_mode & ROADTYPES_ROAD) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_ROAD)];
return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE);
}
if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0;
return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE);
return CombineTrackStatus(TrackBitsToTrackdirBits(mode == TRANSPORT_RAIL ? GetTunnelBridgeTrackBits(tile) : DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE);
}
static void UpdateRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end, bool add) {
@@ -2063,9 +2158,6 @@ void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
{
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
/* Set number of pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
const TransportType tt = GetTunnelBridgeTransportType(tile);
if (tt == TRANSPORT_ROAD && tile < other_end) {
@@ -2092,9 +2184,15 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner
Company *old = Company::Get(old_owner);
if (tt == TRANSPORT_RAIL) {
/* Set number of middle pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + (tile < other_end ? GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0);
old->infrastructure.rail[GetRailType(tile)] -= num_pieces;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces;
} else if (tt == TRANSPORT_WATER) {
/* Set number of pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
old->infrastructure.water -= num_pieces;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces;
}
@@ -2141,23 +2239,31 @@ extern const byte _tunnel_turnaround_pre_visibility_frame[DIAGDIR_END] = {31, 27
static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
{
/* Direction into the wormhole */
const DiagDirection dir = GetTunnelBridgeDirection(tile);
/* New position of the vehicle on the tile */
int pos = (DiagDirToAxis(dir) == AXIS_X ? x - (TileX(tile) * TILE_SIZE) : y - (TileY(tile) * TILE_SIZE));
/* Number of units moved by the vehicle since entering the tile */
int frame = (dir == DIAGDIR_NE || dir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
if (frame > (int) TILE_SIZE || frame < 0) return VETSB_CANNOT_ENTER;
if (frame == TILE_SIZE) {
TileIndexDiffC offset = TileIndexDiffCByDiagDir(ReverseDiagDir(dir));
x += offset.x;
y += offset.y;
}
int z = GetSlopePixelZ(x, y) - v->z_pos;
if (abs(z) > 2) return VETSB_CANNOT_ENTER;
/* Direction into the wormhole */
const DiagDirection dir = GetTunnelBridgeDirection(tile);
/* Direction of the vehicle */
const DiagDirection vdir = DirToDiagDir(v->direction);
/* New position of the vehicle on the tile */
byte pos = (DiagDirToAxis(vdir) == AXIS_X ? x : y) & TILE_UNIT_MASK;
/* Number of units moved by the vehicle since entering the tile */
byte frame = (vdir == DIAGDIR_NE || vdir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
if (IsTunnel(tile)) {
/* Direction of the vehicle */
const DiagDirection vdir = DirToDiagDir(v->direction);
if (v->type == VEH_TRAIN) {
Train *t = Train::From(v);
if (t->track != TRACK_BIT_WORMHOLE && dir == vdir) {
if (!(t->track & TRACK_BIT_WORMHOLE) && dir == vdir) {
if (t->IsFrontEngine() && frame == TUNNEL_SOUND_FRAME) {
if (!PlayVehicleSound(t, VSE_TUNNEL) && RailVehInfo(t->engine_type)->engclass == 0) {
SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
@@ -2173,7 +2279,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
}
}
if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
if (dir == ReverseDiagDir(vdir) && frame == (int) (_tunnel_visibility_frame[dir] - 1) && z == 0) {
/* We're at the tunnel exit ?? */
if (t->tile != tile && GetOtherTunnelEnd(t->tile) != tile) return VETSB_CONTINUE; // In chunnel
t->tile = tile;
@@ -2203,7 +2309,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
}
/* We're at the tunnel exit ?? */
if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
if (dir == ReverseDiagDir(vdir) && frame == (int) (_tunnel_visibility_frame[dir] - 1) && z == 0) {
if (rv->tile != tile && GetOtherTunnelEnd(rv->tile) != tile) return VETSB_CONTINUE; // In chunnel
rv->tile = tile;
rv->cur_image_valid_dir = INVALID_DIR;
@@ -2224,11 +2330,12 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
first->cur_speed = min(first->cur_speed, spd);
}
if (vdir == dir) {
/* Vehicle enters bridge at the last frame inside this tile. */
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
const Direction bridge_dir = DiagDirToDir(dir);
if (v->direction == bridge_dir) {
switch (v->type) {
case VEH_TRAIN: {
/* Trains enter bridge at the first frame beyond this tile. */
if (frame != TILE_SIZE) return VETSB_CONTINUE;
Train *t = Train::From(v);
t->track = TRACK_BIT_WORMHOLE;
ClrBit(t->gv_flags, GVF_GOINGUP_BIT);
@@ -2237,6 +2344,8 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
}
case VEH_ROAD: {
/* Non-train vehicles enter bridge at the last frame inside this tile. */
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
RoadVehicle *rv = RoadVehicle::From(v);
if (IsRoadCustomBridgeHeadTile(tile)) {
RoadBits bits = ROAD_NONE;
@@ -2253,29 +2362,36 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
}
case VEH_SHIP:
/* Non-train vehicles enter bridge at the last frame inside this tile. */
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
Ship::From(v)->state = TRACK_BIT_WORMHOLE;
break;
default: NOT_REACHED();
}
return VETSB_ENTERED_WORMHOLE;
} else if (vdir == ReverseDiagDir(dir)) {
v->tile = tile;
} else if (v->direction == ReverseDir(bridge_dir)) {
switch (v->type) {
case VEH_TRAIN: {
Train *t = Train::From(v);
if (t->track == TRACK_BIT_WORMHOLE) {
t->track = DiagDirToDiagTrackBits(vdir);
if (t->track & TRACK_BIT_WORMHOLE) {
if (IsRailCustomBridgeHeadTile(tile)) {
return VETSB_ENTERED_WORMHOLE;
} else {
v->tile = tile;
t->track = DiagDirToDiagTrackBits(DirToDiagDir(v->direction));
}
return VETSB_ENTERED_WORMHOLE;
}
break;
}
case VEH_ROAD: {
v->tile = tile;
RoadVehicle *rv = RoadVehicle::From(v);
if (rv->state == RVSB_WORMHOLE) {
rv->cur_image_valid_dir = INVALID_DIR;
rv->state = DiagDirToDiagTrackdir(vdir);
rv->state = DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
rv->frame = 0;
return VETSB_ENTERED_WORMHOLE;
}
@@ -2283,9 +2399,10 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
}
case VEH_SHIP: {
v->tile = tile;
Ship *ship = Ship::From(v);
if (ship->state == TRACK_BIT_WORMHOLE) {
ship->state = DiagDirToDiagTrackBits(vdir);
ship->state = DiagDirToDiagTrackBits(DirToDiagDir(v->direction));
return VETSB_ENTERED_WORMHOLE;
}
break;
@@ -2293,6 +2410,29 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
default: NOT_REACHED();
}
} else if (v->type == VEH_TRAIN && IsRailCustomBridgeHeadTile(tile)) {
DirDiff dir_diff = DirDifference(v->direction, bridge_dir);
DirDiff reverse_dir_diff = DirDifference(v->direction, ReverseDir(bridge_dir));
if (dir_diff == DIRDIFF_45RIGHT || dir_diff == DIRDIFF_45LEFT) {
if (frame != TILE_SIZE) return VETSB_CONTINUE;
Train *t = Train::From(v);
TileIndex other = GetOtherTunnelBridgeEnd(tile);
if (GetTunnelBridgeLength(tile, other) == 0 && IsRailCustomBridgeHead(other)) {
t->track |= TRACK_BIT_WORMHOLE;
} else {
t->direction = bridge_dir;
t->track = TRACK_BIT_WORMHOLE;
}
ClrBit(t->gv_flags, GVF_GOINGUP_BIT);
ClrBit(t->gv_flags, GVF_GOINGDOWN_BIT);
return VETSB_ENTERED_WORMHOLE;
}
if (reverse_dir_diff == DIRDIFF_45RIGHT || reverse_dir_diff == DIRDIFF_45LEFT) {
Train *t = Train::From(v);
if (t->track & TRACK_BIT_WORMHOLE) return VETSB_ENTERED_WORMHOLE;
}
}
}
return VETSB_CONTINUE;
@@ -2318,6 +2458,16 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
}
if (IsRailCustomBridgeHeadTile(tile)) {
extern bool IsValidFlatRailBridgeHeadTrackBits(Slope normalised_slope, DiagDirection bridge_direction, TrackBits tracks);
/* Steep slopes behave the same as slopes with one corner raised. */
const Slope normalised_tileh_new = IsSteepSlope(tileh_new) ? SlopeWithOneCornerRaised(GetHighestSlopeCorner(tileh_new)) : tileh_new;
if (!IsValidFlatRailBridgeHeadTrackBits(normalised_tileh_new, direction, GetCustomBridgeHeadTrackBits(tile))) {
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
}
/* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) {