diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5475cfb535..ad3f2bb95a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,8 +44,8 @@ set(TITLE_SEQUENCE_SHA1 "304d13a126c15bf2c86ff13b81a2f2cc1856ac8d")
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip")
set(OBJECTS_SHA1 "151424d24b1d49a167932b58319bedaa6ec368e9")
-set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.24/replays.zip")
-set(REPLAYS_SHA1 "58AD8B2D2A804D2FEE787597A2A4DEADBF4D2A65")
+set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.25/replays.zip")
+set(REPLAYS_SHA1 "DA3E595E4D0231934F1DCFC3B540097CE2ED1B17")
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
option(WITH_TESTS "Build tests")
diff --git a/distribution/changelog.txt b/distribution/changelog.txt
index a8a3794242..a7aa44c1fe 100644
--- a/distribution/changelog.txt
+++ b/distribution/changelog.txt
@@ -2,6 +2,7 @@
------------------------------------------------------------------------
- Feature: [#6677] Add Discord RPC to macOS builds.
- Feature: [#6844] Enhanced track designer with ability to add/remove scenery and footpaths.
+- Feature: [#7059] Landscape doors for the Ghost Train.
- Feature: [#11859] Add on-ride photo section to Air Powered Vertical and Reverse Freefall Coaster.
- Feature: [#12307] Allow extraction of GOG installer via innoextract (for Linux users).
- Feature: [#13057] Make GameAction flags accessible by plugins.
diff --git a/openrct2.proj b/openrct2.proj
index 39029076fb..83e25c0753 100644
--- a/openrct2.proj
+++ b/openrct2.proj
@@ -48,8 +48,8 @@
304d13a126c15bf2c86ff13b81a2f2cc1856ac8d
https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip
151424d24b1d49a167932b58319bedaa6ec368e9
- https://github.com/OpenRCT2/replays/releases/download/v0.0.24/replays.zip
- 58AD8B2D2A804D2FEE787597A2A4DEADBF4D2A65
+ https://github.com/OpenRCT2/replays/releases/download/v0.0.25/replays.zip
+ DA3E595E4D0231934F1DCFC3B540097CE2ED1B17
diff --git a/src/openrct2/actions/TrackPlaceAction.cpp b/src/openrct2/actions/TrackPlaceAction.cpp
index c4caec13b8..0bbc903265 100644
--- a/src/openrct2/actions/TrackPlaceAction.cpp
+++ b/src/openrct2/actions/TrackPlaceAction.cpp
@@ -629,6 +629,11 @@ GameActions::Result::Ptr TrackPlaceAction::Execute() const
{
tileElement->AsTrack()->SetBrakeBoosterSpeed(_brakeSpeed);
}
+ else if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
+ {
+ tileElement->AsTrack()->SetDoorAState(LANDSCAPE_DOOR_CLOSED);
+ tileElement->AsTrack()->SetDoorBState(LANDSCAPE_DOOR_CLOSED);
+ }
else
{
tileElement->AsTrack()->SetSeatRotation(_seatRotation);
diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp
index d836a166d6..dcc92f51eb 100644
--- a/src/openrct2/network/NetworkBase.cpp
+++ b/src/openrct2/network/NetworkBase.cpp
@@ -34,7 +34,7 @@
// This string specifies which version of network stream current build uses.
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
-#define NETWORK_STREAM_VERSION "10"
+#define NETWORK_STREAM_VERSION "11"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;
diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp
index 788328f5bf..c90b684e3b 100644
--- a/src/openrct2/rct1/S4Importer.cpp
+++ b/src/openrct2/rct1/S4Importer.cpp
@@ -2100,6 +2100,7 @@ private:
{
auto dst2 = dst->AsTrack();
auto src2 = src->AsTrack();
+ auto rideType = _s4.rides[src2->GetRideIndex()].type;
dst2->SetTrackType(src2->GetTrackType());
dst2->SetSequenceIndex(src2->GetSequenceIndex());
@@ -2108,12 +2109,18 @@ private:
dst2->SetHasChain(src2->HasChain());
dst2->SetHasCableLift(false);
dst2->SetInverted(src2->IsInverted());
- dst2->SetDoorAState(src2->GetDoorAState());
- dst2->SetDoorBState(src2->GetDoorBState());
dst2->SetStationIndex(src2->GetStationIndex());
dst2->SetHasGreenLight(src2->HasGreenLight());
dst2->SetIsIndestructible(src2->IsIndestructible());
- dst2->SetSeatRotation(DEFAULT_SEAT_ROTATION);
+ if (rideType == RCT1_RIDE_TYPE_GHOST_TRAIN)
+ {
+ dst2->SetDoorAState(src2->GetDoorAState());
+ dst2->SetDoorBState(src2->GetDoorBState());
+ }
+ else
+ {
+ dst2->SetSeatRotation(DEFAULT_SEAT_ROTATION);
+ }
// Skipping IsHighlighted()
auto trackType = dst2->GetTrackType();
@@ -2127,7 +2134,7 @@ private:
}
// This has to be done last, since the maze entry shares fields with the colour and sequence fields.
- if (_s4.rides[src2->GetRideIndex()].type == RIDE_TYPE_MAZE)
+ if (rideType == RIDE_TYPE_MAZE)
{
dst2->SetMazeEntry(src2->GetMazeEntry());
}
diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp
index 963da8f07c..d8128a4925 100644
--- a/src/openrct2/rct12/RCT12.cpp
+++ b/src/openrct2/rct12/RCT12.cpp
@@ -290,6 +290,18 @@ uint8_t RCT12TrackElement::GetDoorBState() const
return (colour & RCT12_TRACK_ELEMENT_DOOR_B_MASK) >> 5;
}
+void RCT12TrackElement::SetDoorAState(uint8_t newState)
+{
+ colour &= ~RCT12_TRACK_ELEMENT_DOOR_B_MASK;
+ colour |= ((newState << 2) & RCT12_TRACK_ELEMENT_DOOR_B_MASK);
+}
+
+void RCT12TrackElement::SetDoorBState(uint8_t newState)
+{
+ colour &= ~RCT12_TRACK_ELEMENT_DOOR_B_MASK;
+ colour |= ((newState << 5) & RCT12_TRACK_ELEMENT_DOOR_B_MASK);
+}
+
bool RCT12TrackElement::IsIndestructible() const
{
return (flags & RCT12_TILE_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE) != 0;
diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h
index d2542aabbc..6d09cc894d 100644
--- a/src/openrct2/rct12/RCT12.h
+++ b/src/openrct2/rct12/RCT12.h
@@ -511,10 +511,11 @@ public:
uint8_t GetSeatRotation() const;
uint16_t GetMazeEntry() const;
uint8_t GetPhotoTimeout() const;
- // Used in RCT1, will be reintroduced at some point.
- // (See https://github.com/OpenRCT2/OpenRCT2/issues/7059)
+ // RCT1 feature, reintroduced by OpenRCT2. See https://github.com/OpenRCT2/OpenRCT2/issues/7059
uint8_t GetDoorAState() const;
uint8_t GetDoorBState() const;
+ void SetDoorAState(uint8_t newState);
+ void SetDoorBState(uint8_t newState);
void SetTrackType(uint8_t newEntryIndex);
void SetSequenceIndex(uint8_t newSequenceIndex);
diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp
index 5e0dedbb77..89bf503232 100644
--- a/src/openrct2/rct2/S6Exporter.cpp
+++ b/src/openrct2/rct2/S6Exporter.cpp
@@ -1511,7 +1511,7 @@ void S6Exporter::ExportTileElement(RCT12TileElement* dst, TileElement* src)
dst2->SetPhotoTimeout(src2->GetPhotoTimeout());
dst2->SetBlockBrakeClosed(src2->BlockBrakeClosed());
dst2->SetIsIndestructible(src2->IsIndestructible());
- dst2->SetSeatRotation(src2->GetSeatRotation());
+
// Skipping IsHighlighted()
// This has to be done last, since the maze entry shares fields with the colour and sequence fields.
@@ -1522,6 +1522,20 @@ void S6Exporter::ExportTileElement(RCT12TileElement* dst, TileElement* src)
{
dst2->SetMazeEntry(src2->GetMazeEntry());
}
+ else if (ride->type == RIDE_TYPE_GHOST_TRAIN)
+ {
+ dst2->SetDoorAState(src2->GetDoorAState());
+ dst2->SetDoorBState(src2->GetDoorBState());
+ }
+ else
+ {
+ dst2->SetSeatRotation(src2->GetSeatRotation());
+ }
+ }
+ // _Should_ not happen, but if it does, pick the most likely option.
+ else
+ {
+ dst2->SetSeatRotation(src2->GetSeatRotation());
}
break;
diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp
index 7bc48be7d7..e706d1d942 100644
--- a/src/openrct2/rct2/S6Importer.cpp
+++ b/src/openrct2/rct2/S6Importer.cpp
@@ -1126,7 +1126,6 @@ public:
dst2->SetHasGreenLight(src2->HasGreenLight());
dst2->SetBlockBrakeClosed(src2->BlockBrakeClosed());
dst2->SetIsIndestructible(src2->IsIndestructible());
- dst2->SetSeatRotation(src2->GetSeatRotation());
// Skipping IsHighlighted()
auto trackType = dst2->GetTrackType();
@@ -1145,6 +1144,15 @@ public:
{
dst2->SetMazeEntry(src2->GetMazeEntry());
}
+ else if (rideType == RIDE_TYPE_GHOST_TRAIN)
+ {
+ dst2->SetDoorAState(src2->GetDoorAState());
+ dst2->SetDoorBState(src2->GetDoorBState());
+ }
+ else
+ {
+ dst2->SetSeatRotation(src2->GetSeatRotation());
+ }
break;
}
diff --git a/src/openrct2/ride/RideData.h b/src/openrct2/ride/RideData.h
index 7f83add5a5..bc855ccd36 100644
--- a/src/openrct2/ride/RideData.h
+++ b/src/openrct2/ride/RideData.h
@@ -258,6 +258,7 @@ enum ride_type_flags : uint64_t
RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY = (1ULL << 48),
RIDE_TYPE_FLAG_SUPPORTS_LEVEL_CROSSINGS = (1ULL << 49),
RIDE_TYPE_FLAG_IS_SUSPENDED = (1ULL << 50),
+ RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS = (1ULL << 51),
};
// Set on ride types that have a main colour, additional colour and support colour.
diff --git a/src/openrct2/ride/Track.cpp b/src/openrct2/ride/Track.cpp
index 41584bdc23..3b34b156ad 100644
--- a/src/openrct2/ride/Track.cpp
+++ b/src/openrct2/ride/Track.cpp
@@ -1211,6 +1211,10 @@ bool TrackTypeHasSpeedSetting(track_type_t trackType)
uint8_t TrackElement::GetSeatRotation() const
{
+ const auto* ride = get_ride(GetRideIndex());
+ if (ride != nullptr && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
+ return DEFAULT_SEAT_ROTATION;
+
return ColourScheme >> 4;
}
diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp
index 2b267a8c0c..33111ecbdc 100644
--- a/src/openrct2/ride/Vehicle.cpp
+++ b/src/openrct2/ride/Vehicle.cpp
@@ -7483,6 +7483,44 @@ void Vehicle::UpdateSceneryDoor() const
{ wallCoords, static_cast(direction) }, TrackLocation, next_vehicle_on_train == SPRITE_INDEX_NULL);
}
+template static void AnimateLandscapeDoor(TrackElement* trackElement, bool isLastVehicle)
+{
+ auto doorState = isBackwards ? trackElement->GetDoorAState() : trackElement->GetDoorBState();
+ if (!isLastVehicle && doorState == LANDSCAPE_DOOR_CLOSED)
+ {
+ if (isBackwards)
+ trackElement->SetDoorAState(LANDSCAPE_DOOR_OPEN);
+ else
+ trackElement->SetDoorBState(LANDSCAPE_DOOR_OPEN);
+ // TODO: play door open sound
+ }
+
+ if (isLastVehicle)
+ {
+ if (isBackwards)
+ trackElement->SetDoorAState(LANDSCAPE_DOOR_CLOSED);
+ else
+ trackElement->SetDoorBState(LANDSCAPE_DOOR_CLOSED);
+ // TODO: play door close sound
+ }
+}
+
+void Vehicle::UpdateLandscapeDoor() const
+{
+ const auto* currentRide = GetRide();
+ if (currentRide == nullptr || !currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
+ {
+ return;
+ }
+
+ auto coords = CoordsXYZ{ x, y, TrackLocation.z }.ToTileStart();
+ auto* tileElement = map_get_track_element_at_from_ride(coords, ride);
+ if (tileElement != nullptr && tileElement->GetType() == static_cast(TileElementType::Track))
+ {
+ AnimateLandscapeDoor(tileElement->AsTrack(), next_vehicle_on_train == SPRITE_INDEX_NULL);
+ }
+}
+
/**
*
* rct2: 0x006DB38B
@@ -7539,6 +7577,22 @@ void Vehicle::UpdateSceneryDoorBackwards() const
{ wallCoords, static_cast(direction) }, TrackLocation, next_vehicle_on_train == SPRITE_INDEX_NULL);
}
+void Vehicle::UpdateLandscapeDoorBackwards() const
+{
+ const auto* currentRide = GetRide();
+ if (currentRide == nullptr || !currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
+ {
+ return;
+ }
+
+ auto coords = CoordsXYZ{ TrackLocation, TrackLocation.z };
+ auto* tileElement = map_get_track_element_at_from_ride(coords, ride);
+ if (tileElement != nullptr && tileElement->GetType() == static_cast(TileElementType::Track))
+ {
+ AnimateLandscapeDoor(tileElement->AsTrack(), next_vehicle_on_train == SPRITE_INDEX_NULL);
+ }
+}
+
static void vehicle_update_play_water_splash_sound()
{
if (_vehicleVelocityF64E08 <= BLOCK_BRAKE_BASE_SPEED)
@@ -7970,6 +8024,7 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType, Ride* cur
// Change from original: this used to check if the vehicle allowed doors.
UpdateSceneryDoor();
+ UpdateLandscapeDoor();
bool isGoingBack = false;
switch (TrackSubposition)
@@ -8113,6 +8168,7 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType, Ride* cur
}
// Change from original: this used to check if the vehicle allowed doors.
UpdateSceneryDoorBackwards();
+ UpdateLandscapeDoorBackwards();
return true;
}
diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h
index 91364cd072..347b42e9c6 100644
--- a/src/openrct2/ride/Vehicle.h
+++ b/src/openrct2/ride/Vehicle.h
@@ -450,6 +450,8 @@ private:
void UpdateGoKartAttemptSwitchLanes();
void UpdateSceneryDoor() const;
void UpdateSceneryDoorBackwards() const;
+ void UpdateLandscapeDoor() const;
+ void UpdateLandscapeDoorBackwards() const;
};
struct train_ref
diff --git a/src/openrct2/ride/gentle/GhostTrain.cpp b/src/openrct2/ride/gentle/GhostTrain.cpp
index 654bb03ea5..6dea62282e 100644
--- a/src/openrct2/ride/gentle/GhostTrain.cpp
+++ b/src/openrct2/ride/gentle/GhostTrain.cpp
@@ -127,6 +127,42 @@ static constexpr const uint32_t ghost_train_track_pieces_brakes[4] = {
SPR_GHOST_TRAIN_TRACK_BRAKES_NW_SE,
};
+static constexpr const uint8_t doorOpeningOutwardsToImage[] = {
+ TUNNEL_DOORS_2, // Closed
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_3, // Half open
+ TUNNEL_DOORS_4, // Fully open
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_2, // Unused?
+};
+
+static constexpr const uint8_t doorOpeningInwardsToImage[] = {
+ TUNNEL_DOORS_2, // Closed
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_5, // Half open
+ TUNNEL_DOORS_6, // Fully open
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_2, // Unused?
+ TUNNEL_DOORS_2, // Unused?
+};
+
+static uint8_t get_tunnel_doors_image_straight_flat(const TrackElement* trackElement, uint8_t direction)
+{
+ switch (direction)
+ {
+ case 0:
+ return doorOpeningInwardsToImage[trackElement->GetDoorAState()];
+ case 1:
+ return doorOpeningOutwardsToImage[trackElement->GetDoorBState()];
+ case 2:
+ return doorOpeningOutwardsToImage[trackElement->GetDoorBState()];
+ case 3:
+ return doorOpeningInwardsToImage[trackElement->GetDoorAState()];
+ }
+ return TUNNEL_DOORS_2;
+}
+
/** rct2: 0x00770BEC */
static void paint_ghost_train_track_flat(
paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height,
@@ -136,7 +172,8 @@ static void paint_ghost_train_track_flat(
PaintAddImageAsParentRotated(session, direction, imageId, 0, 0, 32, 20, 3, height, 0, 6, height);
- paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_0);
+ auto tunnelImage = get_tunnel_doors_image_straight_flat(tileElement->AsTrack(), direction);
+ paint_util_push_tunnel_rotated(session, direction, height, tunnelImage);
if (track_paint_util_should_paint_supports(session->MapPosition))
{
@@ -190,6 +227,17 @@ static void paint_ghost_train_track_flat_to_25_deg_up(
paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height,
const TileElement* tileElement)
{
+ bool isBackwards = tileElement->AsTrack()->GetTrackType() == TrackElemType::Down25ToFlat;
+ uint8_t doorImage;
+ if (!isBackwards)
+ {
+ doorImage = doorOpeningInwardsToImage[tileElement->AsTrack()->GetDoorAState()];
+ }
+ else
+ {
+ doorImage = doorOpeningOutwardsToImage[tileElement->AsTrack()->GetDoorBState()];
+ }
+
uint32_t imageId = ghost_train_track_pieces_flat_to_25_deg_up[direction][0] | session->TrackColours[SCHEME_TRACK];
PaintAddImageAsParentRotated(session, direction, imageId, 0, 0, 32, 20, 3, height, 0, 6, height);
@@ -204,7 +252,7 @@ static void paint_ghost_train_track_flat_to_25_deg_up(
switch (direction)
{
case 0:
- paint_util_push_tunnel_left(session, height, TUNNEL_0);
+ paint_util_push_tunnel_left(session, height, doorImage);
break;
case 1:
paint_util_push_tunnel_right(session, height, TUNNEL_2);
@@ -213,7 +261,7 @@ static void paint_ghost_train_track_flat_to_25_deg_up(
paint_util_push_tunnel_left(session, height, TUNNEL_2);
break;
case 3:
- paint_util_push_tunnel_right(session, height, TUNNEL_0);
+ paint_util_push_tunnel_right(session, height, doorImage);
break;
}
@@ -255,10 +303,12 @@ static void paint_ghost_train_track_25_deg_up_to_flat(
paint_util_push_tunnel_left(session, height - 8, TUNNEL_0);
break;
case 1:
- paint_util_push_tunnel_right(session, height + 8, TUNNEL_12);
+ paint_util_push_tunnel_right(
+ session, height + 8, doorOpeningOutwardsToImage[tileElement->AsTrack()->GetDoorBState()]);
break;
case 2:
- paint_util_push_tunnel_left(session, height + 8, TUNNEL_12);
+ paint_util_push_tunnel_left(
+ session, height + 8, doorOpeningOutwardsToImage[tileElement->AsTrack()->GetDoorBState()]);
break;
case 3:
paint_util_push_tunnel_right(session, height - 8, TUNNEL_0);
@@ -288,10 +338,12 @@ static void paint_ghost_train_track_flat_to_25_deg_down(
paint_util_push_tunnel_left(session, height - 8, TUNNEL_0);
break;
case 1:
- paint_util_push_tunnel_right(session, height + 8, TUNNEL_0);
+ paint_util_push_tunnel_right(
+ session, height + 8, doorOpeningInwardsToImage[tileElement->AsTrack()->GetDoorAState()]);
break;
case 2:
- paint_util_push_tunnel_left(session, height + 8, TUNNEL_0);
+ paint_util_push_tunnel_left(
+ session, height + 8, doorOpeningInwardsToImage[tileElement->AsTrack()->GetDoorAState()]);
break;
case 3:
paint_util_push_tunnel_right(session, height - 8, TUNNEL_0);
@@ -355,7 +407,12 @@ static void paint_ghost_train_track_right_quarter_turn_3_tiles(
session, 3, height, direction, trackSequence, session->TrackColours[SCHEME_TRACK],
ghost_train_track_pieces_quarter_turn_3_tiles, nullptr, defaultRightQuarterTurn3TilesBoundLengths,
defaultRightQuarterTurn3TilesBoundOffsets);
- track_paint_util_right_quarter_turn_3_tiles_tunnel(session, height, direction, trackSequence, TUNNEL_0);
+ const auto* trackElement = tileElement->AsTrack();
+ bool isBackwards = trackElement->GetTrackType() == TrackElemType::LeftQuarterTurn3Tiles;
+ bool isDoorA = (!isBackwards && trackSequence == 0) || (isBackwards && trackSequence == 3);
+ auto tunnelType = isDoorA ? doorOpeningInwardsToImage[trackElement->GetDoorAState()]
+ : doorOpeningOutwardsToImage[trackElement->GetDoorBState()];
+ track_paint_util_right_quarter_turn_3_tiles_tunnel(session, height, direction, trackSequence, tunnelType);
switch (trackSequence)
{
@@ -398,9 +455,22 @@ static void paint_ghost_train_track_left_quarter_turn_1_tile(
paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height,
const TileElement* tileElement)
{
+ bool isBackwards = tileElement->AsTrack()->GetTrackType() == TrackElemType::RightQuarterTurn1Tile;
+ uint8_t tunnelStartImage, tunnelEndImage;
+ if (!isBackwards)
+ {
+ tunnelStartImage = doorOpeningInwardsToImage[tileElement->AsTrack()->GetDoorAState()];
+ tunnelEndImage = doorOpeningOutwardsToImage[tileElement->AsTrack()->GetDoorBState()];
+ }
+ else
+ {
+ tunnelStartImage = doorOpeningOutwardsToImage[tileElement->AsTrack()->GetDoorBState()];
+ tunnelEndImage = doorOpeningInwardsToImage[tileElement->AsTrack()->GetDoorAState()];
+ }
+
track_paint_util_left_quarter_turn_1_tile_paint(
session, 3, height, 0, direction, session->TrackColours[SCHEME_TRACK], ghost_train_track_pieces_quarter_turn_1_tile);
- track_paint_util_left_quarter_turn_1_tile_tunnel(session, direction, height, 0, TUNNEL_0, 0, TUNNEL_0);
+ track_paint_util_left_quarter_turn_1_tile_tunnel(session, direction, height, 0, tunnelStartImage, 0, tunnelEndImage);
metal_a_supports_paint_setup(session, METAL_SUPPORTS_BOXED, 4, 0, height, session->TrackColours[SCHEME_SUPPORTS]);
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
@@ -427,7 +497,8 @@ static void paint_ghost_train_track_spinning_tunnel(
track_paint_util_spinning_tunnel_paint(session, 3, height, direction);
- paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_0);
+ auto tunnelImage = get_tunnel_doors_image_straight_flat(tileElement->AsTrack(), direction);
+ paint_util_push_tunnel_rotated(session, direction, height, tunnelImage);
wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr);
@@ -444,7 +515,8 @@ static void paint_ghost_train_track_brakes(
PaintAddImageAsParentRotated(session, direction, imageId, 0, 0, 32, 20, 3, height, 0, 6, height);
- paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_0);
+ auto tunnelImage = get_tunnel_doors_image_straight_flat(tileElement->AsTrack(), direction);
+ paint_util_push_tunnel_rotated(session, direction, height, tunnelImage);
if (track_paint_util_should_paint_supports(session->MapPosition))
{
diff --git a/src/openrct2/ride/gentle/meta/GhostTrain.h b/src/openrct2/ride/gentle/meta/GhostTrain.h
index 7d1fd0df29..a2bbe1bc3f 100644
--- a/src/openrct2/ride/gentle/meta/GhostTrain.h
+++ b/src/openrct2/ride/gentle/meta/GhostTrain.h
@@ -29,7 +29,8 @@ constexpr const RideTypeDescriptor GhostTrainRTD =
RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS | RIDE_TYPE_FLAG_PEEP_WILL_RIDE_AGAIN | RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS |
RIDE_TYPE_FLAG_HAS_TRACK | RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR | RIDE_TYPE_FLAG_ALLOW_DOORS_ON_TRACK |
RIDE_TYPE_FLAG_ALLOW_MUSIC | RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT | RIDE_TYPE_FLAG_ALLOW_MORE_VEHICLES_THAN_STATION_FITS |
- RIDE_TYPE_FLAG_HAS_AIR_TIME | RIDE_TYPE_FLAG_SHOW_IN_TRACK_DESIGNER | RIDE_TYPE_FLAG_INTERESTING_TO_LOOK_AT),
+ RIDE_TYPE_FLAG_HAS_AIR_TIME | RIDE_TYPE_FLAG_SHOW_IN_TRACK_DESIGNER | RIDE_TYPE_FLAG_INTERESTING_TO_LOOK_AT |
+ RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS),
SET_FIELD(RideModes, EnumsToFlags(RideMode::ContinuousCircuit)),
SET_FIELD(DefaultMode, RideMode::ContinuousCircuit),
SET_FIELD(OperatingSettings, { 0, 0, 0, 0, 0, 0 }),
diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h
index 9225352ccf..3235728756 100644
--- a/src/openrct2/world/TileElement.h
+++ b/src/openrct2/world/TileElement.h
@@ -683,4 +683,11 @@ enum
#define TILE_ELEMENT_COLOUR_MASK 0b00011111
+enum
+{
+ LANDSCAPE_DOOR_CLOSED = 0,
+ LANDSCAPE_DOOR_HALF_OPEN = 2,
+ LANDSCAPE_DOOR_OPEN = 3,
+};
+
bool tile_element_is_underground(TileElement* tileElement);
diff --git a/test/testpaint/Compat.cpp b/test/testpaint/Compat.cpp
index 31e248e6c9..4a6c52e5b5 100644
--- a/test/testpaint/Compat.cpp
+++ b/test/testpaint/Compat.cpp
@@ -229,6 +229,10 @@ bool is_csg_loaded()
uint8_t TrackElement::GetSeatRotation() const
{
+ const auto* ride = get_ride(GetRideIndex());
+ if (ride != nullptr && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
+ return DEFAULT_SEAT_ROTATION;
+
return ColourScheme >> 4;
}