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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user