1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-16 19:43:06 +01:00

Merge pull request #21164 from Gymnasiast/refactor/td-entrance-elements

Import maze entrances in the same way as regular ones
This commit is contained in:
Michael Steenbeek
2024-01-12 08:10:00 +01:00
committed by GitHub
8 changed files with 282 additions and 339 deletions

View File

@@ -433,7 +433,40 @@ private:
_trackDesign.get(), RideGetTemporaryForPreview(), { loc, z, _currentTrackPieceDirection });
}
void DrawMiniPreviewTrack(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY min, CoordsXY max)
void DrawMiniPreviewEntrances(
const TrackDesign& td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max, Direction rotation)
{
for (const auto& entrance : td6.entrance_elements)
{
auto rotatedAndOffsetEntrance = origin + entrance.Location.ToCoordsXY().Rotate(rotation);
if (pass == 0)
{
min.x = std::min(min.x, rotatedAndOffsetEntrance.x);
max.x = std::max(max.x, rotatedAndOffsetEntrance.x);
min.y = std::min(min.y, rotatedAndOffsetEntrance.y);
max.y = std::max(max.y, rotatedAndOffsetEntrance.y);
}
else
{
auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetEntrance);
if (DrawMiniPreviewIsPixelInBounds(pixelPosition))
{
uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition);
uint8_t colour = entrance.IsExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance;
for (int32_t i = 0; i < 4; i++)
{
pixel[338 + i] = colour; // x + 2, y + 2
pixel[168 + i] = colour; // y + 1
pixel[2 + i] = colour; // x + 2
pixel[172 + i] = colour; // x + 4, y + 1
}
}
}
}
}
void DrawMiniPreviewTrack(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max)
{
const uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3;
@@ -503,38 +536,10 @@ private:
}
}
// Draw entrance and exit preview.
for (const auto& entrance : td6->entrance_elements)
{
auto rotatedAndOffsetEntrance = origin + entrance.Location.ToCoordsXY().Rotate(rotation);
if (pass == 0)
{
min.x = std::min(min.x, rotatedAndOffsetEntrance.x);
max.x = std::max(max.x, rotatedAndOffsetEntrance.x);
min.y = std::min(min.y, rotatedAndOffsetEntrance.y);
max.y = std::max(max.y, rotatedAndOffsetEntrance.y);
}
else
{
auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetEntrance);
if (DrawMiniPreviewIsPixelInBounds(pixelPosition))
{
uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition);
uint8_t colour = entrance.IsExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance;
for (int32_t i = 0; i < 4; i++)
{
pixel[338 + i] = colour; // x + 2, y + 2
pixel[168 + i] = colour; // y + 1
pixel[2 + i] = colour; // x + 2
pixel[172 + i] = colour; // x + 4, y + 1
}
}
}
}
DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation);
}
void DrawMiniPreviewMaze(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY min, CoordsXY max)
void DrawMiniPreviewMaze(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max)
{
uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3;
for (const auto& mazeElement : td6->maze_elements)
@@ -556,13 +561,6 @@ private:
uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition);
uint8_t colour = _PaletteIndexColourTrack;
// Draw entrance and exit with different colours.
if (mazeElement.type == MAZE_ELEMENT_TYPE_ENTRANCE)
colour = _PaletteIndexColourEntrance;
else if (mazeElement.type == MAZE_ELEMENT_TYPE_EXIT)
colour = _PaletteIndexColourExit;
for (int32_t i = 0; i < 4; i++)
{
pixel[338 + i] = colour; // x + 2, y + 2
@@ -573,6 +571,8 @@ private:
}
}
}
DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation);
}
ScreenCoordsXY DrawMiniPreviewGetPixelPosition(const CoordsXY& location)

View File

@@ -250,12 +250,7 @@ namespace RCT1
_stream.Read(&t4MazeElement, sizeof(TD46MazeElement));
if (t4MazeElement.All != 0)
{
TrackDesignMazeElement mazeElement{};
mazeElement.x = t4MazeElement.x;
mazeElement.y = t4MazeElement.y;
mazeElement.direction = t4MazeElement.Direction;
mazeElement.type = t4MazeElement.Type;
td->maze_elements.push_back(mazeElement);
ImportMazeElement(*td, t4MazeElement);
}
}
}

