OpenRCT2/src/openrct2-ui/windows/RideConstruction.cpp

4625 lines
210 KiB
C++
Raw Normal View History

2014-11-04 22:39:03 +01:00
/*****************************************************************************
* Copyright (c) 2014-2024 OpenRCT2 developers
2014-11-04 22:39:03 +01:00
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
2014-11-04 22:39:03 +01:00
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
2014-11-04 22:39:03 +01:00
*****************************************************************************/
#include "../ride/Construction.h"
#include <algorithm>
#include <limits>
2018-06-22 23:21:44 +02:00
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
2017-12-13 13:02:24 +01:00
#include <openrct2/Cheats.h>
#include <openrct2/Context.h>
2017-11-30 18:17:06 +01:00
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
2017-12-12 14:52:57 +01:00
#include <openrct2/Input.h>
2022-08-11 00:00:58 +02:00
#include <openrct2/actions/MazeSetTrackAction.h>
#include <openrct2/actions/RideDemolishAction.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/RideEntranceExitPlaceAction.h>
2022-08-11 00:00:58 +02:00
#include <openrct2/actions/RideSetStatusAction.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/TrackPlaceAction.h>
#include <openrct2/actions/TrackRemoveAction.h>
#include <openrct2/actions/TrackSetBrakeSpeedAction.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/interface/Viewport.h>
2021-12-12 00:06:06 +01:00
#include <openrct2/localisation/Formatter.h>
2018-01-06 18:32:25 +01:00
#include <openrct2/localisation/Localisation.h>
#include <openrct2/network/network.h>
#include <openrct2/object/ObjectManager.h>
#include <openrct2/paint/tile_element/Paint.TileElement.h>
#include <openrct2/platform/Platform.h>
2017-12-31 13:21:34 +01:00
#include <openrct2/ride/Ride.h>
2021-12-18 19:50:29 +01:00
#include <openrct2/ride/RideConstruction.h>
2018-01-10 00:00:09 +01:00
#include <openrct2/ride/RideData.h>
#include <openrct2/ride/Track.h>
#include <openrct2/ride/TrackData.h>
#include <openrct2/sprites.h>
#include <openrct2/util/Math.hpp>
#include <openrct2/windows/Intent.h>
2017-12-14 10:34:12 +01:00
#include <openrct2/world/Entrance.h>
2018-01-11 10:59:26 +01:00
#include <openrct2/world/Footpath.h>
2018-03-19 23:28:40 +01:00
#include <openrct2/world/Park.h>
using namespace OpenRCT2::TrackMetaData;
namespace OpenRCT2::Ui::Windows
{
static constexpr StringId WINDOW_TITLE = STR_RIDE_CONSTRUCTION_WINDOW_TITLE;
static constexpr int32_t WH = 394;
static constexpr int32_t WW = 210;
static constexpr uint16_t ARROW_PULSE_DURATION = 200;
// Width of the group boxes, e.g. “Banking”
static constexpr int32_t GW = WW - 6;
2015-06-27 16:17:54 +02:00
#pragma region Widgets
enum
{
WIDX_BACKGROUND,
WIDX_TITLE,
WIDX_CLOSE,
WIDX_DIRECTION_GROUPBOX,
WIDX_SLOPE_GROUPBOX,
WIDX_BANKING_GROUPBOX,
WIDX_LEFT_CURVE_VERY_SMALL,
WIDX_LEFT_CURVE_SMALL,
WIDX_LEFT_CURVE,
WIDX_LEFT_CURVE_LARGE,
WIDX_STRAIGHT,
WIDX_RIGHT_CURVE_LARGE,
WIDX_RIGHT_CURVE,
WIDX_RIGHT_CURVE_SMALL,
WIDX_RIGHT_CURVE_VERY_SMALL,
WIDX_SPECIAL_TRACK_DROPDOWN,
WIDX_SLOPE_DOWN_STEEP,
WIDX_SLOPE_DOWN,
WIDX_LEVEL,
WIDX_SLOPE_UP,
WIDX_SLOPE_UP_STEEP,
WIDX_CHAIN_LIFT,
WIDX_BANK_LEFT,
WIDX_BANK_STRAIGHT,
WIDX_BANK_RIGHT,
WIDX_CONSTRUCT,
WIDX_DEMOLISH,
WIDX_PREVIOUS_SECTION,
WIDX_NEXT_SECTION,
WIDX_ENTRANCE_EXIT_GROUPBOX,
WIDX_ENTRANCE,
WIDX_EXIT,
WIDX_ROTATE,
WIDX_U_TRACK,
WIDX_O_TRACK,
WIDX_SEAT_ROTATION_GROUPBOX,
WIDX_SEAT_ROTATION_ANGLE_SPINNER,
WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP,
WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN,
WIDX_SIMULATE,
WIDX_SPEED_GROUPBOX = WIDX_BANKING_GROUPBOX,
WIDX_SPEED_SETTING_SPINNER = WIDX_BANK_LEFT,
WIDX_SPEED_SETTING_SPINNER_UP = WIDX_BANK_STRAIGHT,
WIDX_SPEED_SETTING_SPINNER_DOWN = WIDX_BANK_RIGHT,
};
validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_CONSTRUCT);
validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ENTRANCE);
validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_EXIT);
validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ROTATE);
// clang-format off
static Widget _rideConstructionWidgets[] = {
2020-05-09 16:44:21 +02:00
WINDOW_SHIM(WINDOW_TITLE, WW, WH),
MakeWidget ({ 3, 17}, { GW, 57}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_RIDE_CONSTRUCTION_DIRECTION ),
MakeWidget ({ 3, 76}, { GW, 41}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_RIDE_CONSTRUCTION_SLOPE ),
MakeWidget ({ 3, 120}, { GW, 41}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_RIDE_CONSTRUCTION_ROLL_BANKING ),
MakeWidget ({ 6, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_LEFT_CURVE_SMALL), STR_RIDE_CONSTRUCTION_LEFT_CURVE_VERY_SMALL_TIP ),
2023-12-23 14:40:59 +01:00
MakeWidget ({ 28, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_ICON_MEDIUM_CURVE_LEFT), STR_RIDE_CONSTRUCTION_LEFT_CURVE_SMALL_TIP ),
MakeWidget ({ 50, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_LEFT_CURVE), STR_RIDE_CONSTRUCTION_LEFT_CURVE_TIP ),
MakeWidget ({ 72, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE), STR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE_TIP ),
MakeWidget ({ 94, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_STRAIGHT), STR_RIDE_CONSTRUCTION_STRAIGHT_TIP ),
MakeWidget ({116, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_CURVE_LARGE), STR_RIDE_CONSTRUCTION_RIGHT_CURVE_LARGE_TIP ),
MakeWidget ({138, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_CURVE), STR_RIDE_CONSTRUCTION_RIGHT_CURVE_TIP ),
2023-12-23 14:40:59 +01:00
MakeWidget ({160, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_ICON_MEDIUM_CURVE_RIGHT), STR_RIDE_CONSTRUCTION_RIGHT_CURVE_SMALL_TIP ),
MakeWidget ({182, 29}, { 22, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_CURVE_SMALL), STR_RIDE_CONSTRUCTION_RIGHT_CURVE_VERY_SMALL_TIP ),
MakeWidget ({ 6, 55}, { GW - 6, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_YELLOW_STRING, STR_RIDE_CONSTRUCTION_OTHER_TRACK_CONFIGURATIONS_TIP),
MakeWidget ({ 45, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP), STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP ),
MakeWidget ({ 69, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN), STR_RIDE_CONSTRUCTION_SLOPE_DOWN_TIP ),
MakeWidget ({ 93, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_LEVEL), STR_RIDE_CONSTRUCTION_LEVEL_TIP ),
MakeWidget ({117, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP), STR_RIDE_CONSTRUCTION_SLOPE_UP_TIP ),
MakeWidget ({141, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP), STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP ),
MakeWidget ({178, 88}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_CHAIN_LIFT), STR_RIDE_CONSTRUCTION_CHAIN_LIFT_TIP ),
MakeWidget ({ 69, 132}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_LEFT_BANK), STR_RIDE_CONSTRUCTION_ROLL_FOR_LEFT_CURVE_TIP ),
MakeWidget ({ 93, 132}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_NO_BANK), STR_RIDE_CONSTRUCTION_NO_ROLL_TIP ),
MakeWidget ({117, 132}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_BANK), STR_RIDE_CONSTRUCTION_ROLL_FOR_RIGHT_CURVE_TIP ),
MakeWidget ({ 3, 164}, { GW, 170}, WindowWidgetType::ImgBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_RIDE_CONSTRUCTION_CONSTRUCT_SELECTED_SECTION_TIP),
MakeWidget ({ 82, 338}, { 46, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_DEMOLISH_CURRENT_SECTION), STR_RIDE_CONSTRUCTION_REMOVE_HIGHLIGHTED_SECTION_TIP),
MakeWidget ({ 52, 338}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_PREVIOUS), STR_RIDE_CONSTRUCTION_MOVE_TO_PREVIOUS_SECTION_TIP ),
MakeWidget ({134, 338}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_NEXT), STR_RIDE_CONSTRUCTION_MOVE_TO_NEXT_SECTION_TIP ),
MakeWidget ({ 3, 362}, { GW, 28}, WindowWidgetType::Groupbox, WindowColour::Primary ),
MakeWidget ({ 31, 372}, { 70, 12}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_CONSTRUCTION_ENTRANCE, STR_RIDE_CONSTRUCTION_ENTRANCE_TIP ),
MakeWidget ({109, 372}, { 70, 12}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_CONSTRUCTION_EXIT, STR_RIDE_CONSTRUCTION_EXIT_TIP ),
MakeWidget ({ 94, 338}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_ROTATE_ARROW), STR_ROTATE_90_TIP ),
MakeWidget ({ 41, 132}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_U_SHAPED_TRACK), STR_RIDE_CONSTRUCTION_U_SHAPED_OPEN_TRACK_TIP ),
MakeWidget ({144, 132}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK), STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP ),
MakeWidget ({118, 120}, { 89, 41}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_RIDE_CONSTRUCTION_SEAT_ROT ),
MakeSpinnerWidgets({123, 138}, { 58, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, 0, STR_RIDE_CONSTRUCTION_SELECT_SEAT_ROTATION_ANGLE_TIP),
MakeWidget ({161, 338}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_SIMULATE), STR_SIMULATE_RIDE_TIP ),
kWidgetsEnd,
};
// clang-format on
2015-06-27 16:17:54 +02:00
#pragma endregion
static bool _trackPlaceCtrlState;
static int32_t _trackPlaceCtrlZ;
static bool _trackPlaceShiftState;
static ScreenCoordsXY _trackPlaceShiftStart;
static int32_t _trackPlaceShiftZ;
static int32_t _trackPlaceZ;
static money64 _trackPlaceCost;
static StringId _trackPlaceErrorMessage;
static bool _autoRotatingShop;
static bool _gotoStartPlacementMode = false;
static constexpr StringId RideConstructionSeatAngleRotationStrings[] = {
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_135,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_45,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_0, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_45,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_135,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_225,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_270, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_315,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_360, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_405,
STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_450, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_495,
};
static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type);
/* move to ride.c */
static void CloseRideWindowForConstruction(RideId rideId)
{
WindowBase* w = WindowFindByNumber(WindowClass::Ride, rideId.ToUnderlying());
if (w != nullptr && w->page == 1)
WindowClose(*w);
}
static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result);
static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result);
static void CloseConstructWindowOnCompletion(const Ride& ride);
class RideConstructionWindow final : public Window
{
private:
uint8_t _currentlyShowingBrakeOrBoosterSpeed{};
SpecialElementsDropdownState _specialElementDropdownState;
bool _autoOpeningShop{};
public:
void OnOpen() override
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
widgets = _rideConstructionWidgets;
number = _currentRideIndex.ToUnderlying();
InitScrollWidgets();
WindowPushOthersRight(*this);
ShowGridlines();
_currentTrackPrice = kMoney64Undefined;
_currentBrakeSpeed2 = 8;
_currentSeatRotationAngle = 4;
_currentTrackCurve = currentRide->GetRideTypeDescriptor().StartTrackPiece | RideConstructionSpecialPieceSelected;
_currentTrackPitchEnd = TrackPitch::None;
_currentTrackRollEnd = TrackRoll::None;
_currentTrackLiftHill = 0;
_currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES;
if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_START_CONSTRUCTION_INVERTED))
_currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_TYPE;
_previousTrackRollEnd = TrackRoll::None;
_previousTrackPitchEnd = TrackPitch::None;
_currentTrackPieceDirection = 0;
_rideConstructionState = RideConstructionState::Place;
_currentTrackSelectionFlags = 0;
_autoOpeningShop = false;
_autoRotatingShop = true;
_trackPlaceCtrlState = false;
_trackPlaceShiftState = false;
}
void OnClose() override
{
RideConstructionInvalidateCurrentTrack();
ViewportSetVisibility(ViewportVisibility::Default);
MapInvalidateMapSelectionTiles();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
// In order to cancel the yellow arrow correctly the
// selection tool should be cancelled. Don't do a tool cancel if
// another window has already taken control of tool.
if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number)
ToolCancel();
HideGridlines();
2017-10-07 14:48:54 +02:00
// If we demolish a currentRide all windows will be closed including the construction window,
// the currentRide at this point is already gone.
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
if (RideTryGetOriginElement(*currentRide, nullptr))
{
// Auto open shops if required.
if (currentRide->mode == RideMode::ShopStall && gConfigGeneral.AutoOpenShops)
{
// HACK: Until we find a good a way to defer the game command for opening the shop, stop this
// from getting stuck in an infinite loop as opening the currentRide will try to close this window
if (!_autoOpeningShop)
{
_autoOpeningShop = true;
auto gameAction = RideSetStatusAction(currentRide->id, RideStatus::Open);
GameActions::Execute(&gameAction);
_autoOpeningShop = false;
}
}
currentRide->SetToDefaultInspectionInterval();
auto intent = Intent(WindowClass::Ride);
intent.PutExtra(INTENT_EXTRA_RIDE_ID, currentRide->id.ToUnderlying());
ContextOpenIntent(&intent);
}
else
{
auto gameAction = RideDemolishAction(currentRide->id, RIDE_MODIFY_DEMOLISH);
gameAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
GameActions::Execute(&gameAction);
}
}
void OnResize() override
{
ResizeFrame();
WindowRideConstructionUpdateEnabledTrackPieces();
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
const auto& rtd = currentRide->GetRideTypeDescriptor();
const auto currentTrackDrawerDescriptor = getCurrentTrackDrawerDescriptor(rtd);
uint64_t disabledWidgets = 0;
if (_currentTrackCurve & RideConstructionSpecialPieceSelected)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX)
| (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL)
| (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT)
| (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT);
}
// Disable large curves if the start or end of the track is sloped and large sloped curves are not available
if ((_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None))
{
if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE)
|| !(_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Down25)
|| !(_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Down25))
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE);
}
}
if (IsTrackEnabled(TRACK_SLOPE_CURVE) && IsTrackEnabled(TRACK_CURVE_VERY_SMALL))
{
// Disable small curves if the start or end of the track is sloped.
if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
}
}
if (!IsTrackEnabled(TRACK_SLOPE_CURVE))
2018-06-22 23:21:44 +02:00
{
if (IsTrackEnabled(TRACK_CURVE_VERTICAL))
{
// Disable all curves only on vertical track
if (_previousTrackPitchEnd != TrackPitch::Up90 || _currentTrackPitchEnd != TrackPitch::Up90)
{
if (_previousTrackPitchEnd != TrackPitch::Down90 || _currentTrackPitchEnd != TrackPitch::Down90)
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
}
}
}
else
{
// Disable all curves on sloped track
if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None)
{
2022-10-17 19:21:18 +02:00
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
}
}
}
if (!IsTrackEnabled(TRACK_FLAT_ROLL_BANKING))
2018-06-22 23:21:44 +02:00
{
// Disable banking
disabledWidgets |= (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT)
| (1uLL << WIDX_BANK_RIGHT);
}
// Disable banking if the start track is steep and the end of the track becomes flat.
if ((_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Up60)
&& _currentTrackPitchEnd == TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
if (!IsTrackEnabled(TRACK_SLOPE) && !IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN)
&& !IsTrackEnabled(TRACK_SLOPE_STEEP_UP))
{
if (!currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL))
{
// Disable all slopes
disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_SLOPE_DOWN_STEEP)
| (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP)
| (1uLL << WIDX_SLOPE_UP_STEEP);
}
}
// If ride type does not have access to diagonal sloped turns, disallow simultaneous use of banked and sloped
// diagonals
if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE) && TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))
{
if (_currentTrackPitchEnd != TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
else if (_currentTrackRollEnd != TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP);
}
}
if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT)
&& !GetGameState().Cheats.EnableAllDrawableTrackPieces)
{
// Disable lift hill toggle and banking if current track piece is uphill
if (_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Up60
|| _currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60)
disabledWidgets |= 1uLL << WIDX_CHAIN_LIFT | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
// Disable upward slope if current track piece is not flat
if ((_previousTrackPitchEnd != TrackPitch::None || _previousTrackRollEnd != TrackRoll::None)
&& !(_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED))
disabledWidgets |= (1uLL << WIDX_SLOPE_UP);
}
if (_rideConstructionState == RideConstructionState::State0)
{
disabledWidgets |= (1uLL << WIDX_CONSTRUCT) | (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION)
| (1uLL << WIDX_NEXT_SECTION);
}
switch (_currentTrackCurve)
{
case EnumValue(TrackCurve::LeftVerySmall):
case EnumValue(TrackCurve::LeftSmall):
case EnumValue(TrackCurve::Left):
case EnumValue(TrackCurve::LeftLarge):
2022-10-17 19:21:18 +02:00
disabledWidgets |= (1uLL << WIDX_BANK_RIGHT);
if (_previousTrackRollEnd == TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT);
}
else
{
disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT);
}
break;
case EnumValue(TrackCurve::RightLarge):
case EnumValue(TrackCurve::Right):
case EnumValue(TrackCurve::RightSmall):
case EnumValue(TrackCurve::RightVerySmall):
disabledWidgets |= (1uLL << WIDX_BANK_LEFT);
if (_previousTrackRollEnd == TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_BANK_RIGHT);
}
else
{
disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT);
}
break;
}
if (!IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING))
{
if (_currentTrackRollEnd != TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP);
}
}
if (_previousTrackPitchEnd == _currentTrackPitchEnd)
{
switch (_currentTrackPitchEnd)
{
case TrackPitch::Up60:
case TrackPitch::Down60:
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
if (!IsTrackEnabled(TRACK_SLOPE_CURVE_STEEP))
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL);
}
break;
case TrackPitch::Up90:
case TrackPitch::Down90:
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
if (!IsTrackEnabled(TRACK_CURVE_VERTICAL))
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL);
}
break;
default:
break;
}
}
else
2018-06-22 23:21:44 +02:00
{
// Disable all curves
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
2018-06-22 23:21:44 +02:00
}
switch (_previousTrackPitchEnd)
2018-06-22 23:21:44 +02:00
{
case TrackPitch::None:
if (_currentTrackCurve != EnumValue(TrackCurve::None)
|| (IsTrackEnabled(TRACK_SLOPE_STEEP_LONG)
&& TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection)))
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP);
}
break;
case TrackPitch::Down25:
disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP);
break;
2024-01-14 23:36:12 +01:00
case TrackPitch::Down60:
disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP);
if (!IsTrackEnabled(TRACK_SLOPE_LONG)
&& !(
IsTrackEnabled(TRACK_SLOPE_STEEP_LONG)
&& !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection)))
{
disabledWidgets |= (1uLL << WIDX_LEVEL);
}
break;
case TrackPitch::Up25:
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN);
break;
case TrackPitch::Up60:
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN);
if (!IsTrackEnabled(TRACK_SLOPE_LONG)
&& !(
IsTrackEnabled(TRACK_SLOPE_STEEP_LONG)
&& !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection)))
{
disabledWidgets |= (1uLL << WIDX_LEVEL);
}
break;
case TrackPitch::Down90:
case TrackPitch::Up90:
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP);
2024-01-14 23:36:12 +01:00
break;
2018-06-22 23:21:44 +02:00
}
if (_previousTrackPitchEnd == TrackPitch::None)
{
if (!IsTrackEnabled(TRACK_SLOPE_LONG) && !IsTrackEnabled(TRACK_SLOPE_STEEP_LONG))
{
2022-10-17 19:21:18 +02:00
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP);
}
}
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL))
{
if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackPieceDirection < 4)
2018-06-22 23:21:44 +02:00
{
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP);
2018-06-22 23:21:44 +02:00
}
if (_previousTrackPitchEnd == TrackPitch::Up90)
2018-06-22 23:21:44 +02:00
{
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP);
}
if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackPieceDirection < 4)
{
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP);
2018-06-22 23:21:44 +02:00
}
}
if (_previousTrackRollEnd == TrackRoll::Left)
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE_LARGE) | (1uLL << WIDX_BANK_RIGHT);
}
if (_previousTrackRollEnd == TrackRoll::Right)
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE)
| (1uLL << WIDX_BANK_LEFT);
}
if (_currentTrackRollEnd != _previousTrackRollEnd)
{
disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE_LARGE) | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE)
| (1uLL << WIDX_LEFT_CURVE_LARGE);
}
if (_currentTrackPitchEnd != TrackPitch::None)
2018-06-22 23:21:44 +02:00
{
if (IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING))
2018-06-22 23:21:44 +02:00
{
if (_previousTrackPitchEnd == TrackPitch::None)
2018-06-22 23:21:44 +02:00
{
if (_currentTrackPitchEnd != TrackPitch::Up25 && _currentTrackPitchEnd != TrackPitch::Down25)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
}
else
{
if (_currentTrackPitchEnd != _previousTrackPitchEnd)
{
2022-10-17 19:21:18 +02:00
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
else
{
if (_currentTrackPitchEnd != TrackPitch::Up25 && _currentTrackPitchEnd != TrackPitch::Down25)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
}
}
}
else
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
}
}
if (_currentTrackRollEnd != TrackRoll::None || _previousTrackRollEnd != TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT);
}
if (_currentTrackCurve != EnumValue(TrackCurve::None))
{
if (!IsTrackEnabled(TRACK_LIFT_HILL_CURVE))
{
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
if (_currentTrackPitchEnd == TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
if (_currentTrackPitchEnd == TrackPitch::Up60)
{
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
if (_currentTrackPitchEnd == TrackPitch::Down60)
{
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
}
if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90)
{
2022-10-17 19:21:18 +02:00
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
if (!IsTrackEnabled(TRACK_LIFT_HILL_STEEP))
{
if (_previousTrackPitchEnd == TrackPitch::Up60 || _currentTrackPitchEnd == TrackPitch::Up60)
{
disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT);
}
}
if (_previousTrackRollEnd == TrackRoll::UpsideDown)
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE)
| (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE_LARGE);
}
if (_currentTrackCurve != EnumValue(TrackCurve::None))
{
if (_currentTrackPitchEnd == TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP);
}
if (_currentTrackPitchEnd == _previousTrackPitchEnd)
{
if (_currentTrackPitchEnd == TrackPitch::Up25)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP);
if (_currentTrackCurve == EnumValue(TrackCurve::Left)
|| _currentTrackCurve == EnumValue(TrackCurve::Right)
|| _rideConstructionState != RideConstructionState::Back
|| !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED))
{
disabledWidgets |= (1uLL << WIDX_LEVEL);
}
}
if (_currentTrackPitchEnd == TrackPitch::Down25)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP);
if (_currentTrackCurve == EnumValue(TrackCurve::Left)
|| _currentTrackCurve == EnumValue(TrackCurve::Right)
|| _rideConstructionState != RideConstructionState::Front
|| !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED))
{
disabledWidgets |= (1uLL << WIDX_LEVEL);
}
}
}
else if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED))
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP);
if (_currentTrackRollEnd == TrackRoll::Left)
{
disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT);
disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT);
}
if (_currentTrackRollEnd == TrackRoll::Right)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT);
disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT);
}
if (_currentTrackRollEnd == TrackRoll::None)
{
disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT);
disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT);
}
if (_currentTrackPitchEnd == TrackPitch::None)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP);
disabledWidgets &= ~(1uLL << WIDX_LEVEL);
}
if (_currentTrackPitchEnd == TrackPitch::Up25)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL);
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP);
}
if (_currentTrackPitchEnd == TrackPitch::Down25)
{
disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP);
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN);
}
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall))
{
disabledWidgets &= ~(1uLL << WIDX_LEFT_CURVE_SMALL);
}
if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall))
{
disabledWidgets &= ~(1uLL << WIDX_RIGHT_CURVE_SMALL);
}
}
}
if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Up60)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_UP);
}
if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Down60)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN);
}
if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) && !GetGameState().Cheats.EnableChainLiftOnAllTrack)
{
if (_currentTrackPitchEnd != TrackPitch::None && !IsTrackEnabled(TRACK_LIFT_HILL_CURVE))
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE)
| (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE_LARGE);
}
if (!IsTrackEnabled(TRACK_LIFT_HILL_STEEP))
{
if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP)
{
disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP);
}
}
}
if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackCurve != EnumValue(TrackCurve::None))
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_LEVEL);
}
if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackCurve != EnumValue(TrackCurve::None))
{
disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP_STEEP);
}
if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90)
{
if (_currentTrackCurve != EnumValue(TrackCurve::None))
{
disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP);
}
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE);
if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL))
{
disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE);
}
}
else if (_currentTrackPitchEnd == TrackPitch::Down90 || _previousTrackPitchEnd == TrackPitch::Down90)
{
if (_currentTrackCurve != EnumValue(TrackCurve::None))
{
disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP);
}
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE);
if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL))
{
disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE);
}
}
// If the previous track is flat and the next track is flat, attempt to show buttons for helixes
if (_currentTrackPitchEnd == TrackPitch::None && _currentTrackPitchEnd == _previousTrackPitchEnd)
{
// If the bank is none, attempt to show unbanked quarter helixes
if (_currentTrackRollEnd == TrackRoll::None
&& (_currentTrackCurve == EnumValue(TrackCurve::Left)
|| _currentTrackCurve == EnumValue(TrackCurve::Right)))
{
if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP);
if (IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP);
}
// If the track is banked left or right and curvature is standard size (2.5 tile radius), attempt to show
// buttons for half or quarter helixes
else if (
(_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right)
&& (_currentTrackCurve == EnumValue(TrackCurve::Left)
|| _currentTrackCurve == EnumValue(TrackCurve::Right)))
{
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP);
if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP);
}
// If the track is banked left or right and curvature is small size (1.5 tile radius), attempt to show buttons
// for half helixes
else if (
(_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right)
&& (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)
|| _currentTrackCurve == EnumValue(TrackCurve::RightSmall)))
{
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP);
if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF))
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP);
}
}
if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED))
{
if (_rideConstructionState == RideConstructionState::Front)
{
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)
|| _currentTrackCurve == EnumValue(TrackCurve::RightSmall))
{
if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None
&& (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT)
|| GetGameState().Cheats.EnableAllDrawableTrackPieces))
{
disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP);
}
}
}
else if (_rideConstructionState == RideConstructionState::Back)
{
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)
|| _currentTrackCurve == EnumValue(TrackCurve::RightSmall))
{
if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None)
{
disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN);
}
}
}
}
if (_currentTrackPieceDirection >= 4)
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL)
| (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL);
}
if (_rideConstructionState == RideConstructionState::Front)
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_NEXT_SECTION);
if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr))
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_CONSTRUCT);
}
}
else if (_rideConstructionState == RideConstructionState::Back)
2018-06-22 23:21:44 +02:00
{
disabledWidgets |= (1uLL << WIDX_PREVIOUS_SECTION);
if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr))
{
disabledWidgets |= (1uLL << WIDX_CONSTRUCT);
}
}
if (currentTrackDrawerDescriptor.HasCoveredPieces())
2018-06-22 23:21:44 +02:00
{
disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX);
}
if (_rideConstructionState == RideConstructionState::EntranceExit
|| _rideConstructionState == RideConstructionState::Selected)
{
disabledWidgets |= (1uLL << WIDX_DIRECTION_GROUPBOX) | (1uLL << WIDX_SLOPE_GROUPBOX)
| (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL)
| (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE)
| (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL)
| (1uLL << WIDX_SPECIAL_TRACK_DROPDOWN) | (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN)
| (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT)
| (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT)
| (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE);
}
if (_currentlyShowingBrakeOrBoosterSpeed)
{
disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX);
disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT);
disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT);
disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT);
}
// If chain lift cheat is enabled then show the chain lift widget no matter what
if (GetGameState().Cheats.EnableChainLiftOnAllTrack)
2018-06-22 23:21:44 +02:00
{
disabledWidgets &= ~(1uLL << WIDX_CHAIN_LIFT);
}
// Set and invalidate the changed widgets
uint64_t currentDisabledWidgets = disabled_widgets;
if (currentDisabledWidgets == disabledWidgets)
return;
for (WidgetIndex i = 0; i < 64; i++)
2018-06-22 23:21:44 +02:00
{
if ((disabledWidgets & (1uLL << i)) != (currentDisabledWidgets & (1uLL << i)))
{
WidgetInvalidate(*this, i);
}
}
disabled_widgets = disabledWidgets;
}
void OnUpdate() override
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
2018-06-22 23:21:44 +02:00
{
return;
}
// Close construction window if currentRide is not closed,
// editing currentRide while open will cause many issues until properly handled
if (currentRide->status != RideStatus::Closed && currentRide->status != RideStatus::Simulating)
2018-06-22 23:21:44 +02:00
{
Close();
return;
}
switch (_currentTrackCurve)
{
case TrackElemType::SpinningTunnel | RideConstructionSpecialPieceSelected:
case TrackElemType::Whirlpool | RideConstructionSpecialPieceSelected:
case TrackElemType::Rapids | RideConstructionSpecialPieceSelected:
case TrackElemType::Waterfall | RideConstructionSpecialPieceSelected:
WidgetInvalidate(*this, WIDX_CONSTRUCT);
break;
}
if (_rideConstructionState == RideConstructionState::Place)
2018-06-22 23:21:44 +02:00
{
if (!WidgetIsActiveTool(*this, WIDX_CONSTRUCT))
{
Close();
return;
}
}
if (_rideConstructionState == RideConstructionState::EntranceExit)
2018-06-22 23:21:44 +02:00
{
if (!WidgetIsActiveTool(*this, WIDX_ENTRANCE) && !WidgetIsActiveTool(*this, WIDX_EXIT))
{
_rideConstructionState = gRideEntranceExitPlacePreviousRideConstructionState;
WindowRideConstructionUpdateActiveElements();
}
}
switch (_rideConstructionState)
{
case RideConstructionState::Front:
case RideConstructionState::Back:
case RideConstructionState::Selected:
if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))
&& gCurrentToolWidget.window_classification == WindowClass::RideConstruction)
{
ToolCancel();
}
break;
default:
break;
}
UpdateGhostTrackAndArrow();
}
void OnMouseUp(WidgetIndex widgetIndex) override
2018-06-22 23:21:44 +02:00
{
WindowRideConstructionUpdateEnabledTrackPieces();
switch (widgetIndex)
2018-06-22 23:21:44 +02:00
{
case WIDX_CLOSE:
Close();
break;
case WIDX_NEXT_SECTION:
RideSelectNextSection();
break;
case WIDX_PREVIOUS_SECTION:
RideSelectPreviousSection();
break;
case WIDX_CONSTRUCT:
Construct();
// Force any footpath construction to recheck the area.
gProvisionalFootpath.Flags |= PROVISIONAL_PATH_FLAG_2;
break;
case WIDX_DEMOLISH:
MouseUpDemolish();
break;
case WIDX_ROTATE:
Rotate();
break;
case WIDX_ENTRANCE:
EntranceClick();
break;
case WIDX_EXIT:
ExitClick();
break;
case WIDX_SIMULATE:
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide != nullptr)
{
auto status = currentRide->status == RideStatus::Simulating ? RideStatus::Closed
: RideStatus::Simulating;
auto gameAction = RideSetStatusAction(currentRide->id, status);
GameActions::Execute(&gameAction);
}
break;
}
}
}
void OnMouseDown(WidgetIndex widgetIndex) override
2018-06-22 23:21:44 +02:00
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
2018-06-22 23:21:44 +02:00
{
return;
}
WindowRideConstructionUpdateEnabledTrackPieces();
switch (widgetIndex)
2018-06-22 23:21:44 +02:00
{
case WIDX_LEFT_CURVE:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::Left);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_RIGHT_CURVE:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::Right);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_LEFT_CURVE_SMALL:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::LeftSmall);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_RIGHT_CURVE_SMALL:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::RightSmall);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_LEFT_CURVE_VERY_SMALL:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::LeftVerySmall);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_RIGHT_CURVE_VERY_SMALL:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::RightVerySmall);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_LEFT_CURVE_LARGE:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::LeftLarge);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_RIGHT_CURVE_LARGE:
RideConstructionInvalidateCurrentTrack();
_currentTrackCurve = EnumValue(TrackCurve::RightLarge);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_STRAIGHT:
RideConstructionInvalidateCurrentTrack();
if (_currentTrackCurve != EnumValue(TrackCurve::None))
_currentTrackRollEnd = TrackRoll::None;
_currentTrackCurve = EnumValue(TrackCurve::None);
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_SLOPE_DOWN_STEEP:
RideConstructionInvalidateCurrentTrack();
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF))
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownLarge
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightHalfBankedHelixDownLarge
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownSmall
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightHalfBankedHelixDownSmall
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
}
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER))
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeDown
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeDown
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
}
if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER))
{
if (_currentTrackRollEnd == TrackRoll::None)
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left))
{
_currentTrackCurve = TrackElemType::LeftQuarterHelixLargeDown
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right))
{
_currentTrackCurve = TrackElemType::RightQuarterHelixLargeDown
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
}
}
if (widgets[WIDX_SLOPE_DOWN_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP)
{
UpdateLiftHillSelected(TrackPitch::Down60);
}
else
{
UpdateLiftHillSelected(TrackPitch::Up90);
}
break;
case WIDX_SLOPE_DOWN:
RideConstructionInvalidateCurrentTrack();
if (_rideConstructionState == RideConstructionState::Back && _currentTrackRollEnd != TrackRoll::None)
{
_currentTrackRollEnd = TrackRoll::None;
}
UpdateLiftHillSelected(TrackPitch::Down25);
break;
case WIDX_LEVEL:
RideConstructionInvalidateCurrentTrack();
if (_rideConstructionState == RideConstructionState::Front && _previousTrackPitchEnd == TrackPitch::Down25)
2018-06-22 23:21:44 +02:00
{
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall))
{
_currentTrackRollEnd = TrackRoll::Left;
}
else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall))
{
_currentTrackRollEnd = TrackRoll::Right;
}
2018-06-22 23:21:44 +02:00
}
else if (
_rideConstructionState == RideConstructionState::Back && _previousTrackPitchEnd == TrackPitch::Up25)
2018-06-22 23:21:44 +02:00
{
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall))
{
_currentTrackRollEnd = TrackRoll::Left;
}
else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall))
{
_currentTrackRollEnd = TrackRoll::Right;
}
2018-06-22 23:21:44 +02:00
}
UpdateLiftHillSelected(TrackPitch::None);
break;
case WIDX_SLOPE_UP:
RideConstructionInvalidateCurrentTrack();
if (_rideConstructionState == RideConstructionState::Front && _currentTrackRollEnd != TrackRoll::None)
2018-06-22 23:21:44 +02:00
{
_currentTrackRollEnd = TrackRoll::None;
2018-06-22 23:21:44 +02:00
}
if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL))
2018-06-22 23:21:44 +02:00
{
if (_rideConstructionState == RideConstructionState::Front
&& _currentTrackCurve == EnumValue(TrackCurve::None))
{
_currentTrackCurve = TrackElemType::ReverseFreefallSlope | RideConstructionSpecialPieceSelected;
WindowRideConstructionUpdateActiveElements();
}
2018-06-22 23:21:44 +02:00
}
else
2018-06-22 23:21:44 +02:00
{
UpdateLiftHillSelected(TrackPitch::Up25);
2018-06-22 23:21:44 +02:00
}
break;
case WIDX_SLOPE_UP_STEEP:
RideConstructionInvalidateCurrentTrack();
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF))
2018-06-22 23:21:44 +02:00
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpLarge
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightHalfBankedHelixUpLarge
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpSmall
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightHalfBankedHelixUpSmall
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
2018-06-22 23:21:44 +02:00
}
if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER))
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left)
{
_currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeUp
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right)
{
_currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeUp
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
}
if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER))
{
if (_currentTrackRollEnd == TrackRoll::None)
{
if (_currentTrackCurve == EnumValue(TrackCurve::Left))
{
_currentTrackCurve = TrackElemType::LeftQuarterHelixLargeUp
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
if (_currentTrackCurve == EnumValue(TrackCurve::Right))
{
_currentTrackCurve = TrackElemType::RightQuarterHelixLargeUp
| RideConstructionSpecialPieceSelected;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
}
}
}
if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP)
{
UpdateLiftHillSelected(TrackPitch::Up60);
}
else
{
UpdateLiftHillSelected(TrackPitch::Down90);
}
break;
case WIDX_CHAIN_LIFT:
RideConstructionInvalidateCurrentTrack();
_currentTrackLiftHill ^= CONSTRUCTION_LIFT_HILL_SELECTED;
if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)
&& !GetGameState().Cheats.EnableChainLiftOnAllTrack)
_currentTrackAlternative &= ~RIDE_TYPE_ALTERNATIVE_TRACK_PIECES;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_BANK_LEFT:
RideConstructionInvalidateCurrentTrack();
if (!_currentlyShowingBrakeOrBoosterSpeed)
{
_currentTrackRollEnd = TrackRoll::Left;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
}
break;
case WIDX_BANK_STRAIGHT:
RideConstructionInvalidateCurrentTrack();
if (!_currentlyShowingBrakeOrBoosterSpeed)
{
_currentTrackRollEnd = TrackRoll::None;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
}
else
{
uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2;
uint8_t maxBrakesSpeed = 30;
uint8_t brakesSpeed = *brakesSpeedPtr + 2;
if (brakesSpeed <= maxBrakesSpeed)
{
if (_rideConstructionState == RideConstructionState::Selected)
{
SetBrakeSpeed(brakesSpeed);
}
else
{
*brakesSpeedPtr = brakesSpeed;
WindowRideConstructionUpdateActiveElements();
}
}
}
break;
case WIDX_BANK_RIGHT:
RideConstructionInvalidateCurrentTrack();
if (!_currentlyShowingBrakeOrBoosterSpeed)
{
_currentTrackRollEnd = TrackRoll::Right;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
}
else
{
uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2;
uint8_t brakesSpeed = *brakesSpeedPtr - 2;
if (brakesSpeed >= 2)
{
if (_rideConstructionState == RideConstructionState::Selected)
{
SetBrakeSpeed(brakesSpeed);
}
else
{
*brakesSpeedPtr = brakesSpeed;
WindowRideConstructionUpdateActiveElements();
}
}
}
break;
case WIDX_SPECIAL_TRACK_DROPDOWN:
ShowSpecialTrackDropdown(&widgets[widgetIndex]);
break;
case WIDX_U_TRACK:
RideConstructionInvalidateCurrentTrack();
_currentTrackAlternative &= ~RIDE_TYPE_ALTERNATIVE_TRACK_PIECES;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_O_TRACK:
RideConstructionInvalidateCurrentTrack();
_currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_PIECES;
if (!GetGameState().Cheats.EnableChainLiftOnAllTrack)
_currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
break;
case WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP:
if (_currentSeatRotationAngle < 15)
{
if (_rideConstructionState == RideConstructionState::Selected)
{
RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle + 1);
}
else
{
_currentSeatRotationAngle++;
WindowRideConstructionUpdateActiveElements();
}
}
break;
case WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN:
if (_currentSeatRotationAngle > 0)
{
if (_rideConstructionState == RideConstructionState::Selected)
{
RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle - 1);
}
else
{
_currentSeatRotationAngle--;
WindowRideConstructionUpdateActiveElements();
}
}
break;
}
2018-06-22 23:21:44 +02:00
}
void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override
{
if (widgetIndex != WIDX_SPECIAL_TRACK_DROPDOWN)
return;
if (selectedIndex == -1)
return;
RideConstructionInvalidateCurrentTrack();
_currentTrackPrice = kMoney64Undefined;
track_type_t trackPiece = _specialElementDropdownState.Elements[selectedIndex].TrackType;
switch (trackPiece)
{
case TrackElemType::EndStation:
case TrackElemType::SBendLeft:
case TrackElemType::SBendRight:
_currentTrackPitchEnd = TrackPitch::None;
break;
case TrackElemType::LeftVerticalLoop:
case TrackElemType::RightVerticalLoop:
_currentTrackRollEnd = TrackRoll::None;
_currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
break;
case TrackElemType::BlockBrakes:
case TrackElemType::DiagBlockBrakes:
_currentBrakeSpeed2 = kRCT2DefaultBlockBrakeSpeed;
}
_currentTrackCurve = trackPiece | RideConstructionSpecialPieceSelected;
WindowRideConstructionUpdateActiveElements();
}
void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
2018-06-22 23:21:44 +02:00
{
switch (widgetIndex)
{
case WIDX_CONSTRUCT:
RideConstructionToolupdateConstruct(screenCoords);
break;
case WIDX_ENTRANCE:
case WIDX_EXIT:
RideConstructionToolupdateEntranceExit(screenCoords);
break;
}
2018-06-22 23:21:44 +02:00
}
void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
2018-06-22 23:21:44 +02:00
{
switch (widgetIndex)
{
case WIDX_CONSTRUCT:
RideConstructionTooldownConstruct(screenCoords);
break;
case WIDX_ENTRANCE:
case WIDX_EXIT:
ToolDownEntranceExit(screenCoords);
break;
}
}
void OnPrepareDraw() override
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
StringId stringId = STR_RIDE_CONSTRUCTION_SPECIAL;
if (_currentTrackCurve & RideConstructionSpecialPieceSelected)
{
const auto& rtd = currentRide->GetRideTypeDescriptor();
const auto& ted = GetTrackElementDescriptor(_currentTrackCurve & ~RideConstructionSpecialPieceSelected);
stringId = ted.Description;
if (stringId == STR_RAPIDS && rtd.Category != RIDE_CATEGORY_WATER)
{
stringId = STR_LOG_BUMPS;
}
}
auto ft = Formatter::Common();
ft.Add<uint16_t>(stringId);
if (_currentlyShowingBrakeOrBoosterSpeed)
{
uint16_t brakeSpeed2 = ((_currentBrakeSpeed2 * 9) >> 2) & 0xFFFF;
if (TrackTypeIsBooster(_selectedTrackType)
|| TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected))
{
brakeSpeed2 = GetBoosterSpeed(currentRide->type, brakeSpeed2);
}
ft.Add<uint16_t>(brakeSpeed2);
}
else
{
ft.Increment(2);
}
2015-06-27 16:17:54 +02:00
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].text = RideConstructionSeatAngleRotationStrings
[_currentSeatRotationAngle];
// Simulate button
auto& simulateWidget = widgets[WIDX_SIMULATE];
simulateWidget.type = WindowWidgetType::Empty;
if (currentRide->SupportsStatus(RideStatus::Simulating))
{
simulateWidget.type = WindowWidgetType::FlatBtn;
if (currentRide->status == RideStatus::Simulating)
{
pressed_widgets |= (1uLL << WIDX_SIMULATE);
}
else
{
pressed_widgets &= ~(1uLL << WIDX_SIMULATE);
}
}
// Set window title arguments
currentRide->FormatNameTo(ft);
}
void OnDraw(DrawPixelInfo& dpi) override
2018-06-22 23:21:44 +02:00
{
DrawPixelInfo clipdpi;
Widget* widget;
int32_t widgetWidth, widgetHeight;
DrawWidgets(dpi);
widget = &widgets[WIDX_CONSTRUCT];
if (widget->type == WindowWidgetType::Empty)
return;
RideId rideIndex;
int32_t trackType, trackDirection, liftHillAndInvertedState;
if (WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndInvertedState, nullptr, nullptr))
return;
// Draw track piece
auto screenCoords = ScreenCoordsXY{ windowPos.x + widget->left + 1, windowPos.y + widget->top + 1 };
widgetWidth = widget->width() - 1;
widgetHeight = widget->height() - 1;
if (ClipDrawPixelInfo(clipdpi, dpi, screenCoords, widgetWidth, widgetHeight))
{
DrawTrackPiece(
clipdpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, widgetWidth, widgetHeight);
}
// Draw cost
screenCoords = { windowPos.x + widget->midX(), windowPos.y + widget->bottom - 23 };
if (_rideConstructionState != RideConstructionState::Place)
DrawTextBasic(dpi, screenCoords, STR_BUILD_THIS, {}, { TextAlignment::CENTRE });
screenCoords.y += 11;
if (_currentTrackPrice != kMoney64Undefined && !(GetGameState().Park.Flags & PARK_FLAGS_NO_MONEY))
{
auto ft = Formatter();
ft.Add<money64>(_currentTrackPrice);
DrawTextBasic(dpi, screenCoords, STR_COST_LABEL, ft, { TextAlignment::CENTRE });
}
}
void UpdateWidgets()
2018-06-22 23:21:44 +02:00
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
const auto& rtd = GetRideTypeDescriptor(currentRide->type);
auto trackDrawerDescriptor = getCurrentTrackDrawerDescriptor(rtd);
hold_down_widgets = 0;
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY) || !currentRide->HasStation())
{
widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Empty;
widgets[WIDX_ENTRANCE].type = WindowWidgetType::Empty;
widgets[WIDX_EXIT].type = WindowWidgetType::Empty;
}
else
{
widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Groupbox;
widgets[WIDX_ENTRANCE].type = WindowWidgetType::Button;
widgets[WIDX_EXIT].type = WindowWidgetType::Button;
}
if (_specialElementDropdownState.HasActiveElements)
{
widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Button;
}
else
{
widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Empty;
}
if (IsTrackEnabled(TRACK_STRAIGHT))
{
widgets[WIDX_STRAIGHT].type = WindowWidgetType::FlatBtn;
}
else
{
widgets[WIDX_STRAIGHT].type = WindowWidgetType::Empty;
}
if (IsTrackEnabled(TRACK_CURVE_LARGE))
{
widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::FlatBtn;
widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::FlatBtn;
}
else
{
widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::Empty;
widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::Empty;
}
widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::Empty;
widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::Empty;
widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::Empty;
widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::Empty;
widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty;
widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty;
if (IsTrackEnabled(TRACK_CURVE_VERTICAL))
{
widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn;
widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_CURVE))
{
widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::FlatBtn;
widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_CURVE_SMALL))
{
widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn;
widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_CURVE_VERY_SMALL))
{
widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn;
widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn;
}
widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::Empty;
widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::Empty;
widgets[WIDX_LEVEL].type = WindowWidgetType::Empty;
widgets[WIDX_SLOPE_UP].type = WindowWidgetType::Empty;
widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::Empty;
widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP);
widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP;
widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP);
widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP;
if (trackDrawerDescriptor.SupportsTrackPiece(TRACK_REVERSE_FREEFALL))
{
widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn;
widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_SLOPE) || IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN) || IsTrackEnabled(TRACK_SLOPE_STEEP_UP))
{
widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_SLOPE))
{
widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::FlatBtn;
widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn;
}
if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF))
&& _currentTrackRollEnd != TrackRoll::None && _currentTrackPitchEnd == TrackPitch::None)
{
if (_currentTrackCurve >= EnumValue(TrackCurve::Left)
&& _currentTrackCurve <= EnumValue(TrackCurve::RightSmall))
{
// Enable helix
widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::FlatBtn;
if (currentRide->type != RIDE_TYPE_SPLASH_BOATS && currentRide->type != RIDE_TYPE_RIVER_RAFTS)
widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn;
}
}
if (IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN))
{
widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::FlatBtn;
}
if (IsTrackEnabled(TRACK_SLOPE_STEEP_UP))
{
widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn;
}
if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT)
&& (_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60)
&& !GetGameState().Cheats.EnableAllDrawableTrackPieces)
2018-06-22 23:21:44 +02:00
{
_currentTrackLiftHill |= CONSTRUCTION_LIFT_HILL_SELECTED;
2018-06-22 23:21:44 +02:00
}
if ((IsTrackEnabled(TRACK_LIFT_HILL) && (_currentTrackCurve & RideConstructionSpecialPieceSelected) == 0)
|| (GetGameState().Cheats.EnableChainLiftOnAllTrack
&& currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)))
{
widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::FlatBtn;
}
else
{
widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::Empty;
}
int32_t x = 45;
for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i <= WIDX_SLOPE_UP_STEEP; i++)
{
widgets[i].left = x;
widgets[i].right = x + 23;
x += 24;
}
widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP);
widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP;
widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP);
widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP;
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL))
{
if (_previousTrackPitchEnd == TrackPitch::Up60 || _previousTrackPitchEnd == TrackPitch::Up90)
{
int32_t originalSlopeUpSteepLeft = widgets[WIDX_SLOPE_UP_STEEP].left;
int32_t originalSlopeUpSteepRight = widgets[WIDX_SLOPE_UP_STEEP].right;
for (int32_t i = WIDX_SLOPE_UP_STEEP; i > WIDX_SLOPE_DOWN_STEEP; i--)
{
widgets[i].left = widgets[i - 1].left;
widgets[i].right = widgets[i - 1].right;
}
widgets[WIDX_SLOPE_DOWN_STEEP].left = originalSlopeUpSteepLeft;
widgets[WIDX_SLOPE_DOWN_STEEP].right = originalSlopeUpSteepRight;
widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_RISE);
widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_RISE_TIP;
}
else if (_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Down90)
{
int32_t originalSlopeDownSteepLeft = widgets[WIDX_SLOPE_DOWN_STEEP].left;
int32_t originalSlopeDownSteepRight = widgets[WIDX_SLOPE_DOWN_STEEP].right;
for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i < WIDX_SLOPE_UP_STEEP; i++)
{
widgets[i].left = widgets[i + 1].left;
widgets[i].right = widgets[i + 1].right;
}
widgets[WIDX_SLOPE_UP_STEEP].left = originalSlopeDownSteepLeft;
widgets[WIDX_SLOPE_UP_STEEP].right = originalSlopeDownSteepRight;
widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_DROP);
widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_DROP_TIP;
}
}
if ((IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER))
&& _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None
&& (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right)))
{
widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN);
widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP;
widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP);
widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP;
int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left;
widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left;
widgets[WIDX_SLOPE_DOWN].left = tmp;
tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right;
widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right;
widgets[WIDX_SLOPE_DOWN].right = tmp;
tmp = widgets[WIDX_SLOPE_UP_STEEP].left;
widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left;
widgets[WIDX_SLOPE_UP].left = tmp;
tmp = widgets[WIDX_SLOPE_UP_STEEP].right;
widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right;
widgets[WIDX_SLOPE_UP].right = tmp;
}
if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)
|| IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF))
&& (_currentTrackCurve >= EnumValue(TrackCurve::Left)
&& _currentTrackCurve <= EnumValue(TrackCurve::RightSmall))
&& _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd != TrackRoll::None)
{
widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN);
widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP;
widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP);
widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP;
int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left;
widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left;
widgets[WIDX_SLOPE_DOWN].left = tmp;
tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right;
widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right;
widgets[WIDX_SLOPE_DOWN].right = tmp;
tmp = widgets[WIDX_SLOPE_UP_STEEP].left;
widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left;
widgets[WIDX_SLOPE_UP].left = tmp;
tmp = widgets[WIDX_SLOPE_UP_STEEP].right;
widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right;
widgets[WIDX_SLOPE_UP].right = tmp;
}
widgets[WIDX_BANKING_GROUPBOX].image = ImageId(STR_RIDE_CONSTRUCTION_ROLL_BANKING);
widgets[WIDX_BANK_LEFT].image = ImageId(SPR_RIDE_CONSTRUCTION_LEFT_BANK);
widgets[WIDX_BANK_LEFT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_LEFT_CURVE_TIP;
widgets[WIDX_BANK_LEFT].left = 69;
widgets[WIDX_BANK_LEFT].right = 92;
widgets[WIDX_BANK_LEFT].top = 132;
widgets[WIDX_BANK_LEFT].bottom = 155;
widgets[WIDX_BANK_STRAIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_NO_BANK);
widgets[WIDX_BANK_STRAIGHT].tooltip = STR_RIDE_CONSTRUCTION_NO_ROLL_TIP;
widgets[WIDX_BANK_STRAIGHT].left = 93;
widgets[WIDX_BANK_STRAIGHT].right = 116;
widgets[WIDX_BANK_STRAIGHT].top = 132;
widgets[WIDX_BANK_STRAIGHT].bottom = 155;
widgets[WIDX_BANK_RIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_BANK);
widgets[WIDX_BANK_RIGHT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_RIGHT_CURVE_TIP;
widgets[WIDX_BANK_RIGHT].left = 117;
widgets[WIDX_BANK_RIGHT].right = 140;
widgets[WIDX_BANK_RIGHT].top = 132;
widgets[WIDX_BANK_RIGHT].bottom = 155;
widgets[WIDX_BANK_LEFT].type = WindowWidgetType::Empty;
widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::Empty;
widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::Empty;
widgets[WIDX_U_TRACK].type = WindowWidgetType::Empty;
widgets[WIDX_O_TRACK].type = WindowWidgetType::Empty;
bool trackHasSpeedSetting = TrackTypeHasSpeedSetting(_selectedTrackType)
|| TrackTypeHasSpeedSetting(_currentTrackCurve & ~RideConstructionSpecialPieceSelected);
bool boosterTrackSelected = TrackTypeIsBooster(_selectedTrackType)
|| TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected);
// Only necessary because TD6 writes speed and seat rotation to the same bits. Remove for new track design format.
bool trackHasSpeedAndSeatRotation = _selectedTrackType == TrackElemType::BlockBrakes
|| _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::BlockBrakes)
|| _selectedTrackType > TrackElemType::HighestAlias
|| _currentTrackCurve > (RideConstructionSpecialPieceSelected | TrackElemType::HighestAlias);
bool rideHasSeatRotation = rtd.HasFlag(RIDE_TYPE_FLAG_HAS_SEAT_ROTATION);
if (!trackHasSpeedSetting)
{
if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING))
{
widgets[WIDX_BANK_LEFT].type = WindowWidgetType::FlatBtn;
widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::FlatBtn;
widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::FlatBtn;
}
if (trackDrawerDescriptor.HasCoveredPieces())
{
widgets[WIDX_BANKING_GROUPBOX].text = STR_RIDE_CONSTRUCTION_TRACK_STYLE;
widgets[WIDX_U_TRACK].type = WindowWidgetType::FlatBtn;
widgets[WIDX_O_TRACK].type = WindowWidgetType::FlatBtn;
if (rtd.Category == RIDE_CATEGORY_WATER)
{
widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_U_SHAPED_TRACK);
widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK);
widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_U_SHAPED_OPEN_TRACK_TIP;
widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP;
}
else
{
widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_RC_TRACK);
widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_WATER_CHANNEL);
widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_STANDARD_RC_TRACK_TIP;
widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_WATER_CHANNEL_TIP;
}
// TODO: Read these from the TrackDrawerEntry
if (currentRide->type == RIDE_TYPE_WATER_COASTER)
{
const bool hasCoveredVersion = (_currentTrackCurve < EnumValue(TrackCurve::LeftSmall)
|| _currentTrackCurve
== (RideConstructionSpecialPieceSelected | TrackElemType::SBendLeft)
|| _currentTrackCurve
== (RideConstructionSpecialPieceSelected
| TrackElemType::SBendRight))
&& _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None;
if (!hasCoveredVersion)
{
widgets[WIDX_U_TRACK].type = WindowWidgetType::Empty;
widgets[WIDX_O_TRACK].type = WindowWidgetType::Empty;
}
}
}
}
else
{
if (!boosterTrackSelected)
{
widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED;
widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP;
}
else
{
widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED;
widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP;
}
_currentlyShowingBrakeOrBoosterSpeed = true;
widgets[WIDX_SPEED_SETTING_SPINNER].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_VELOCITY;
2023-07-03 00:37:04 +02:00
widgets[WIDX_SPEED_SETTING_SPINNER].type = WindowWidgetType::Spinner;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].type = WindowWidgetType::Button;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].text = STR_NUMERIC_UP;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].type = WindowWidgetType::Button;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].text = STR_NUMERIC_DOWN;
2023-07-03 00:37:04 +02:00
ResizeSpinner(WIDX_SPEED_SETTING_SPINNER, { 12, 138 }, { 85, SPINNER_HEIGHT });
hold_down_widgets |= (1uLL << WIDX_SPEED_SETTING_SPINNER_UP) | (1uLL << WIDX_SPEED_SETTING_SPINNER_DOWN);
}
static constexpr int16_t bankingGroupboxRightNoSeatRotation = GW;
static constexpr int16_t bankingGroupboxRightWithSeatRotation = 114;
widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightNoSeatRotation;
widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Empty;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Empty;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Empty;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Empty;
// Simplify this condition to "rideHasSeatRotation" for new track design format
if ((rideHasSeatRotation && !trackHasSpeedSetting)
|| (rideHasSeatRotation && trackHasSpeedSetting && trackHasSpeedAndSeatRotation))
{
widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Groupbox;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Spinner;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Button;
widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Button;
widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightWithSeatRotation;
// squishes the track speed spinner slightly to make room for the seat rotation widgets
if (trackHasSpeedSetting)
{
widgets[WIDX_SPEED_SETTING_SPINNER].left -= 4;
widgets[WIDX_SPEED_SETTING_SPINNER].right -= 8;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].right -= 8;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].right -= 8;
widgets[WIDX_SPEED_SETTING_SPINNER_UP].left -= 8;
widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].left -= 8;
}
// moves banking buttons to the left to make room for the seat rotation widgets
else if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING))
{
for (int32_t i = WIDX_BANK_LEFT; i <= WIDX_BANK_RIGHT; i++)
{
widgets[i].left -= 36;
widgets[i].right -= 36;
}
}
}
uint64_t pressedWidgets = pressed_widgets
& ((1uLL << WIDX_BACKGROUND) | (1uLL << WIDX_TITLE) | (1uLL << WIDX_CLOSE) | (1uLL << WIDX_DIRECTION_GROUPBOX)
| (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_CONSTRUCT)
| (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION) | (1uLL << WIDX_NEXT_SECTION)
| (1uLL << WIDX_ENTRANCE_EXIT_GROUPBOX) | (1uLL << WIDX_ENTRANCE) | (1uLL << WIDX_EXIT));
widgets[WIDX_CONSTRUCT].type = WindowWidgetType::Empty;
widgets[WIDX_DEMOLISH].type = WindowWidgetType::FlatBtn;
widgets[WIDX_ROTATE].type = WindowWidgetType::Empty;
if (rtd.HasFlag(RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS))
{
widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty;
widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty;
}
else
{
widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::FlatBtn;
widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::FlatBtn;
}
switch (_rideConstructionState)
{
case RideConstructionState::Front:
widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn;
widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty;
break;
case RideConstructionState::Back:
widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn;
widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty;
break;
case RideConstructionState::Place:
widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn;
widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty;
widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty;
widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty;
widgets[WIDX_ROTATE].type = WindowWidgetType::FlatBtn;
break;
case RideConstructionState::EntranceExit:
widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty;
widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty;
widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty;
break;
default:
pressed_widgets = pressedWidgets;
Invalidate();
return;
}
WidgetIndex widgetIndex;
switch (_currentTrackCurve)
{
case EnumValue(TrackCurve::None):
widgetIndex = WIDX_STRAIGHT;
break;
case EnumValue(TrackCurve::Left):
widgetIndex = WIDX_LEFT_CURVE;
break;
case EnumValue(TrackCurve::Right):
widgetIndex = WIDX_RIGHT_CURVE;
break;
case EnumValue(TrackCurve::LeftSmall):
widgetIndex = WIDX_LEFT_CURVE_SMALL;
break;
case EnumValue(TrackCurve::RightSmall):
widgetIndex = WIDX_RIGHT_CURVE_SMALL;
break;
case EnumValue(TrackCurve::LeftVerySmall):
widgetIndex = WIDX_LEFT_CURVE_VERY_SMALL;
break;
case EnumValue(TrackCurve::RightVerySmall):
widgetIndex = WIDX_RIGHT_CURVE_VERY_SMALL;
break;
case EnumValue(TrackCurve::LeftLarge):
widgetIndex = WIDX_LEFT_CURVE_LARGE;
break;
case EnumValue(TrackCurve::RightLarge):
widgetIndex = WIDX_RIGHT_CURVE_LARGE;
break;
default:
widgetIndex = WIDX_SPECIAL_TRACK_DROPDOWN;
break;
}
pressedWidgets |= (1uLL << widgetIndex);
switch (_currentTrackPitchEnd)
{
case TrackPitch::Down60:
case TrackPitch::Up90:
widgetIndex = WIDX_SLOPE_DOWN_STEEP;
break;
case TrackPitch::Down25:
widgetIndex = WIDX_SLOPE_DOWN;
break;
case TrackPitch::Up25:
widgetIndex = WIDX_SLOPE_UP;
break;
case TrackPitch::Up60:
case TrackPitch::Down90:
widgetIndex = WIDX_SLOPE_UP_STEEP;
break;
default:
widgetIndex = WIDX_LEVEL;
break;
}
2022-10-17 19:21:18 +02:00
pressedWidgets |= (1uLL << widgetIndex);
if (!_currentlyShowingBrakeOrBoosterSpeed)
{
if (trackDrawerDescriptor.HasCoveredPieces())
{
if (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_PIECES)
{
pressed_widgets |= (1uLL << WIDX_O_TRACK);
}
else
{
pressed_widgets |= (1uLL << WIDX_U_TRACK);
}
}
switch (_currentTrackRollEnd)
{
case TrackRoll::Left:
widgetIndex = WIDX_BANK_LEFT;
break;
case TrackRoll::None:
widgetIndex = WIDX_BANK_STRAIGHT;
break;
default:
widgetIndex = WIDX_BANK_RIGHT;
break;
}
pressedWidgets |= (1uLL << widgetIndex);
}
if (_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)
pressedWidgets |= (1uLL << WIDX_CHAIN_LIFT);
pressed_widgets = pressedWidgets;
Invalidate();
}
void UpdatePossibleRideConfigurations()
2018-06-22 23:21:44 +02:00
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
_specialElementDropdownState = BuildSpecialElementsList(
*currentRide, _currentTrackPieceDirection, _previousTrackPitchEnd, _previousTrackRollEnd,
_rideConstructionState);
_currentlyShowingBrakeOrBoosterSpeed = false;
}
void UpdateMapSelection()
{
int32_t trackType, trackDirection;
CoordsXYZ trackPos{};
MapInvalidateMapSelectionTiles();
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags |= MAP_SELECT_FLAG_GREEN;
switch (_rideConstructionState)
{
case RideConstructionState::State0:
trackDirection = _currentTrackPieceDirection;
trackType = 0;
trackPos = _currentTrackBegin;
break;
case RideConstructionState::Selected:
trackDirection = _currentTrackPieceDirection;
trackType = _currentTrackPieceType;
trackPos = _currentTrackBegin;
break;
case RideConstructionState::EntranceExit:
gMapSelectionTiles.clear();
return;
default:
if (WindowRideConstructionUpdateState(&trackType, &trackDirection, nullptr, nullptr, &trackPos, nullptr))
{
trackDirection = _currentTrackPieceDirection;
trackType = 0;
trackPos = _currentTrackBegin;
}
break;
}
if (GetRide(_currentRideIndex))
{
SelectMapTiles(trackType, trackDirection, trackPos);
MapInvalidateMapSelectionTiles();
}
}
void SelectMapTiles(int32_t trackType, int32_t trackDirection, const CoordsXY& tileCoords)
2018-06-22 23:21:44 +02:00
{
// If the scenery tool is active, we do not display our tiles as it
// will conflict with larger scenery objects selecting tiles
if (SceneryToolIsActive())
{
return;
}
const PreviewTrack* trackBlock;
2015-06-27 16:17:54 +02:00
const auto& ted = GetTrackElementDescriptor(trackType);
trackBlock = ted.Block;
trackDirection &= 3;
gMapSelectionTiles.clear();
while (trackBlock->index != 255)
{
CoordsXY offsets = { trackBlock->x, trackBlock->y };
CoordsXY currentTileCoords = tileCoords + offsets.Rotate(trackDirection);
gMapSelectionTiles.push_back(currentTileCoords);
trackBlock++;
}
}
private:
void Construct()
{
RideId rideIndex;
int32_t trackType, trackDirection, liftHillAndAlternativeState, properties;
CoordsXYZ trackPos{};
_currentTrackPrice = kMoney64Undefined;
_trackPlaceCost = kMoney64Undefined;
_trackPlaceErrorMessage = STR_NONE;
RideConstructionInvalidateCurrentTrack();
if (WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, &properties))
{
WindowRideConstructionUpdateActiveElements();
return;
}
auto currentRide = GetRide(_currentRideIndex);
if (currentRide == nullptr)
{
return;
}
auto trackPlaceAction = TrackPlaceAction(
rideIndex, trackType, currentRide->type, { trackPos, static_cast<uint8_t>(trackDirection) }, properties & 0xFF,
(properties >> 8) & 0x0F, (properties >> 12) & 0x0F, liftHillAndAlternativeState, false);
if (_rideConstructionState == RideConstructionState::Back)
{
trackPlaceAction.SetCallback(RideConstructPlacedBackwardGameActionCallback);
}
else if (_rideConstructionState == RideConstructionState::Front)
{
trackPlaceAction.SetCallback(RideConstructPlacedForwardGameActionCallback);
}
auto res = GameActions::Execute(&trackPlaceAction);
// Used by some functions
if (res.Error != GameActions::Status::Ok)
{
_trackPlaceCost = kMoney64Undefined;
_trackPlaceErrorMessage = std::get<StringId>(res.ErrorMessage);
}
else
{
_trackPlaceCost = res.Cost;
_trackPlaceErrorMessage = STR_NONE;
}
if (res.Error != GameActions::Status::Ok)
{
return;
}
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, trackPos);
2015-06-27 16:17:54 +02:00
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED;
}
const auto resultData = res.GetData<TrackPlaceActionResult>();
if (resultData.GroundFlags & ELEMENT_IS_UNDERGROUND)
{
ViewportSetVisibility(ViewportVisibility::UndergroundViewOn);
}
const bool helixSelected = (_currentTrackCurve & RideConstructionSpecialPieceSelected)
&& TrackTypeIsHelix(_currentTrackCurve & ~RideConstructionSpecialPieceSelected);
if (helixSelected || (_currentTrackPitchEnd != TrackPitch::None))
{
ViewportSetVisibility(ViewportVisibility::TrackHeights);
}
}
void MouseUpDemolish()
{
int32_t direction;
TileElement* tileElement;
CoordsXYE inputElement, outputElement;
TrackBeginEnd trackBeginEnd;
_currentTrackPrice = kMoney64Undefined;
RideConstructionInvalidateCurrentTrack();
// Select the track element that is to be deleted
_rideConstructionState2 = RideConstructionState::Selected;
if (_rideConstructionState == RideConstructionState::Front)
{
if (!RideSelectBackwardsFromFront())
{
WindowRideConstructionUpdateActiveElements();
return;
}
_rideConstructionState2 = RideConstructionState::Front;
}
else if (_rideConstructionState == RideConstructionState::Back)
{
if (!RideSelectForwardsFromBack())
{
WindowRideConstructionUpdateActiveElements();
return;
}
_rideConstructionState2 = RideConstructionState::Back;
}
// Invalidate the selected track element or make sure it's at origin???
direction = _currentTrackPieceDirection;
// The direction is reset by ride_initialise_construction_window(), but we need it to remove flat rides properly.
Direction currentDirection = _currentTrackPieceDirection;
track_type_t type = _currentTrackPieceType;
auto newCoords = GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(direction & 3) }, type, 0, &tileElement, 0);
if (!newCoords.has_value())
{
WindowRideConstructionUpdateActiveElements();
return;
}
2015-06-27 16:17:54 +02:00
// Get the previous track element to go to after the selected track element is deleted
inputElement.x = newCoords->x;
inputElement.y = newCoords->y;
inputElement.element = tileElement;
if (TrackBlockGetPrevious({ *newCoords, tileElement }, &trackBeginEnd))
{
*newCoords = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z };
direction = trackBeginEnd.begin_direction;
type = trackBeginEnd.begin_element->AsTrack()->GetTrackType();
_gotoStartPlacementMode = false;
}
else if (TrackBlockGetNext(&inputElement, &outputElement, &newCoords->z, &direction))
{
newCoords->x = outputElement.x;
newCoords->y = outputElement.y;
direction = outputElement.element->GetDirection();
type = outputElement.element->AsTrack()->GetTrackType();
_gotoStartPlacementMode = false;
}
else
{
direction = _currentTrackPieceDirection;
type = _currentTrackPieceType;
newCoords = GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(direction & 3) }, type, 0, &tileElement, 0);
if (!newCoords.has_value())
{
WindowRideConstructionUpdateActiveElements();
return;
}
const auto& ted = GetTrackElementDescriptor(tileElement->AsTrack()->GetTrackType());
const PreviewTrack* trackBlock = ted.Block;
newCoords->z = (tileElement->GetBaseZ()) - trackBlock->z;
_gotoStartPlacementMode = true;
// When flat rides are deleted, the window should be reset so the currentRide can be placed again.
auto currentRide = GetRide(_currentRideIndex);
const auto& rtd = currentRide->GetRideTypeDescriptor();
if (rtd.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY))
{
RideInitialiseConstructionWindow(*currentRide);
}
}
auto trackRemoveAction = TrackRemoveAction(
_currentTrackPieceType, 0,
{ _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z, currentDirection });
trackRemoveAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
{
WindowRideConstructionUpdateActiveElements();
}
else
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide != nullptr)
{
WindowRideConstructionMouseUpDemolishNextPiece({ *newCoords, static_cast<Direction>(direction) }, type);
}
}
});
GameActions::Execute(&trackRemoveAction);
}
void Rotate()
{
_autoRotatingShop = false;
_currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3;
RideConstructionInvalidateCurrentTrack();
_currentTrackPrice = kMoney64Undefined;
WindowRideConstructionUpdateActiveElements();
}
void EntranceClick()
{
if (ToolSet(*this, WIDX_ENTRANCE, Tool::Crosshair))
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide != nullptr && !RideTryGetOriginElement(*currentRide, nullptr))
{
RideInitialiseConstructionWindow(*currentRide);
}
}
else
2018-06-22 23:21:44 +02:00
{
gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_ENTRANCE;
gRideEntranceExitPlaceRideIndex = _currentRideIndex;
gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0);
InputSetFlag(INPUT_FLAG_6, true);
RideConstructionInvalidateCurrentTrack();
if (_rideConstructionState != RideConstructionState::EntranceExit)
{
gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState;
_rideConstructionState = RideConstructionState::EntranceExit;
}
WindowRideConstructionUpdateActiveElements();
2018-06-22 23:21:44 +02:00
}
}
void ExitClick()
{
if (ToolSet(*this, WIDX_EXIT, Tool::Crosshair))
{
auto currentRide = GetRide(_currentRideIndex);
if (!RideTryGetOriginElement(*currentRide, nullptr))
{
RideInitialiseConstructionWindow(*currentRide);
}
}
else
{
gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_EXIT;
gRideEntranceExitPlaceRideIndex = _currentRideIndex;
gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0);
InputSetFlag(INPUT_FLAG_6, true);
RideConstructionInvalidateCurrentTrack();
if (_rideConstructionState != RideConstructionState::EntranceExit)
{
gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState;
_rideConstructionState = RideConstructionState::EntranceExit;
}
WindowRideConstructionUpdateActiveElements();
}
}
void UpdateLiftHillSelected(TrackPitch slope)
{
_currentTrackPitchEnd = slope;
_currentTrackPrice = kMoney64Undefined;
if (_rideConstructionState == RideConstructionState::Front && !GetGameState().Cheats.EnableChainLiftOnAllTrack)
2018-06-22 23:21:44 +02:00
{
switch (slope)
{
case TrackPitch::None:
case TrackPitch::Up25:
case TrackPitch::Up60:
break;
default:
_currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
break;
}
}
WindowRideConstructionUpdateActiveElements();
}
void SetBrakeSpeed(int32_t brakesSpeed)
{
TileElement* tileElement;
if (GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0,
&tileElement, 0)
!= std::nullopt)
{
auto trackSetBrakeSpeed = TrackSetBrakeSpeedAction(
_currentTrackBegin, tileElement->AsTrack()->GetTrackType(), brakesSpeed);
trackSetBrakeSpeed.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
WindowRideConstructionUpdateActiveElements();
});
GameActions::Execute(&trackSetBrakeSpeed);
return;
}
WindowRideConstructionUpdateActiveElements();
}
void ShowSpecialTrackDropdown(Widget* widget)
{
int32_t defaultIndex = -1;
for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++)
2018-06-22 23:21:44 +02:00
{
track_type_t trackPiece = _specialElementDropdownState.Elements[i].TrackType;
const auto& ted = GetTrackElementDescriptor(trackPiece);
StringId trackPieceStringId = ted.Description;
if (trackPieceStringId == STR_RAPIDS)
{
auto currentRide = GetRide(_currentRideIndex);
if (currentRide != nullptr)
{
const auto& rtd = currentRide->GetRideTypeDescriptor();
if (rtd.Category != RIDE_CATEGORY_WATER)
trackPieceStringId = STR_LOG_BUMPS;
}
}
gDropdownItems[i].Format = trackPieceStringId;
if ((trackPiece | RideConstructionSpecialPieceSelected) == _currentTrackCurve)
{
defaultIndex = static_cast<int32_t>(i);
}
2018-06-22 23:21:44 +02:00
}
WindowDropdownShowTextCustomWidth(
{ windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 0,
_specialElementDropdownState.Elements.size(), widget->width());
for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++)
{
Dropdown::SetDisabled(static_cast<int32_t>(i), _specialElementDropdownState.Elements[i].Disabled);
}
gDropdownDefaultIndex = defaultIndex;
}
void RideSelectedTrackSetSeatRotation(int32_t seatRotation)
{
GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(_currentTrackPieceDirection & 3) }, _currentTrackPieceType,
seatRotation, nullptr, TRACK_ELEMENT_SET_SEAT_ROTATION);
WindowRideConstructionUpdateActiveElements();
}
2015-06-27 16:17:54 +02:00
void ToolDownEntranceExit(const ScreenCoordsXY& screenCoords)
{
RideConstructionInvalidateCurrentTrack();
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords);
if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION)
return;
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
entranceOrExitCoords, DirectionReverse(gRideEntranceExitPlaceDirection), gRideEntranceExitPlaceRideIndex,
gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT);
rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
return;
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
auto currentRide = GetRide(gRideEntranceExitPlaceRideIndex);
if (currentRide != nullptr && RideAreAllPossibleEntrancesAndExitsBuilt(*currentRide).Successful)
{
ToolCancel();
if (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))
{
WindowCloseByClass(WindowClass::RideConstruction);
}
}
else
{
gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1;
WindowInvalidateByClass(WindowClass::RideConstruction);
gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE)
? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE
: WC_RIDE_CONSTRUCTION__WIDX_EXIT;
}
});
auto res = GameActions::Execute(&rideEntranceExitPlaceAction);
}
void DrawTrackPiece(
DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState,
int32_t widgetWidth, int32_t widgetHeight)
{
auto currentRide = GetRide(rideIndex);
if (currentRide == nullptr)
{
return;
}
const auto& ted = GetTrackElementDescriptor(trackType);
const auto* trackBlock = ted.Block;
while ((trackBlock + 1)->index != 0xFF)
trackBlock++;
CoordsXYZ mapCoords{ trackBlock->x, trackBlock->y, trackBlock->z };
if (trackBlock->flags & RCT_PREVIEW_TRACK_FLAG_1)
{
mapCoords.x = 0;
mapCoords.y = 0;
}
auto rotatedMapCoords = mapCoords.Rotate(trackDirection);
// this is actually case 0, but the other cases all jump to it
mapCoords.x = 4112 + (rotatedMapCoords.x / 2);
mapCoords.y = 4112 + (rotatedMapCoords.y / 2);
mapCoords.z = 1024 + mapCoords.z;
auto previewZOffset = ted.Definition.PreviewZOffset;
mapCoords.z -= previewZOffset;
const ScreenCoordsXY rotatedScreenCoords = Translate3DTo2DWithZ(GetCurrentRotation(), mapCoords);
dpi.x += rotatedScreenCoords.x - widgetWidth / 2;
dpi.y += rotatedScreenCoords.y - widgetHeight / 2 - 16;
DrawTrackPieceHelper(dpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, { 4096, 4096 }, 1024);
}
void DrawTrackPieceHelper(
DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState,
const CoordsXY& originCoords, int32_t originZ)
{
TileElement tempSideTrackTileElement{ 0x80, 0x8F, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
TileElement tempTrackTileElement{};
TileElement* backupTileElementArrays[5]{};
PaintSession* session = PaintSessionAlloc(dpi, 0, GetCurrentRotation());
trackDirection &= 3;
auto currentRide = GetRide(rideIndex);
if (currentRide == nullptr)
{
return;
}
auto& gameState = OpenRCT2::GetGameState();
auto preserveMapSize = gameState.MapSize;
gameState.MapSize = { kMaximumMapSizeTechnical, kMaximumMapSizeTechnical };
// Setup non changing parts of the temporary track tile element
tempTrackTileElement.SetType(TileElementType::Track);
tempTrackTileElement.SetDirection(trackDirection);
tempTrackTileElement.AsTrack()->SetHasChain((liftHillAndInvertedState & CONSTRUCTION_LIFT_HILL_SELECTED) != 0);
tempTrackTileElement.SetLastForTile(true);
tempTrackTileElement.AsTrack()->SetTrackType(trackType);
tempTrackTileElement.AsTrack()->SetRideType(currentRide->type);
tempTrackTileElement.AsTrack()->SetHasCableLift(false);
tempTrackTileElement.AsTrack()->SetInverted((liftHillAndInvertedState & CONSTRUCTION_INVERTED_TRACK_SELECTED) != 0);
tempTrackTileElement.AsTrack()->SetColourScheme(RIDE_COLOUR_SCHEME_MAIN);
// Skipping seat rotation, should not be necessary for a temporary piece.
tempTrackTileElement.AsTrack()->SetRideIndex(rideIndex);
const auto& ted = GetTrackElementDescriptor(trackType);
const auto* trackBlock = ted.Block;
const auto* rideEntry = currentRide->GetRideEntry();
auto clearanceHeight = (rideEntry != nullptr) ? rideEntry->Clearance
: currentRide->GetRideTypeDescriptor().Heights.ClearanceHeight;
while (trackBlock->index != 255)
{
auto quarterTile = trackBlock->var_08.Rotate(trackDirection);
CoordsXY offsets = { trackBlock->x, trackBlock->y };
CoordsXY coords = originCoords + offsets.Rotate(trackDirection);
int32_t baseZ = originZ + trackBlock->z;
int32_t clearanceZ = trackBlock->ClearanceZ + clearanceHeight + baseZ + (4 * COORDS_Z_STEP);
auto centreTileCoords = TileCoordsXY{ coords };
auto eastTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_EAST];
auto westTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_WEST];
auto northTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_NORTH];
auto southTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_SOUTH];
// Replace map elements with temporary ones containing track
backupTileElementArrays[0] = MapGetFirstElementAt(centreTileCoords);
backupTileElementArrays[1] = MapGetFirstElementAt(eastTileCoords);
backupTileElementArrays[2] = MapGetFirstElementAt(westTileCoords);
backupTileElementArrays[3] = MapGetFirstElementAt(northTileCoords);
backupTileElementArrays[4] = MapGetFirstElementAt(southTileCoords);
MapSetTileElement(centreTileCoords, &tempTrackTileElement);
MapSetTileElement(eastTileCoords, &tempSideTrackTileElement);
MapSetTileElement(westTileCoords, &tempSideTrackTileElement);
MapSetTileElement(northTileCoords, &tempSideTrackTileElement);
MapSetTileElement(southTileCoords, &tempSideTrackTileElement);
// Set the temporary track element
tempTrackTileElement.SetOccupiedQuadrants(quarterTile.GetBaseQuarterOccupied());
tempTrackTileElement.SetBaseZ(baseZ);
tempTrackTileElement.SetClearanceZ(clearanceZ);
tempTrackTileElement.AsTrack()->SetSequenceIndex(trackBlock->index);
// Draw this map tile
TileElementPaintSetup(*session, coords, true);
// Restore map elements
MapSetTileElement(centreTileCoords, backupTileElementArrays[0]);
MapSetTileElement(eastTileCoords, backupTileElementArrays[1]);
MapSetTileElement(westTileCoords, backupTileElementArrays[2]);
MapSetTileElement(northTileCoords, backupTileElementArrays[3]);
MapSetTileElement(southTileCoords, backupTileElementArrays[4]);
trackBlock++;
}
gameState.MapSize = preserveMapSize;
PaintSessionArrange(*session);
PaintDrawStructs(*session);
PaintSessionFree(session);
}
};
static void WindowRideConstructionUpdateDisabledPieces(ObjectEntryIndex rideType)
{
RideTrackGroup disabledPieces{};
const auto& rtd = GetRideTypeDescriptor(rideType);
if (rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))
2018-06-22 23:21:44 +02:00
{
// Set all pieces as “disabled”. When looping over the ride entries,
// pieces will be re-enabled as soon as a single entry supports it.
disabledPieces.flip();
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
auto& rideEntries = objManager.GetAllRideEntries(rideType);
// If there are no vehicles for this ride type, enable all the track pieces.
if (rideEntries.size() == 0)
{
disabledPieces.reset();
}
for (auto rideEntryIndex : rideEntries)
{
const auto* currentRideEntry = GetRideEntryByIndex(rideEntryIndex);
if (currentRideEntry == nullptr)
continue;
// Non-default vehicle visuals do not use this system, so we have to assume it supports all the track pieces.
if (currentRideEntry->Cars[0].PaintStyle != VEHICLE_VISUAL_DEFAULT || rideType == RIDE_TYPE_CHAIRLIFT
|| (currentRideEntry->Cars[0].flags & CAR_ENTRY_FLAG_SLIDE_SWING))
{
disabledPieces.reset();
break;
}
// Any pieces that this ride entry supports must be taken out of the array.
auto supportedPieces = RideEntryGetSupportedTrackPieces(*currentRideEntry);
disabledPieces &= supportedPieces.flip();
}
}
UpdateDisabledRidePieces(disabledPieces);
}
2015-06-27 16:17:54 +02:00
/**
*
* rct2: 0x006CB481
*/
WindowBase* RideConstructionOpen()
{
RideId rideIndex = _currentRideIndex;
CloseRideWindowForConstruction(rideIndex);
auto currentRide = GetRide(rideIndex);
if (currentRide == nullptr)
{
return nullptr;
}
WindowRideConstructionUpdateDisabledPieces(currentRide->type);
const auto& rtd = currentRide->GetRideTypeDescriptor();
switch (rtd.ConstructionWindowContext)
{
case RideConstructionWindowContext::Maze:
return ContextOpenWindowView(WV_MAZE_CONSTRUCTION);
case RideConstructionWindowContext::Default:
return WindowCreate<RideConstructionWindow>(
WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE);
}
return WindowCreate<RideConstructionWindow>(
WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE);
}
static void CloseConstructWindowOnCompletion(const Ride& ride)
{
if (_rideConstructionState == RideConstructionState::State0)
{
auto w = WindowFindByClass(WindowClass::RideConstruction);
if (w != nullptr)
{
if (RideAreAllPossibleEntrancesAndExitsBuilt(ride).Successful)
{
WindowClose(*w);
}
else
{
w->OnMouseUp(WIDX_ENTRANCE);
}
}
}
}
static void WindowRideConstructionDoEntranceExitCheck()
2018-06-22 23:21:44 +02:00
{
auto w = WindowFindByClass(WindowClass::RideConstruction);
auto ride = GetRide(_currentRideIndex);
if (w == nullptr || ride == nullptr)
{
return;
}
if (_rideConstructionState == RideConstructionState::State0)
{
w = WindowFindByClass(WindowClass::RideConstruction);
if (w != nullptr)
{
if (!RideAreAllPossibleEntrancesAndExitsBuilt(*ride).Successful)
{
w->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE);
}
}
}
}
static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result)
2018-06-22 23:21:44 +02:00
{
if (result->Error != GameActions::Status::Ok)
{
WindowRideConstructionUpdateActiveElements();
return;
}
auto ride = GetRide(_currentRideIndex);
if (ride != nullptr)
{
int32_t trackDirection = _currentTrackPieceDirection;
auto trackPos = _currentTrackBegin;
if (!(trackDirection & 4))
{
trackPos -= CoordsDirectionDelta[trackDirection];
}
CoordsXYE next_track;
if (TrackBlockGetNextFromZero(trackPos, *ride, trackDirection, &next_track, &trackPos.z, &trackDirection, false))
{
_currentTrackBegin.x = next_track.x;
_currentTrackBegin.y = next_track.y;
_currentTrackBegin.z = trackPos.z;
_currentTrackPieceDirection = next_track.element->GetDirection();
_currentTrackPieceType = next_track.element->AsTrack()->GetTrackType();
_currentTrackSelectionFlags = 0;
_rideConstructionState = RideConstructionState::Selected;
_rideConstructionNextArrowPulse = 0;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
RideSelectNextSection();
}
else
{
_rideConstructionState = RideConstructionState::State0;
}
WindowRideConstructionDoEntranceExitCheck();
WindowRideConstructionUpdateActiveElements();
}
WindowCloseByClass(WindowClass::Error);
if (ride != nullptr)
CloseConstructWindowOnCompletion(*ride);
2022-08-13 18:36:33 +02:00
}
static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result)
2022-08-13 18:36:33 +02:00
{
if (result->Error != GameActions::Status::Ok)
{
WindowRideConstructionUpdateActiveElements();
return;
}
auto ride = GetRide(_currentRideIndex);
if (ride != nullptr)
2022-08-13 18:36:33 +02:00
{
auto trackDirection = DirectionReverse(_currentTrackPieceDirection);
auto trackPos = _currentTrackBegin;
if (!(trackDirection & 4))
2022-08-13 18:36:33 +02:00
{
trackPos += CoordsDirectionDelta[trackDirection];
}
TrackBeginEnd trackBeginEnd;
if (TrackBlockGetPreviousFromZero(trackPos, *ride, trackDirection, &trackBeginEnd))
{
_currentTrackBegin.x = trackBeginEnd.begin_x;
_currentTrackBegin.y = trackBeginEnd.begin_y;
_currentTrackBegin.z = trackBeginEnd.begin_z;
_currentTrackPieceDirection = trackBeginEnd.begin_direction;
_currentTrackPieceType = trackBeginEnd.begin_element->AsTrack()->GetTrackType();
_currentTrackSelectionFlags = 0;
_rideConstructionState = RideConstructionState::Selected;
_rideConstructionNextArrowPulse = 0;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
RideSelectPreviousSection();
}
else
{
_rideConstructionState = RideConstructionState::State0;
2022-08-13 18:36:33 +02:00
}
WindowRideConstructionUpdateActiveElements();
2022-08-13 18:36:33 +02:00
}
WindowCloseByClass(WindowClass::Error);
if (ride != nullptr)
CloseConstructWindowOnCompletion(*ride);
}
/**
*
* rct2: 0x006CC538
*/
static std::optional<CoordsXY> RideGetPlacePositionFromScreenPosition(ScreenCoordsXY screenCoords)
2018-06-22 23:21:44 +02:00
{
CoordsXY mapCoords;
if (!_trackPlaceCtrlState)
{
if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z)
{
auto info = GetMapCoordinatesFromPos(screenCoords, 0xFCCA);
if (info.SpriteType != ViewportInteractionItem::None)
{
_trackPlaceCtrlZ = info.Element->GetBaseZ();
_trackPlaceCtrlState = true;
}
}
}
else
2018-06-22 23:21:44 +02:00
{
if (!(gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z))
{
_trackPlaceCtrlState = false;
}
}
if (!_trackPlaceShiftState)
2018-06-22 23:21:44 +02:00
{
if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z)
{
_trackPlaceShiftState = true;
_trackPlaceShiftStart = screenCoords;
_trackPlaceShiftZ = 0;
}
2018-06-22 23:21:44 +02:00
}
else
2018-06-22 23:21:44 +02:00
{
if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z)
{
uint16_t maxHeight = ZoomLevel::max().ApplyTo(
std::numeric_limits<decltype(TileElement::BaseHeight)>::max() - 32);
_trackPlaceShiftZ = _trackPlaceShiftStart.y - screenCoords.y + 4;
// Scale delta by zoom to match mouse position.
auto* mainWnd = WindowGetMain();
if (mainWnd != nullptr && mainWnd->viewport != nullptr)
{
_trackPlaceShiftZ = mainWnd->viewport->zoom.ApplyTo(_trackPlaceShiftZ);
}
_trackPlaceShiftZ = Floor2(_trackPlaceShiftZ, 8);
// Clamp to maximum possible value of BaseHeight can offer.
_trackPlaceShiftZ = std::min<int16_t>(_trackPlaceShiftZ, maxHeight);
screenCoords = _trackPlaceShiftStart;
}
else
2018-06-22 23:21:44 +02:00
{
_trackPlaceShiftState = false;
}
}
if (!_trackPlaceCtrlState)
2018-06-22 23:21:44 +02:00
{
mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords);
if (mapCoords.IsNull())
return std::nullopt;
_trackPlaceZ = 0;
if (_trackPlaceShiftState)
2018-06-22 23:21:44 +02:00
{
auto surfaceElement = MapGetSurfaceElementAt(mapCoords);
if (surfaceElement == nullptr)
return std::nullopt;
auto mapZ = Floor2(surfaceElement->GetBaseZ(), 16);
mapZ += _trackPlaceShiftZ;
mapZ = std::max<int16_t>(mapZ, 16);
_trackPlaceZ = mapZ;
}
}
else
2018-06-22 23:21:44 +02:00
{
auto mapZ = _trackPlaceCtrlZ;
auto mapXYCoords = ScreenGetMapXYWithZ(screenCoords, mapZ);
if (mapXYCoords.has_value())
{
mapCoords = mapXYCoords.value();
}
else
{
return std::nullopt;
}
if (_trackPlaceShiftState != 0)
{
mapZ += _trackPlaceShiftZ;
}
_trackPlaceZ = std::max<int32_t>(mapZ, 16);
}
if (mapCoords.x == LOCATION_NULL)
return std::nullopt;
return mapCoords.ToTileStart();
}
/**
*
* rct2: 0x006C84CE
*/
void WindowRideConstructionUpdateActiveElementsImpl()
2018-06-22 23:21:44 +02:00
{
WindowRideConstructionUpdateEnabledTrackPieces();
if (auto currentRide = GetRide(_currentRideIndex);
!currentRide || currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_MAZE))
{
return;
}
auto window = static_cast<RideConstructionWindow*>(WindowFindByClass(WindowClass::RideConstruction));
if (!window)
{
return;
}
2019-03-28 19:29:51 +01:00
window->UpdateMapSelection();
_selectedTrackType = TrackElemType::None;
if (_rideConstructionState == RideConstructionState::Selected)
{
TileElement* tileElement;
if (GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0,
&tileElement, 0)
!= std::nullopt)
{
_selectedTrackType = tileElement->AsTrack()->GetTrackType();
if (TrackTypeHasSpeedSetting(tileElement->AsTrack()->GetTrackType()))
_currentBrakeSpeed2 = tileElement->AsTrack()->GetBrakeBoosterSpeed();
_currentSeatRotationAngle = tileElement->AsTrack()->GetSeatRotation();
}
}
window->UpdatePossibleRideConfigurations();
window->UpdateWidgets();
}
/**
*
* rct2: 0x006C6A77
*/
void WindowRideConstructionUpdateEnabledTrackPieces()
{
auto ride = GetRide(_currentRideIndex);
if (ride == nullptr)
return;
auto rideEntry = ride->GetRideEntry();
if (rideEntry == nullptr)
return;
2015-06-27 16:17:54 +02:00
auto trackDrawerDescriptor = getCurrentTrackDrawerDescriptor(ride->GetRideTypeDescriptor());
UpdateEnabledRidePieces(trackDrawerDescriptor);
}
/**
*
* rct2: 0x006C94D8
*/
void UpdateGhostTrackAndArrow()
{
RideId rideIndex;
int32_t direction, type, liftHillAndAlternativeState;
CoordsXYZ trackPos{};
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED)
{
return;
}
// Recheck if area is fine for new track.
// Set by footpath placement
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_RECHECK)
{
RideConstructionInvalidateCurrentTrack();
_currentTrackSelectionFlags &= ~TRACK_SELECTION_FLAG_RECHECK;
}
switch (_rideConstructionState)
2018-06-22 23:21:44 +02:00
{
case RideConstructionState::Front:
case RideConstructionState::Back:
{
// place ghost piece
if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK))
{
if (WindowRideConstructionUpdateState(
&type, &direction, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr))
{
RideConstructionRemoveGhosts();
}
else
{
_currentTrackPrice = PlaceProvisionalTrackPiece(
rideIndex, type, direction, liftHillAndAlternativeState, trackPos);
WindowRideConstructionUpdateActiveElements();
}
}
// update flashing arrow
auto curTime = Platform::GetTicks();
if (_rideConstructionNextArrowPulse >= curTime)
break;
_rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION;
_currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW;
trackPos = _currentTrackBegin;
direction = _currentTrackPieceDirection;
type = _currentTrackPieceType;
// diagonal pieces trigger this
if (direction >= 4)
direction += 4;
if (_rideConstructionState == RideConstructionState::Back)
direction = DirectionReverse(direction);
gMapSelectArrowPosition = trackPos;
gMapSelectArrowDirection = direction;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW)
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW;
MapInvalidateTileFull(trackPos);
break;
}
case RideConstructionState::Selected:
{
auto curTime = Platform::GetTicks();
if (_rideConstructionNextArrowPulse >= curTime)
break;
_rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION;
_currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW;
direction = _currentTrackPieceDirection & 3;
type = _currentTrackPieceType;
uint16_t flags = _currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW ? TRACK_ELEMENT_SET_HIGHLIGHT_TRUE
: TRACK_ELEMENT_SET_HIGHLIGHT_FALSE;
auto newCoords = GetTrackElementOriginAndApplyChanges(
{ _currentTrackBegin, static_cast<Direction>(direction) }, type, 0, nullptr, flags);
if (!newCoords.has_value())
{
RideConstructionRemoveGhosts();
_rideConstructionState = RideConstructionState::State0;
}
break;
}
case RideConstructionState::MazeBuild:
case RideConstructionState::MazeMove:
case RideConstructionState::MazeFill:
{
auto curTime = Platform::GetTicks();
if (_rideConstructionNextArrowPulse >= curTime)
break;
_rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION;
2015-06-27 16:17:54 +02:00
_currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW;
trackPos = CoordsXYZ{ _currentTrackBegin.x & 0xFFE0, _currentTrackBegin.y & 0xFFE0, _currentTrackBegin.z + 15 };
gMapSelectArrowPosition = trackPos;
gMapSelectArrowDirection = 4;
if (((_currentTrackBegin.x & 0x1F) | (_currentTrackBegin.y & 0x1F)) != 0)
{
gMapSelectArrowDirection = 6;
if (((_currentTrackBegin.x & 0x1F) & (_currentTrackBegin.y & 0x1F)) == 0)
{
gMapSelectArrowDirection = 5;
if ((_currentTrackBegin.y & 0x1F) == 0)
gMapSelectArrowDirection = 7;
}
}
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW)
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW;
MapInvalidateTileFull(trackPos);
break;
}
default:
break;
}
}
/**
*
* rct2: 0x006CC6A8
*/
void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords)
2018-06-22 23:21:44 +02:00
{
int32_t z;
const PreviewTrack* trackBlock;
MapInvalidateMapSelectionTiles();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
auto mapCoords = RideGetPlacePositionFromScreenPosition(screenCoords);
if (!mapCoords)
{
RideConstructionInvalidateCurrentTrack();
MapInvalidateMapSelectionTiles();
return;
}
z = _trackPlaceZ;
if (z == 0)
z = MapGetHighestZ(*mapCoords);
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW;
gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN;
gMapSelectArrowPosition = CoordsXYZ{ *mapCoords, z };
gMapSelectArrowDirection = _currentTrackPieceDirection;
gMapSelectionTiles.clear();
gMapSelectionTiles.push_back(*mapCoords);
RideId rideIndex;
int32_t trackType, trackDirection, liftHillAndAlternativeState;
if (WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, nullptr, nullptr))
{
RideConstructionInvalidateCurrentTrack();
MapInvalidateMapSelectionTiles();
return;
}
_currentTrackPieceType = trackType;
auto ride = GetRide(_currentRideIndex);
if (!ride)
{
return;
}
const auto& rtd = ride->GetRideTypeDescriptor();
if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE))
2018-06-22 23:21:44 +02:00
{
auto window = static_cast<RideConstructionWindow*>(WindowFindByClass(WindowClass::RideConstruction));
if (!window)
{
return;
}
// Re-using this other code, very slight difference from original
// - Original code checks for MSB mask instead of 255 on trackPart->var_00
// - Original code checks this first as its already set origin tile, probably just a micro optimisation
window->SelectMapTiles(trackType, trackDirection, *mapCoords);
}
gMapSelectArrowPosition.z = z;
if (_trackPlaceZ == 0)
{
// Raise z above all slopes and water
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT)
2018-06-22 23:21:44 +02:00
{
int32_t highestZ = 0;
for (const auto& selectedTile : gMapSelectionTiles)
2018-06-22 23:21:44 +02:00
{
if (MapIsLocationValid(selectedTile))
{
z = MapGetHighestZ(selectedTile);
if (z > highestZ)
highestZ = z;
}
}
}
// Loc6CC8BF:
// z = MapGetHighestZ(x >> 5, y >> 5);
}
// Loc6CC91B:
const auto& ted = GetTrackElementDescriptor(trackType);
trackBlock = ted.Block;
int32_t bx = 0;
do
{
bx = std::min<int32_t>(bx, trackBlock->z);
trackBlock++;
} while (trackBlock->index != 255);
z -= bx;
gMapSelectArrowPosition.z = z;
_currentTrackBegin.x = mapCoords->x;
_currentTrackBegin.y = mapCoords->y;
_currentTrackBegin.z = z;
if ((_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK) && _currentTrackBegin == _previousTrackPiece)
{
MapInvalidateMapSelectionTiles();
return;
}
_previousTrackPiece = _currentTrackBegin;
// search for appropriate z value for ghost, up to max ride height
int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2);
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE))
{
for (int zAttempts = 0; zAttempts < numAttempts; ++zAttempts)
{
CoordsXYZ trackPos{};
WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr);
_currentTrackPrice = PlaceProvisionalTrackPiece(
rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos);
if (_currentTrackPrice != kMoney64Undefined)
break;
_currentTrackBegin.z -= 8;
if (_currentTrackBegin.z < 0)
break;
_currentTrackBegin.z += 16;
}
auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION);
ContextBroadcastIntent(&intent);
MapInvalidateMapSelectionTiles();
return;
}
2020-01-24 22:23:29 +01:00
for (int zAttempts = 0; zAttempts < numAttempts; ++zAttempts)
2018-06-22 23:21:44 +02:00
{
CoordsXYZ trackPos{};
WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr);
_currentTrackPrice = PlaceProvisionalTrackPiece(
rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos);
mapCoords = trackPos;
z = trackPos.z;
if (_currentTrackPrice != kMoney64Undefined)
break;
_currentTrackBegin.z -= 8;
if (_currentTrackBegin.z < 0)
break;
_currentTrackBegin.z += 16;
}
if (_autoRotatingShop && _rideConstructionState == RideConstructionState::Place
&& ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY))
{
PathElement* pathsByDir[NumOrthogonalDirections];
bool keepOrientation = false;
for (int8_t i = 0; i < NumOrthogonalDirections; i++)
{
const auto testLoc = CoordsXYZ{ *mapCoords + CoordsDirectionDelta[i], z };
if (!MapIsLocationOwned(testLoc))
{
pathsByDir[i] = nullptr;
continue;
}
pathsByDir[i] = MapGetFootpathElement(testLoc);
if (pathsByDir[i] != nullptr && pathsByDir[i]->IsSloped() && pathsByDir[i]->GetSlopeDirection() != i)
{
pathsByDir[i] = nullptr;
}
// Sloped path on the level below
if (pathsByDir[i] == nullptr)
{
pathsByDir[i] = MapGetFootpathElement({ *mapCoords + CoordsDirectionDelta[i], z - PATH_HEIGHT_STEP });
if (pathsByDir[i] != nullptr
&& (!pathsByDir[i]->IsSloped() || pathsByDir[i]->GetSlopeDirection() != DirectionReverse(i)))
{
pathsByDir[i] = nullptr;
}
}
if (pathsByDir[i] != nullptr && pathsByDir[i]->IsQueue())
{
pathsByDir[i] = nullptr;
}
if (pathsByDir[i] != nullptr && i == _currentTrackPieceDirection)
{
keepOrientation = true;
break;
}
}
if (!keepOrientation)
{
for (int8_t i = 0; i < NumOrthogonalDirections; i++)
{
if (pathsByDir[i] != nullptr)
{
_currentTrackPieceDirection = i;
CoordsXYZ trackPos{};
WindowRideConstructionUpdateState(
&trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr);
PlaceProvisionalTrackPiece(rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos);
gMapSelectArrowDirection = _currentTrackPieceDirection;
break;
}
}
}
}
WindowRideConstructionUpdateActiveElements();
MapInvalidateMapSelectionTiles();
}
/**
*
* rct2: 0x006CD354
*/
void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords)
2018-06-22 23:21:44 +02:00
{
MapInvalidateSelectionRect();
MapInvalidateMapSelectionTiles();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords);
if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION)
2019-02-13 21:16:42 +01:00
{
RideConstructionInvalidateCurrentTrack();
return;
}
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW;
gMapSelectType = MAP_SELECT_TYPE_FULL;
gMapSelectPositionA = entranceOrExitCoords;
gMapSelectPositionB = entranceOrExitCoords;
gMapSelectArrowPosition = entranceOrExitCoords;
gMapSelectArrowDirection = DirectionReverse(entranceOrExitCoords.direction);
MapInvalidateSelectionRect();
entranceOrExitCoords.direction = DirectionReverse(gRideEntranceExitPlaceDirection);
StationIndex stationNum = gRideEntranceExitPlaceStationIndex;
if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT)
|| entranceOrExitCoords != gRideEntranceExitGhostPosition || stationNum != gRideEntranceExitGhostStationIndex)
{
auto ride = GetRide(_currentRideIndex);
if (ride != nullptr)
{
_currentTrackPrice = RideEntranceExitPlaceGhost(
*ride, entranceOrExitCoords, entranceOrExitCoords.direction, gRideEntranceExitPlaceType, stationNum);
}
WindowRideConstructionUpdateActiveElements();
2019-02-13 21:16:42 +01:00
}
}
2015-06-27 16:17:54 +02:00
/**
*
* rct2: 0x006CCA73
*/
void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords)
{
const CursorState* state = ContextGetCursorState();
WindowBase* w;
MapInvalidateMapSelectionTiles();
RideConstructionInvalidateCurrentTrack();
CoordsXYZ mapCoords{};
int32_t trackType, z, highestZ;
if (WindowRideConstructionUpdateState(&trackType, nullptr, nullptr, nullptr, nullptr, nullptr))
return;
z = mapCoords.z;
_currentTrackPieceType = trackType;
// Raise z above all slopes and water
highestZ = 0;
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT)
2018-06-22 23:21:44 +02:00
{
for (const auto& selectedTile : gMapSelectionTiles)
{
if (!MapIsLocationValid(selectedTile))
continue;
z = MapGetHighestZ(selectedTile);
if (z > highestZ)
highestZ = z;
}
}
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
auto ridePlacePosition = RideGetPlacePositionFromScreenPosition(screenCoords);
if (!ridePlacePosition)
return;
mapCoords = { *ridePlacePosition, z };
z = _trackPlaceZ;
if (z == 0)
z = MapGetHighestZ(mapCoords);
ToolCancel();
auto ride = GetRide(_currentRideIndex);
if (ride == nullptr)
return;
if (_trackPlaceZ == 0)
2018-06-22 23:21:44 +02:00
{
const auto& ted = GetTrackElementDescriptor(_currentTrackPieceType);
const PreviewTrack* trackBlock = ted.Block;
int32_t bx = 0;
do
{
bx = std::min<int32_t>(bx, trackBlock->z);
trackBlock++;
} while (trackBlock->index != 255);
z -= bx;
// FIX not sure exactly why it starts trial and error place from a lower Z, but it causes issues with disable
// clearance
if (!GetGameState().Cheats.DisableClearanceChecks && z > kMinimumLandZ)
{
z -= LAND_HEIGHT_STEP;
}
}
else
2018-06-22 23:21:44 +02:00
{
z = _trackPlaceZ;
}
// search for z value to build at, up to max ride height
int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2);
const auto& rtd = ride->GetRideTypeDescriptor();
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE))
{
for (int32_t zAttempts = 0; zAttempts < numAttempts; ++zAttempts)
{
_rideConstructionState = RideConstructionState::MazeBuild;
_currentTrackBegin.x = mapCoords.x;
_currentTrackBegin.y = mapCoords.y;
_currentTrackBegin.z = z;
_currentTrackSelectionFlags = 0;
auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION);
ContextBroadcastIntent(&intent);
w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr)
break;
gDisableErrorWindowSound = true;
auto gameAction = MazeSetTrackAction(
CoordsXYZD{ _currentTrackBegin, 0 }, true, _currentRideIndex, GC_SET_MAZE_TRACK_BUILD);
auto mazeSetTrackResult = GameActions::Execute(&gameAction);
if (mazeSetTrackResult.Error == GameActions::Status::Ok)
{
_trackPlaceCost = mazeSetTrackResult.Cost;
_trackPlaceErrorMessage = STR_NONE;
}
else
{
_trackPlaceCost = kMoney64Undefined;
_trackPlaceErrorMessage = std::get<StringId>(mazeSetTrackResult.ErrorMessage);
}
gDisableErrorWindowSound = false;
if (mazeSetTrackResult.Error != GameActions::Status::Ok)
{
_rideConstructionState = RideConstructionState::Place;
StringId errorText = std::get<StringId>(mazeSetTrackResult.ErrorMessage);
z -= 8;
if (errorText == STR_NOT_ENOUGH_CASH_REQUIRES || errorText == STR_CAN_ONLY_BUILD_THIS_UNDERWATER
|| errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_RIDE_CANT_BUILD_THIS_UNDERWATER
|| errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND || errorText == STR_TOO_HIGH_FOR_SUPPORTS
|| zAttempts == (numAttempts - 1) || z < 0)
{
OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, state->position.x);
w = WindowFindByClass(WindowClass::RideConstruction);
if (w != nullptr)
{
ToolSet(*w, WIDX_CONSTRUCT, Tool::Crosshair);
InputSetFlag(INPUT_FLAG_6, true);
_trackPlaceCtrlState = false;
_trackPlaceShiftState = false;
}
auto intent2 = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION);
ContextBroadcastIntent(&intent2);
break;
}
z += 16;
}
else
{
WindowCloseByClass(WindowClass::Error);
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, _currentTrackBegin);
break;
}
}
return;
}
2020-01-22 03:13:16 +01:00
for (int32_t zAttempts = 0; zAttempts < numAttempts; ++zAttempts)
2018-06-22 23:21:44 +02:00
{
_rideConstructionState = RideConstructionState::Front;
_currentTrackBegin.x = mapCoords.x;
_currentTrackBegin.y = mapCoords.y;
_currentTrackBegin.z = z;
_currentTrackSelectionFlags = 0;
WindowRideConstructionUpdateActiveElements();
w = WindowFindByClass(WindowClass::RideConstruction);
2017-08-15 10:07:44 +02:00
if (w == nullptr)
break;
gDisableErrorWindowSound = true;
w->OnMouseUp(WIDX_CONSTRUCT);
gDisableErrorWindowSound = false;
if (_trackPlaceCost == kMoney64Undefined)
2018-06-22 23:21:44 +02:00
{
StringId errorText = _trackPlaceErrorMessage;
z -= 8;
2018-06-22 23:21:44 +02:00
if (errorText == STR_NOT_ENOUGH_CASH_REQUIRES || errorText == STR_CAN_ONLY_BUILD_THIS_UNDERWATER
|| errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND
|| errorText == STR_TOO_HIGH_FOR_SUPPORTS || errorText == STR_TOO_HIGH
|| errorText == STR_LOCAL_AUTHORITY_WONT_ALLOW_CONSTRUCTION_ABOVE_TREE_HEIGHT
2020-01-22 03:13:16 +01:00
|| zAttempts == (numAttempts - 1) || z < 0)
2018-06-22 23:21:44 +02:00
{
int32_t saveTrackDirection = _currentTrackPieceDirection;
auto saveCurrentTrackCurve = _currentTrackCurve;
auto savePreviousTrackPitchEnd = _previousTrackPitchEnd;
auto saveCurrentTrackPitchEnd = _currentTrackPitchEnd;
auto savePreviousTrackRollEnd = _previousTrackRollEnd;
auto saveCurrentTrackRollEnd = _currentTrackRollEnd;
int32_t saveCurrentTrackAlternative = _currentTrackAlternative;
int32_t saveCurrentTrackLiftHill = _currentTrackLiftHill;
RideInitialiseConstructionWindow(*ride);
_currentTrackPieceDirection = saveTrackDirection;
_currentTrackCurve = saveCurrentTrackCurve;
_previousTrackPitchEnd = savePreviousTrackPitchEnd;
_currentTrackPitchEnd = saveCurrentTrackPitchEnd;
_previousTrackRollEnd = savePreviousTrackRollEnd;
_currentTrackRollEnd = saveCurrentTrackRollEnd;
_currentTrackAlternative = saveCurrentTrackAlternative;
_currentTrackLiftHill = saveCurrentTrackLiftHill;
OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, state->position.x);
break;
}
2020-01-22 11:41:43 +01:00
z += 16;
}
2018-06-22 23:21:44 +02:00
else
{
break;
}
}
}
void WindowRideConstructionKeyboardShortcutTurnLeft()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty)
2018-06-22 23:21:44 +02:00
{
return;
}
switch (_currentTrackCurve)
{
case EnumValue(TrackCurve::LeftSmall):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
break;
case EnumValue(TrackCurve::Left):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::LeftLarge):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::None):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::RightLarge):
if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::Right):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::RightSmall):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::RightVerySmall):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
default:
return;
}
}
void WindowRideConstructionKeyboardShortcutTurnRight()
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
2018-06-22 23:21:44 +02:00
}
switch (_currentTrackCurve)
2018-06-22 23:21:44 +02:00
{
case EnumValue(TrackCurve::RightSmall):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
break;
case EnumValue(TrackCurve::Right):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::RightLarge):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::None):
if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::LeftLarge):
if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::Left):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::LeftSmall):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
case EnumValue(TrackCurve::LeftVerySmall):
if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL)
&& w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_SMALL);
}
else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE)
&& w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEFT_CURVE_LARGE);
}
else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE)
&& w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL);
}
else if (
!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL)
&& w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL);
}
else
{
return;
}
break;
default:
return;
}
}
2015-06-27 16:17:54 +02:00
void WindowRideConstructionKeyboardShortcutUseTrackDefault()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
}
if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_STRAIGHT);
}
if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEVEL);
}
if (!WidgetIsDisabled(*w, WIDX_CHAIN_LIFT) && w->widgets[WIDX_CHAIN_LIFT].type != WindowWidgetType::Empty
&& _currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)
{
w->OnMouseDown(WIDX_CHAIN_LIFT);
}
if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_STRAIGHT);
}
}
void WindowRideConstructionKeyboardShortcutSlopeDown()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
}
switch (_currentTrackPitchEnd)
{
case TrackPitch::Down60:
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP
&& w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
break;
case TrackPitch::Down25:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
break;
case TrackPitch::None:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN);
}
else if (
IsTrackEnabled(TRACK_SLOPE_VERTICAL)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE)
{
return;
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Up25:
if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEVEL);
}
else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN);
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Up60:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP);
}
else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEVEL);
}
else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN);
}
else if (
IsTrackEnabled(TRACK_SLOPE_VERTICAL)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE)
{
return;
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Up90:
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
break;
default:
return;
}
}
void WindowRideConstructionKeyboardShortcutSlopeUp()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
}
switch (_currentTrackPitchEnd)
{
case TrackPitch::Up60:
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
break;
case TrackPitch::Up25:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
break;
case TrackPitch::None:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP);
}
else if (
IsTrackEnabled(TRACK_SLOPE_VERTICAL)
&& w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP)
{
return;
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Down25:
if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEVEL);
}
else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP);
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Down60:
if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN);
}
else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_LEVEL);
}
else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP);
}
else if (
IsTrackEnabled(TRACK_SLOPE_VERTICAL)
&& w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP)
{
return;
}
else if (
!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_UP_STEEP);
}
else
{
return;
}
break;
case TrackPitch::Down90:
if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP)
&& w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP
&& w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP);
}
break;
default:
2018-06-22 23:21:44 +02:00
return;
}
}
void WindowRideConstructionKeyboardShortcutChainLiftToggle()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_CHAIN_LIFT)
|| w->widgets[WIDX_CHAIN_LIFT].type == WindowWidgetType::Empty)
{
return;
}
w->OnMouseDown(WIDX_CHAIN_LIFT);
}
void WindowRideConstructionKeyboardShortcutBankLeft()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT)
|| w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
}
switch (_currentTrackRollEnd)
{
case TrackRoll::None:
if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_LEFT);
}
break;
case TrackRoll::Right:
if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_STRAIGHT);
}
else if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_LEFT);
}
else
{
return;
}
break;
default:
2018-06-22 23:21:44 +02:00
return;
}
}
void WindowRideConstructionKeyboardShortcutBankRight()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT)
|| w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty)
{
return;
}
switch (_currentTrackRollEnd)
{
case TrackRoll::None:
if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_RIGHT);
}
break;
case TrackRoll::Left:
if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_STRAIGHT);
}
else if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty)
{
w->OnMouseDown(WIDX_BANK_RIGHT);
}
else
{
return;
}
break;
default:
2018-06-22 23:21:44 +02:00
return;
}
}
void WindowRideConstructionKeyboardShortcutPreviousTrack()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_PREVIOUS_SECTION)
|| w->widgets[WIDX_PREVIOUS_SECTION].type == WindowWidgetType::Empty)
{
return;
}
w->OnMouseUp(WIDX_PREVIOUS_SECTION);
}
void WindowRideConstructionKeyboardShortcutNextTrack()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_NEXT_SECTION)
|| w->widgets[WIDX_NEXT_SECTION].type == WindowWidgetType::Empty)
{
return;
}
w->OnMouseUp(WIDX_NEXT_SECTION);
}
void WindowRideConstructionKeyboardShortcutBuildCurrent()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_CONSTRUCT) || w->widgets[WIDX_CONSTRUCT].type == WindowWidgetType::Empty)
{
return;
}
w->OnMouseUp(WIDX_CONSTRUCT);
}
void WindowRideConstructionKeyboardShortcutDemolishCurrent()
2018-06-22 23:21:44 +02:00
{
WindowBase* w = WindowFindByClass(WindowClass::RideConstruction);
if (w == nullptr || WidgetIsDisabled(*w, WIDX_DEMOLISH) || w->widgets[WIDX_DEMOLISH].type == WindowWidgetType::Empty)
{
return;
}
w->OnMouseUp(WIDX_DEMOLISH);
}
static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type)
2022-08-13 18:36:33 +02:00
{
if (_gotoStartPlacementMode)
2022-08-13 18:36:33 +02:00
{
_currentTrackBegin.z = Floor2(piecePos.z, COORDS_Z_STEP);
_rideConstructionState = RideConstructionState::Front;
_currentTrackSelectionFlags = 0;
2022-08-13 18:36:33 +02:00
_currentTrackPieceDirection = piecePos.direction & 3;
auto savedCurrentTrackCurve = _currentTrackCurve;
auto savedPreviousTrackPitchEnd = _previousTrackPitchEnd;
auto savedCurrentTrackPitchEnd = _currentTrackPitchEnd;
auto savedPreviousTrackRollEnd = _previousTrackRollEnd;
auto savedCurrentTrackRollEnd = _currentTrackRollEnd;
int32_t savedCurrentTrackAlternative = _currentTrackAlternative;
int32_t savedCurrentTrackLiftHill = _currentTrackLiftHill;
RideConstructionSetDefaultNextPiece();
WindowRideConstructionUpdateActiveElements();
auto ride = GetRide(_currentRideIndex);
if (!RideTryGetOriginElement(*ride, nullptr))
{
RideInitialiseConstructionWindow(*ride);
_currentTrackPieceDirection = piecePos.direction & 3;
if (!(savedCurrentTrackCurve & RideConstructionSpecialPieceSelected))
{
_currentTrackCurve = savedCurrentTrackCurve;
_previousTrackPitchEnd = savedPreviousTrackPitchEnd;
_currentTrackPitchEnd = savedCurrentTrackPitchEnd;
_previousTrackRollEnd = savedPreviousTrackRollEnd;
_currentTrackRollEnd = savedCurrentTrackRollEnd;
_currentTrackAlternative = savedCurrentTrackAlternative;
_currentTrackLiftHill = savedCurrentTrackLiftHill;
WindowRideConstructionUpdateActiveElements();
}
2022-08-13 18:36:33 +02:00
}
}
else
2022-08-13 18:36:33 +02:00
{
if (_rideConstructionState2 == RideConstructionState::Selected
|| _rideConstructionState2 == RideConstructionState::Front)
2022-08-13 18:36:33 +02:00
{
if (type == TrackElemType::MiddleStation || type == TrackElemType::BeginStation)
{
type = TrackElemType::EndStation;
}
2022-08-13 18:36:33 +02:00
}
if (_rideConstructionState2 == RideConstructionState::Back)
2022-08-13 18:36:33 +02:00
{
if (type == TrackElemType::MiddleStation)
{
type = TrackElemType::BeginStation;
}
2022-08-13 18:36:33 +02:00
}
if (NetworkGetMode() == NETWORK_MODE_CLIENT)
{
// rideConstructionState needs to be set again to the proper value, this only affects the client
_rideConstructionState = RideConstructionState::Selected;
}
_currentTrackBegin = piecePos;
_currentTrackPieceDirection = piecePos.direction;
_currentTrackPieceType = type;
_currentTrackSelectionFlags = 0;
if (_rideConstructionState2 == RideConstructionState::Front)
{
RideSelectNextSection();
}
else if (_rideConstructionState2 == RideConstructionState::Back)
{
RideSelectPreviousSection();
}
WindowRideConstructionUpdateActiveElements();
2022-08-13 18:36:33 +02:00
}
}
} // namespace OpenRCT2::Ui::Windows