View File

@@ -925,6 +925,26 @@ uint8_t ConvertToTD46Flags(const TrackDesignTrackElement& source)
return trackFlags;
}
void ImportMazeElement(TrackDesign& td, const TD46MazeElement& td46MazeElement)
{
if (td46MazeElement.IsEntrance() || td46MazeElement.IsExit())
{
TrackDesignEntranceElement element{};
element.Location = TileCoordsXYZD(td46MazeElement.x, td46MazeElement.y, 0, td46MazeElement.Direction);
element.IsExit = td46MazeElement.IsExit();
td.entrance_elements.push_back(element);
}
else
{
TrackDesignMazeElement mazeElement{};
mazeElement.x = td46MazeElement.x;
mazeElement.y = td46MazeElement.y;
mazeElement.direction = td46MazeElement.Direction;
mazeElement.type = td46MazeElement.Type;
td.maze_elements.push_back(mazeElement);
}
}
namespace RCT12
{
size_t GetRCTStringBufferLen(const char* buffer, size_t maxBufferLen)

View File

@@ -74,6 +74,7 @@ constexpr uint8_t RCT12PeepThoughtItemNone = std::numeric_limits<uint8_t>::max()
constexpr uint8_t RCT12GuestsInParkHistoryFactor = 20;
constexpr uint8_t RCT12ParkHistoryUndefined = std::numeric_limits<uint8_t>::max();
struct TrackDesign;
struct TrackDesignTrackElement;
enum class RCT12TrackDesignVersion : uint8_t
@@ -169,6 +170,25 @@ enum
RCT12_ENTITY_FLAGS_IS_CRASHED_VEHICLE_ENTITY = 1 << 7,
};
// Only written to in RCT2, not used in OpenRCT2. All of these are elements that had to be invented in RCT1.
enum : uint32_t
{
TRACK_FLAGS_CONTAINS_VERTICAL_LOOP = (1 << 7),
TRACK_FLAGS_CONTAINS_INLINE_TWIST = (1 << 17),
TRACK_FLAGS_CONTAINS_HALF_LOOP = (1 << 18),
TRACK_FLAGS_CONTAINS_CORKSCREW = (1 << 19),
TRACK_FLAGS_CONTAINS_WATER_SPLASH = (1 << 27),
TRACK_FLAGS_CONTAINS_BARREL_ROLL = (1 << 29),
TRACK_FLAGS_CONTAINS_POWERED_LIFT = (1 << 30),
TRACK_FLAGS_CONTAINS_LARGE_HALF_LOOP = (1u << 31),
};
enum : uint32_t
{
TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER = (1 << 1),
TRACK_FLAGS2_SIX_FLAGS_RIDE_DEPRECATED = (1u << 31) // Not used anymore.
};
#pragma pack(push, 1)
struct RCT12xy8
@@ -194,6 +214,12 @@ struct RCT12xy8
};
assert_struct_size(RCT12xy8, 2);
enum class TD46MazeElementType : uint8_t
{
Entrance = (1 << 3),
Exit = (1 << 7)
};
/* Maze Element entry size: 0x04 */
struct TD46MazeElement
{
@@ -215,6 +241,16 @@ struct TD46MazeElement
};
};
};
constexpr bool IsEntrance() const
{
return Type == EnumValue(TD46MazeElementType::Entrance);
}
constexpr bool IsExit() const
{
return Type == EnumValue(TD46MazeElementType::Exit);
}
};
assert_struct_size(TD46MazeElement, 0x04);
@@ -914,6 +950,7 @@ enum class TD46Flags : uint8_t
void ConvertFromTD46Flags(TrackDesignTrackElement& target, uint8_t flags);
uint8_t ConvertToTD46Flags(const TrackDesignTrackElement& source);
void ImportMazeElement(TrackDesign& td, const TD46MazeElement& td46MazeElement);
namespace RCT12
{

View File

@@ -107,6 +107,15 @@ namespace RCT2
tempStream.WriteValue<uint32_t>(mazeElement.all);
}
for (const auto& entranceElement : _trackDesign->entrance_elements)
{
tempStream.WriteValue<int8_t>(entranceElement.Location.x);
tempStream.WriteValue<int8_t>(entranceElement.Location.y);
tempStream.WriteValue<int8_t>(entranceElement.Location.direction);
tempStream.WriteValue<int8_t>(
EnumValue(entranceElement.IsExit ? TD46MazeElementType::Exit : TD46MazeElementType::Entrance));
}
tempStream.WriteValue<uint32_t>(0);
}
else

View File

@@ -148,12 +148,7 @@ namespace RCT2
_stream.Read(&t6MazeElement, sizeof(TD46MazeElement));
if (t6MazeElement.All != 0)
{
TrackDesignMazeElement mazeElement{};
mazeElement.x = t6MazeElement.x;
mazeElement.y = t6MazeElement.y;
mazeElement.direction = t6MazeElement.Direction;
mazeElement.type = t6MazeElement.Type;
td->maze_elements.push_back(mazeElement);
ImportMazeElement(*td, t6MazeElement);
}
}
}

View File

@@ -412,13 +412,11 @@ ResultWithMessage TrackDesign::CreateTrackDesignMaze(TrackDesignState& tds, cons
} while (!(tileElement++)->IsLastForTile());
// Add something that stops this from walking off the end
uint8_t entranceDirection = tileElement->GetDirection();
TrackDesignMazeElement mazeEntrance{};
mazeEntrance.direction = entranceDirection;
mazeEntrance.type = 8;
mazeEntrance.x = static_cast<int8_t>((entranceLoc.x - startLoc.x) / 32);
mazeEntrance.y = static_cast<int8_t>((entranceLoc.y - startLoc.y) / 32);
maze_elements.push_back(mazeEntrance);
auto entranceOffset = entranceLoc - startLoc;
TrackDesignEntranceElement mazeEntrance{};
mazeEntrance.Location = TileCoordsXYZD(CoordsXYZD(entranceOffset, 0, tileElement->GetDirection()));
mazeEntrance.IsExit = false;
entrance_elements.push_back(mazeEntrance);
location = ride.GetStation().Exit;
if (location.IsNull())
@@ -441,13 +439,11 @@ ResultWithMessage TrackDesign::CreateTrackDesignMaze(TrackDesignState& tds, cons
} while (!(tileElement++)->IsLastForTile());
// Add something that stops this from walking off the end
uint8_t exit_direction = tileElement->GetDirection();
TrackDesignMazeElement mazeExit{};
mazeExit.direction = exit_direction;
mazeExit.type = 0x80;
mazeExit.x = static_cast<int8_t>((exitLoc.x - startLoc.x) / 32);
mazeExit.y = static_cast<int8_t>((exitLoc.y - startLoc.y) / 32);
maze_elements.push_back(mazeExit);
auto exitOffset = exitLoc - startLoc;
TrackDesignEntranceElement mazeExit{};
mazeExit.Location = TileCoordsXYZD(CoordsXYZD(exitOffset, 0, tileElement->GetDirection()));
mazeExit.IsExit = true;
entrance_elements.push_back(mazeExit);
// Save global vars as they are still used by scenery????
int32_t startZ = tds.Origin.z;
@@ -874,6 +870,18 @@ static void TrackDesignMirrorScenery(TrackDesign* td6)
}
}
static void TrackDesignMirrorEntrances(TrackDesign& td)
{
for (auto& entrance : td.entrance_elements)
{
entrance.Location.y = -entrance.Location.y;
if (entrance.Location.direction & 1)
{
entrance.Location.direction = DirectionReverse(entrance.Location.direction);
}
}
}
/**
*
* rct2: 0x006D2443
@@ -885,15 +893,6 @@ static void TrackDesignMirrorRide(TrackDesign* td6)
const auto& ted = GetTrackElementDescriptor(track.Type);
track.Type = ted.MirrorElement;
}
for (auto& entrance : td6->entrance_elements)
{
entrance.Location.y = -entrance.Location.y;
if (entrance.Location.direction & 1)
{
entrance.Location.direction = DirectionReverse(entrance.Location.direction);
}
}
}
/** rct2: 0x00993EDC */
@@ -911,15 +910,6 @@ static void TrackDesignMirrorMaze(TrackDesign* td6)
{
maze.y = -maze.y;
if (maze.type == 0x8 || maze.type == 0x80)
{
if (maze.direction & 1)
{
maze.direction = DirectionReverse(maze.direction);
}
continue;
}
uint16_t maze_entry = maze.maze_entry;
uint16_t new_entry = 0;
for (uint8_t position = UtilBitScanForward(maze_entry); position != 0xFF; position = UtilBitScanForward(maze_entry))
@@ -946,6 +936,7 @@ void TrackDesignMirror(TrackDesign* td6)
{
TrackDesignMirrorRide(td6);
}
TrackDesignMirrorEntrances(*td6);
TrackDesignMirrorScenery(td6);
}
@@ -1347,27 +1338,130 @@ static GameActions::Result TrackDesignPlaceAllScenery(
return res;
}
static std::optional<GameActions::Result> TrackDesignPlaceEntrances(
TrackDesignState& tds, const TrackDesign& td, CoordsXYZ newCoords, RideId rideId, money64& totalCost)
{
for (const auto& entrance : td.entrance_elements)
{
auto rotation = _currentTrackPieceDirection & 3;
CoordsXY entranceMapPos = entrance.Location.ToCoordsXY();
auto rotatedEntranceMapPos = entranceMapPos.Rotate(rotation);
newCoords = { rotatedEntranceMapPos + tds.Origin, newCoords.z };
TrackDesignUpdatePreviewBounds(tds, newCoords);
switch (tds.PlaceOperation)
{
case PTD_OPERATION_DRAW_OUTLINES:
TrackDesignAddSelectedTile(newCoords);
break;
case PTD_OPERATION_PLACE_QUERY:
case PTD_OPERATION_PLACE:
case PTD_OPERATION_PLACE_GHOST:
case PTD_OPERATION_PLACE_TRACK_PREVIEW:
{
rotation = (rotation + entrance.Location.direction) & 3;
newCoords.z = entrance.Location.z * COORDS_Z_STEP;
newCoords.z += tds.Origin.z;
if (tds.PlaceOperation != PTD_OPERATION_PLACE_QUERY)
{
auto tile = CoordsXY{ newCoords } + CoordsDirectionDelta[rotation];
TileElement* tile_element = MapGetFirstElementAt(tile);
if (tile_element == nullptr)
{
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
do
{
if (tile_element->GetType() != TileElementType::Track)
{
continue;
}
if (tile_element->GetBaseZ() != newCoords.z)
{
continue;
}
auto stationIndex = tile_element->AsTrack()->GetStationIndex();
uint8_t flags = GAME_COMMAND_FLAG_APPLY;
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
| GAME_COMMAND_FLAG_NO_SPEND;
}
if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
flags = 0;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
newCoords, rotation, rideId, stationIndex, entrance.IsExit);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideEntranceExitPlaceAction)
: GameActions::QueryNested(&rideEntranceExitPlaceAction);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
totalCost += res.Cost;
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
break;
} while (!(tile_element++)->IsLastForTile());
}
else
{
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery(newCoords, false);
if (res.Error != GameActions::Status::Ok)
{
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
totalCost += res.Cost;
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
}
break;
}
}
}
return std::nullopt;
}
static GameActions::Result TrackDesignPlaceMaze(
TrackDesignState& tds, TrackDesign* td6, const CoordsXYZ& coords, const Ride& ride)
TrackDesignState& tds, TrackDesign& td, const CoordsXYZ& origin, const Ride& ride)
{
if (tds.PlaceOperation == PTD_OPERATION_DRAW_OUTLINES)
{
gMapSelectionTiles.clear();
gMapSelectArrowPosition = CoordsXYZ{ coords, TileElementHeight(coords) };
gMapSelectArrowPosition = CoordsXYZ{ origin, TileElementHeight(origin) };
gMapSelectArrowDirection = _currentTrackPieceDirection;
}
tds.PlaceZ = 0;
money64 totalCost = 0;
for (const auto& maze_element : td6->maze_elements)
for (const auto& maze_element : td.maze_elements)
{
uint8_t rotation = _currentTrackPieceDirection & 3;
CoordsXY mazeMapPos = TileCoordsXY(maze_element.x, maze_element.y).ToCoordsXY();
auto mapCoord = mazeMapPos.Rotate(rotation);
mapCoord += coords;
mapCoord += origin;
TrackDesignUpdatePreviewBounds(tds, { mapCoord, coords.z });
TrackDesignUpdatePreviewBounds(tds, { mapCoord, origin.z });
if (tds.PlaceOperation == PTD_OPERATION_DRAW_OUTLINES)
{
@@ -1379,135 +1473,40 @@ static GameActions::Result TrackDesignPlaceMaze(
{
uint8_t flags;
money64 cost = 0;
uint16_t maze_entry;
switch (maze_element.type)
uint16_t maze_entry = Numerics::rol16(maze_element.maze_entry, rotation * 4);
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
case MAZE_ELEMENT_TYPE_ENTRANCE:
// entrance
rotation += maze_element.direction;
rotation &= 3;
flags = GAME_COMMAND_FLAG_APPLY;
if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord, coords.z }, false);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
}
else
{
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
| GAME_COMMAND_FLAG_NO_SPEND;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
mapCoord, rotation, ride.id, StationIndex::FromUnderlying(0), false);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = GameActions::ExecuteNested(&rideEntranceExitPlaceAction);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
}
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
break;
case MAZE_ELEMENT_TYPE_EXIT:
// exit
rotation += maze_element.direction;
rotation &= 3;
flags = GAME_COMMAND_FLAG_APPLY;
if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord, coords.z }, true);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
}
else
{
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
| GAME_COMMAND_FLAG_NO_SPEND;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
mapCoord, rotation, ride.id, StationIndex::FromUnderlying(0), true);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = GameActions::ExecuteNested(&rideEntranceExitPlaceAction);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
}
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
break;
default:
maze_entry = Numerics::rol16(maze_element.maze_entry, rotation * 4);
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
flags = 0;
}
else
{
flags = GAME_COMMAND_FLAG_APPLY;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto mazePlace = MazePlaceTrackAction({ mapCoord, coords.z }, ride.id, maze_entry);
mazePlace.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&mazePlace)
: GameActions::QueryNested(&mazePlace);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
break;
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
else if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
flags = 0;
}
else
{
flags = GAME_COMMAND_FLAG_APPLY;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto mazePlace = MazePlaceTrackAction({ mapCoord, origin.z }, ride.id, maze_entry);
mazePlace.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&mazePlace)
: GameActions::QueryNested(&mazePlace);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
cost = res.Cost;
totalCost += cost;
}
@@ -1538,7 +1537,7 @@ static GameActions::Result TrackDesignPlaceMaze(
surfaceZ = waterZ;
}
int16_t temp_z = coords.z + tds.PlaceZ - surfaceZ;
int16_t temp_z = origin.z + tds.PlaceZ - surfaceZ;
if (temp_z < 0)
{
tds.PlaceZ -= temp_z;
@@ -1546,6 +1545,14 @@ static GameActions::Result TrackDesignPlaceMaze(
}
}
tds.Origin = origin;
auto result = TrackDesignPlaceEntrances(tds, td, origin, ride.id, totalCost);
if (result.has_value())
{
return result.value();
}
if (tds.PlaceOperation == PTD_OPERATION_REMOVE_GHOST)
{
auto gameAction = RideDemolishAction(ride.id, RIDE_MODIFY_DEMOLISH);
@@ -1553,8 +1560,6 @@ static GameActions::Result TrackDesignPlaceMaze(
GameActions::Execute(&gameAction);
}
tds.Origin = coords;
auto res = GameActions::Result();
res.Cost = totalCost;
@@ -1721,102 +1726,10 @@ static GameActions::Result TrackDesignPlaceRide(TrackDesignState& tds, TrackDesi
}
}
// Entrance elements
for (const auto& entrance : td6->entrance_elements)
auto result = TrackDesignPlaceEntrances(tds, *td6, newCoords, ride.id, totalCost);
if (result.has_value())
{
rotation = _currentTrackPieceDirection & 3;
CoordsXY entranceMapPos = entrance.Location.ToCoordsXY();
auto rotatedEntranceMapPos = entranceMapPos.Rotate(rotation);
newCoords = { rotatedEntranceMapPos + tds.Origin, newCoords.z };
TrackDesignUpdatePreviewBounds(tds, newCoords);
switch (tds.PlaceOperation)
{
case PTD_OPERATION_DRAW_OUTLINES:
TrackDesignAddSelectedTile(newCoords);
break;
case PTD_OPERATION_PLACE_QUERY:
case PTD_OPERATION_PLACE:
case PTD_OPERATION_PLACE_GHOST:
case PTD_OPERATION_PLACE_TRACK_PREVIEW:
{
rotation = (rotation + entrance.Location.direction) & 3;
newCoords.z = entrance.Location.z * COORDS_Z_STEP;
newCoords.z += tds.Origin.z;
if (tds.PlaceOperation != PTD_OPERATION_PLACE_QUERY)
{
auto tile = CoordsXY{ newCoords } + CoordsDirectionDelta[rotation];
TileElement* tile_element = MapGetFirstElementAt(tile);
if (tile_element == nullptr)
{
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
do
{
if (tile_element->GetType() != TileElementType::Track)
{
continue;
}
if (tile_element->GetBaseZ() != newCoords.z)
{
continue;
}
auto stationIndex = tile_element->AsTrack()->GetStationIndex();
uint8_t flags = GAME_COMMAND_FLAG_APPLY;
if (tds.PlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
| GAME_COMMAND_FLAG_NO_SPEND;
}
if (tds.PlaceOperation == PTD_OPERATION_PLACE_GHOST)
{
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
| GAME_COMMAND_FLAG_GHOST;
}
if (tds.PlaceOperation == PTD_OPERATION_PLACE_QUERY)
{
flags = 0;
}
if (tds.IsReplay)
{
flags |= GAME_COMMAND_FLAG_REPLAY;
}
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
newCoords, rotation, ride.id, stationIndex, entrance.IsExit);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideEntranceExitPlaceAction)
: GameActions::QueryNested(&rideEntranceExitPlaceAction);
if (res.Error != GameActions::Status::Ok)
{
return res;
}
totalCost += res.Cost;
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
break;
} while (!(tile_element++)->IsLastForTile());
}
else
{
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery(newCoords, false);
if (res.Error != GameActions::Status::Ok)
{
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
totalCost += res.Cost;
tds.EntranceExitPlaced = true;
_trackDesignPlaceStateEntranceExitPlaced = true;
}
break;
}
}
return result.value();
}
if (tds.PlaceOperation == PTD_OPERATION_REMOVE_GHOST)
@@ -1877,7 +1790,7 @@ static GameActions::Result TrackDesignPlaceVirtual(
const auto& rtd = GetRideTypeDescriptor(td6->type);
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE))
{
trackPlaceRes = TrackDesignPlaceMaze(tds, td6, coords, ride);
trackPlaceRes = TrackDesignPlaceMaze(tds, *td6, coords, ride);
}
else
{

View File

@@ -177,25 +177,6 @@ private:
CoordsXYE MazeGetFirstElement(const Ride& ride);
};
// Only written to in RCT2, not used in OpenRCT2. All of these are elements that had to be invented in RCT1.
enum : uint32_t
{
TRACK_FLAGS_CONTAINS_VERTICAL_LOOP = (1 << 7),
TRACK_FLAGS_CONTAINS_INLINE_TWIST = (1 << 17),
TRACK_FLAGS_CONTAINS_HALF_LOOP = (1 << 18),
TRACK_FLAGS_CONTAINS_CORKSCREW = (1 << 19),
TRACK_FLAGS_CONTAINS_WATER_SPLASH = (1 << 27),
TRACK_FLAGS_CONTAINS_BARREL_ROLL = (1 << 29),
TRACK_FLAGS_CONTAINS_POWERED_LIFT = (1 << 30),
TRACK_FLAGS_CONTAINS_LARGE_HALF_LOOP = (1u << 31),
};
enum : uint32_t
{
TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER = (1 << 1),
TRACK_FLAGS2_SIX_FLAGS_RIDE_DEPRECATED = (1u << 31) // Not used anymore.
};
enum
{
TDPF_PLACE_SCENERY = 1 << 0,
@@ -221,13 +202,6 @@ enum
static constexpr uint8_t PTD_OPERATION_FLAG_IS_REPLAY = (1 << 7);
enum
{
MAZE_ELEMENT_TYPE_MAZE_TRACK = 0,
MAZE_ELEMENT_TYPE_ENTRANCE = (1 << 3),
MAZE_ELEMENT_TYPE_EXIT = (1 << 7)
};
extern bool gTrackDesignSceneryToggle;
extern bool _trackDesignDrawingPreview;