mirror of https://github.com/OpenRCT2/OpenRCT2.git
7074 lines
250 KiB
C++
7074 lines
250 KiB
C++
/*****************************************************************************
|
||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||
*
|
||
* For a complete list of all authors, please refer to contributors.md
|
||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||
*
|
||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||
*****************************************************************************/
|
||
|
||
#include "../interface/Theme.h"
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <iterator>
|
||
#include <limits>
|
||
#include <memory>
|
||
#include <openrct2-ui/interface/Dropdown.h>
|
||
#include <openrct2-ui/interface/Viewport.h>
|
||
#include <openrct2-ui/interface/Widget.h>
|
||
#include <openrct2-ui/windows/Window.h>
|
||
#include <openrct2/Cheats.h>
|
||
#include <openrct2/Context.h>
|
||
#include <openrct2/Game.h>
|
||
#include <openrct2/Input.h>
|
||
#include <openrct2/Limits.h>
|
||
#include <openrct2/OpenRCT2.h>
|
||
#include <openrct2/actions/GameAction.h>
|
||
#include <openrct2/actions/ParkSetParameterAction.h>
|
||
#include <openrct2/actions/RideSetAppearanceAction.h>
|
||
#include <openrct2/actions/RideSetColourSchemeAction.h>
|
||
#include <openrct2/actions/RideSetPriceAction.h>
|
||
#include <openrct2/actions/RideSetSettingAction.h>
|
||
#include <openrct2/audio/audio.h>
|
||
#include <openrct2/config/Config.h>
|
||
#include <openrct2/core/String.hpp>
|
||
#include <openrct2/entity/EntityList.h>
|
||
#include <openrct2/entity/Staff.h>
|
||
#include <openrct2/localisation/Date.h>
|
||
#include <openrct2/localisation/Formatter.h>
|
||
#include <openrct2/localisation/Localisation.h>
|
||
#include <openrct2/localisation/LocalisationService.h>
|
||
#include <openrct2/localisation/StringIds.h>
|
||
#include <openrct2/network/network.h>
|
||
#include <openrct2/object/MusicObject.h>
|
||
#include <openrct2/object/ObjectManager.h>
|
||
#include <openrct2/object/ObjectRepository.h>
|
||
#include <openrct2/object/StationObject.h>
|
||
#include <openrct2/rct1/RCT1.h>
|
||
#include <openrct2/rct2/T6Exporter.h>
|
||
#include <openrct2/ride/RideConstruction.h>
|
||
#include <openrct2/ride/RideData.h>
|
||
#include <openrct2/ride/ShopItem.h>
|
||
#include <openrct2/ride/Station.h>
|
||
#include <openrct2/ride/Track.h>
|
||
#include <openrct2/ride/TrackData.h>
|
||
#include <openrct2/ride/TrackDesign.h>
|
||
#include <openrct2/ride/TrackDesignRepository.h>
|
||
#include <openrct2/ride/Vehicle.h>
|
||
#include <openrct2/sprites.h>
|
||
#include <openrct2/windows/Intent.h>
|
||
#include <openrct2/world/Park.h>
|
||
#include <optional>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
using namespace OpenRCT2;
|
||
using namespace OpenRCT2::TrackMetaData;
|
||
|
||
static constexpr const rct_string_id WINDOW_TITLE = STR_RIDE_WINDOW_TITLE;
|
||
static constexpr const int32_t WH = 207;
|
||
static constexpr const int32_t WW = 316;
|
||
|
||
static void PopulateVehicleTypeDropdown(Ride* ride, bool forceRefresh = false);
|
||
|
||
enum
|
||
{
|
||
WINDOW_RIDE_PAGE_MAIN,
|
||
WINDOW_RIDE_PAGE_VEHICLE,
|
||
WINDOW_RIDE_PAGE_OPERATING,
|
||
WINDOW_RIDE_PAGE_MAINTENANCE,
|
||
WINDOW_RIDE_PAGE_COLOUR,
|
||
WINDOW_RIDE_PAGE_MUSIC,
|
||
WINDOW_RIDE_PAGE_MEASUREMENTS,
|
||
WINDOW_RIDE_PAGE_GRAPHS,
|
||
WINDOW_RIDE_PAGE_INCOME,
|
||
WINDOW_RIDE_PAGE_CUSTOMER,
|
||
WINDOW_RIDE_PAGE_COUNT
|
||
};
|
||
|
||
#pragma region Widgets
|
||
|
||
// clang-format off
|
||
enum {
|
||
WIDX_BACKGROUND,
|
||
WIDX_TITLE,
|
||
WIDX_CLOSE,
|
||
WIDX_PAGE_BACKGROUND,
|
||
WIDX_TAB_1,
|
||
WIDX_TAB_2,
|
||
WIDX_TAB_3,
|
||
WIDX_TAB_4,
|
||
WIDX_TAB_5,
|
||
WIDX_TAB_6,
|
||
WIDX_TAB_7,
|
||
WIDX_TAB_8,
|
||
WIDX_TAB_9,
|
||
WIDX_TAB_10,
|
||
|
||
WIDX_VIEWPORT = 14,
|
||
WIDX_VIEW,
|
||
WIDX_VIEW_DROPDOWN,
|
||
WIDX_STATUS,
|
||
WIDX_OPEN,
|
||
WIDX_CONSTRUCTION,
|
||
WIDX_RENAME,
|
||
WIDX_LOCATE,
|
||
WIDX_DEMOLISH,
|
||
WIDX_CLOSE_LIGHT,
|
||
WIDX_SIMULATE_LIGHT,
|
||
WIDX_TEST_LIGHT,
|
||
WIDX_OPEN_LIGHT,
|
||
WIDX_RIDE_TYPE,
|
||
WIDX_RIDE_TYPE_DROPDOWN,
|
||
|
||
WIDX_VEHICLE_TYPE = 14,
|
||
WIDX_VEHICLE_TYPE_DROPDOWN,
|
||
WIDX_VEHICLE_TRAINS_PREVIEW,
|
||
WIDX_VEHICLE_TRAINS,
|
||
WIDX_VEHICLE_TRAINS_INCREASE,
|
||
WIDX_VEHICLE_TRAINS_DECREASE,
|
||
WIDX_VEHICLE_CARS_PER_TRAIN,
|
||
WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE,
|
||
WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE,
|
||
|
||
WIDX_MODE_TWEAK = 14,
|
||
WIDX_MODE_TWEAK_INCREASE,
|
||
WIDX_MODE_TWEAK_DECREASE,
|
||
WIDX_LIFT_HILL_SPEED,
|
||
WIDX_LIFT_HILL_SPEED_INCREASE,
|
||
WIDX_LIFT_HILL_SPEED_DECREASE,
|
||
WIDX_LOAD_CHECKBOX,
|
||
WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX,
|
||
WIDX_MINIMUM_LENGTH_CHECKBOX,
|
||
WIDX_MINIMUM_LENGTH,
|
||
WIDX_MINIMUM_LENGTH_INCREASE,
|
||
WIDX_MINIMUM_LENGTH_DECREASE,
|
||
WIDX_MAXIMUM_LENGTH_CHECKBOX,
|
||
WIDX_MAXIMUM_LENGTH,
|
||
WIDX_MAXIMUM_LENGTH_INCREASE,
|
||
WIDX_MAXIMUM_LENGTH_DECREASE,
|
||
WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX,
|
||
WIDX_MODE_TWEAK_LABEL,
|
||
WIDX_LIFT_HILL_SPEED_LABEL,
|
||
WIDX_MODE,
|
||
WIDX_MODE_DROPDOWN,
|
||
WIDX_LOAD,
|
||
WIDX_LOAD_DROPDOWN,
|
||
WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL,
|
||
WIDX_OPERATE_NUMBER_OF_CIRCUITS,
|
||
WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE,
|
||
WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE,
|
||
|
||
WIDX_INSPECTION_INTERVAL = 14,
|
||
WIDX_INSPECTION_INTERVAL_DROPDOWN,
|
||
WIDX_LOCATE_MECHANIC,
|
||
WIDX_REFURBISH_RIDE,
|
||
WIDX_FORCE_BREAKDOWN,
|
||
|
||
WIDX_TRACK_PREVIEW = 14,
|
||
WIDX_TRACK_COLOUR_SCHEME,
|
||
WIDX_TRACK_COLOUR_SCHEME_DROPDOWN,
|
||
WIDX_TRACK_MAIN_COLOUR,
|
||
WIDX_TRACK_ADDITIONAL_COLOUR,
|
||
WIDX_TRACK_SUPPORT_COLOUR,
|
||
WIDX_MAZE_STYLE,
|
||
WIDX_MAZE_STYLE_DROPDOWN,
|
||
WIDX_PAINT_INDIVIDUAL_AREA,
|
||
WIDX_ENTRANCE_PREVIEW,
|
||
WIDX_ENTRANCE_STYLE,
|
||
WIDX_ENTRANCE_STYLE_DROPDOWN,
|
||
WIDX_VEHICLE_PREVIEW,
|
||
WIDX_VEHICLE_COLOUR_SCHEME,
|
||
WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN,
|
||
WIDX_VEHICLE_COLOUR_INDEX,
|
||
WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN,
|
||
WIDX_VEHICLE_BODY_COLOR,
|
||
WIDX_VEHICLE_TRIM_COLOUR,
|
||
WIDX_VEHICLE_TERNARY_COLOUR,
|
||
WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX,
|
||
|
||
WIDX_PLAY_MUSIC = 14,
|
||
WIDX_MUSIC,
|
||
WIDX_MUSIC_DROPDOWN,
|
||
|
||
WIDX_SAVE_TRACK_DESIGN = 14,
|
||
WIDX_SELECT_NEARBY_SCENERY,
|
||
WIDX_RESET_SELECTION,
|
||
WIDX_SAVE_DESIGN,
|
||
WIDX_CANCEL_DESIGN,
|
||
|
||
WIDX_GRAPH = 14,
|
||
WIDX_GRAPH_VELOCITY,
|
||
WIDX_GRAPH_ALTITUDE,
|
||
WIDX_GRAPH_VERTICAL,
|
||
WIDX_GRAPH_LATERAL,
|
||
|
||
WIDX_PRIMARY_PRICE_LABEL = 14,
|
||
WIDX_PRIMARY_PRICE,
|
||
WIDX_PRIMARY_PRICE_INCREASE,
|
||
WIDX_PRIMARY_PRICE_DECREASE,
|
||
WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK,
|
||
WIDX_SECONDARY_PRICE_LABEL,
|
||
WIDX_SECONDARY_PRICE,
|
||
WIDX_SECONDARY_PRICE_INCREASE,
|
||
WIDX_SECONDARY_PRICE_DECREASE,
|
||
WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK,
|
||
|
||
WIDX_SHOW_GUESTS_THOUGHTS = 14,
|
||
WIDX_SHOW_GUESTS_ON_RIDE,
|
||
WIDX_SHOW_GUESTS_QUEUING
|
||
};
|
||
|
||
constexpr int32_t RCT1_LIGHT_OFFSET = 4;
|
||
|
||
#define MAIN_RIDE_WIDGETS \
|
||
WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
|
||
MakeWidget({ 0, 43}, {316, 137}, WindowWidgetType::Resize, WindowColour::Secondary), \
|
||
MakeTab ({ 3, 17}, STR_VIEW_OF_RIDE_ATTRACTION_TIP ), \
|
||
MakeTab ({ 34, 17}, STR_VEHICLE_DETAILS_AND_OPTIONS_TIP ), \
|
||
MakeTab ({ 65, 17}, STR_OPERATING_OPTIONS_TIP ), \
|
||
MakeTab ({ 96, 17}, STR_MAINTENANCE_OPTIONS_TIP ), \
|
||
MakeTab ({127, 17}, STR_COLOUR_SCHEME_OPTIONS_TIP ), \
|
||
MakeTab ({158, 17}, STR_SOUND_AND_MUSIC_OPTIONS_TIP ), \
|
||
MakeTab ({189, 17}, STR_MEASUREMENTS_AND_TEST_DATA_TIP ), \
|
||
MakeTab ({220, 17}, STR_GRAPHS_TIP ), \
|
||
MakeTab ({251, 17}, STR_INCOME_AND_COSTS_TIP ), \
|
||
MakeTab ({282, 17}, STR_CUSTOMER_INFORMATION_TIP )
|
||
|
||
// 0x009ADC34
|
||
static rct_widget window_ride_main_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({ 3, 60}, {288, 107}, WindowWidgetType::Viewport, WindowColour::Secondary, STR_VIEWPORT ),
|
||
MakeWidget({ 35, 46}, {222, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, 0xFFFFFFFF, STR_VIEW_SELECTION ),
|
||
MakeWidget({245, 47}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_VIEW_SELECTION ),
|
||
MakeWidget({ 3, 167}, {288, 11}, WindowWidgetType::LabelCentred, WindowColour::Secondary ),
|
||
MakeWidget({291, 46}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_OPEN_CLOSE_OR_TEST_RIDE),
|
||
MakeWidget({291, 70}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION, STR_CONSTRUCTION ),
|
||
MakeWidget({291, 94}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RENAME, STR_NAME_RIDE_TIP ),
|
||
MakeWidget({291, 118}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP ),
|
||
MakeWidget({291, 142}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH, STR_DEMOLISH_RIDE_TIP ),
|
||
MakeWidget({296, 48}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_CLOSE_BUTTON_0, STR_CLOSE_RIDE_TIP ),
|
||
MakeWidget({296, 62}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_TEST_BUTTON_0, STR_SIMULATE_RIDE_TIP ),
|
||
MakeWidget({296, 62}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_TEST_BUTTON_0, STR_TEST_RIDE_TIP ),
|
||
MakeWidget({296, 76}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_OPEN_BUTTON_0, STR_OPEN_RIDE_TIP ),
|
||
MakeWidget({ 3, 180}, {305, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_6_STRINGID ),
|
||
MakeWidget({297, 180}, { 11, 12}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009ADDA8
|
||
static rct_widget window_ride_vehicle_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget ({ 7, 50}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
|
||
MakeWidget ({297, 51}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
|
||
MakeWidget ({ 7, 147}, {302, 43}, WindowWidgetType::Scroll, WindowColour::Secondary, STR_EMPTY ),
|
||
MakeSpinnerWidgets({ 7, 196}, {145, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_RIDE_VEHICLE_COUNT, STR_MAX_VEHICLES_TIP ),
|
||
MakeSpinnerWidgets({164, 196}, {145, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_1_CAR_PER_TRAIN, STR_MAX_CARS_PER_TRAIN_TIP),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009ADEFC
|
||
static rct_widget window_ride_operating_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeSpinnerWidgets({157, 61}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_18_STRINGID ), // NB: 3 widgets
|
||
MakeSpinnerWidgets({157, 75}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_LIFT_HILL_CHAIN_SPEED_VALUE ), // NB: 3 widgets
|
||
MakeWidget ({ 7, 109}, { 80, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_WAIT_FOR, STR_WAIT_FOR_PASSENGERS_BEFORE_DEPARTING_TIP),
|
||
MakeWidget ({ 7, 124}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ),
|
||
MakeWidget ({ 7, 139}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MINIMUM_WAITING_TIME, STR_MINIMUM_LENGTH_BEFORE_DEPARTING_TIP ),
|
||
MakeSpinnerWidgets({157, 139}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_10_STRINGID ), // NB: 3 widgets
|
||
MakeWidget ({ 7, 154}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAXIMUM_WAITING_TIME, STR_MAXIMUM_LENGTH_BEFORE_DEPARTING_TIP ),
|
||
MakeSpinnerWidgets({157, 154}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_14_STRINGID ), // NB: 3 widgets
|
||
MakeWidget ({ 7, 169}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP ),
|
||
MakeWidget ({ 21, 61}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary ),
|
||
MakeWidget ({ 21, 75}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary, STR_LIFT_HILL_CHAIN_SPEED ),
|
||
MakeWidget ({ 7, 47}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_OPERATING_MODE ),
|
||
MakeWidget ({297, 48}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_OPERATING_MODE ),
|
||
MakeWidget ({ 87, 109}, {222, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
|
||
MakeWidget ({297, 110}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
|
||
MakeWidget ({ 21, 89}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary, STR_NUMBER_OF_CIRCUITS, STR_NUMBER_OF_CIRCUITS_TIP ),
|
||
MakeSpinnerWidgets({157, 89}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_NUMBER_OF_CIRCUITS_VALUE ), // NB: 3 widgets
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE190
|
||
static rct_widget window_ride_maintenance_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({107, 71}, {202, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE),
|
||
MakeWidget({297, 72}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE),
|
||
MakeWidget({289, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_LOCATE_NEAREST_AVAILABLE_MECHANIC_TIP ),
|
||
MakeWidget({265, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION, STR_REFURBISH_RIDE_TIP ),
|
||
MakeWidget({241, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_NO_ENTRY, STR_DEBUG_FORCE_BREAKDOWN_TIP ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE2A4
|
||
static rct_widget window_ride_colour_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({ 3, 47}, { 68, 47}, WindowWidgetType::Spinner, WindowColour::Secondary ),
|
||
MakeWidget({ 74, 49}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_14_STRINGID ),
|
||
MakeWidget({301, 50}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_COLOUR_SCHEME_TO_CHANGE_TIP ),
|
||
MakeWidget({ 79, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP ),
|
||
MakeWidget({ 99, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP ),
|
||
MakeWidget({119, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_SUPPORT_STRUCTURE_COLOUR_TIP ),
|
||
MakeWidget({ 74, 49}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
|
||
MakeWidget({301, 50}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
|
||
MakeWidget({289, 68}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_PAINTBRUSH, STR_PAINT_INDIVIDUAL_AREA_TIP ),
|
||
MakeWidget({245, 101}, { 68, 47}, WindowWidgetType::Spinner, WindowColour::Secondary ),
|
||
MakeWidget({103, 103}, {139, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY ),
|
||
MakeWidget({230, 104}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_STYLE_OF_ENTRANCE_EXIT_STATION_TIP),
|
||
MakeWidget({ 3, 157}, { 68, 47}, WindowWidgetType::Scroll, WindowColour::Secondary, STR_EMPTY ),
|
||
MakeWidget({ 74, 157}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_6_STRINGID ),
|
||
MakeWidget({301, 158}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_COLOUR_SCHEME_TIP ),
|
||
MakeWidget({ 74, 173}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
|
||
MakeWidget({301, 174}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_TO_MODIFY_TIP ),
|
||
MakeWidget({ 79, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP ),
|
||
MakeWidget({ 99, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP ),
|
||
MakeWidget({119, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_2_TIP ),
|
||
MakeWidget({100, 74}, {239, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_RANDOM_COLOUR ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE4C8
|
||
static rct_widget window_ride_music_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({ 7, 47}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_PLAY_MUSIC, STR_SELECT_MUSIC_TIP ),
|
||
MakeWidget({ 7, 62}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY ),
|
||
MakeWidget({297, 63}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_MUSIC_STYLE_TIP),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE5DC
|
||
static rct_widget window_ride_measurements_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({288, 194}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_FLOPPY, STR_SAVE_TRACK_DESIGN),
|
||
MakeWidget({ 4, 127}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_SELECT_NEARBY_SCENERY ),
|
||
MakeWidget({158, 127}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RESET_SELECTION ),
|
||
MakeWidget({ 4, 177}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_DESIGN_SAVE ),
|
||
MakeWidget({158, 177}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_DESIGN_CANCEL ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE710
|
||
static rct_widget window_ride_graphs_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({ 3, 46}, {306, 112}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_HORIZONTAL, STR_LOGGING_DATA_FROM_TIP ),
|
||
MakeWidget({ 3, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_VELOCITY, STR_SHOW_GRAPH_OF_VELOCITY_AGAINST_TIME_TIP ),
|
||
MakeWidget({ 76, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_ALTITUDE, STR_SHOW_GRAPH_OF_ALTITUDE_AGAINST_TIME_TIP ),
|
||
MakeWidget({149, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_VERT_G, STR_SHOW_GRAPH_OF_VERTICAL_ACCELERATION_AGAINST_TIME_TIP),
|
||
MakeWidget({222, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_LAT_G, STR_SHOW_GRAPH_OF_LATERAL_ACCELERATION_AGAINST_TIME_TIP ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE844
|
||
static rct_widget window_ride_income_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget ({ 19, 50}, {126, 14}, WindowWidgetType::Label, WindowColour::Secondary ),
|
||
MakeSpinnerWidgets({147, 50}, {162, 14}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_6_CURRENCY2DP ), // NB: 3 widgets
|
||
MakeWidget ({ 5, 62}, {306, 13}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP),
|
||
MakeWidget ({ 19, 94}, {126, 14}, WindowWidgetType::Label, WindowColour::Secondary ),
|
||
MakeSpinnerWidgets({147, 94}, {162, 14}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_RIDE_SECONDARY_PRICE_VALUE ), // NB: 3 widgets
|
||
MakeWidget ({ 5, 106}, {306, 13}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
// 0x009AE9C8
|
||
static rct_widget window_ride_customer_widgets[] = {
|
||
MAIN_RIDE_WIDGETS,
|
||
MakeWidget({289, 54}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION_TIP),
|
||
MakeWidget({289, 78}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION_TIP ),
|
||
MakeWidget({289, 102}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_QUEUING_FOR_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_QUEUING_FOR_THIS_RIDE_ATTRACTION_TIP ),
|
||
WIDGETS_END,
|
||
};
|
||
|
||
static rct_widget *window_ride_page_widgets[] = {
|
||
window_ride_main_widgets,
|
||
window_ride_vehicle_widgets,
|
||
window_ride_operating_widgets,
|
||
window_ride_maintenance_widgets,
|
||
window_ride_colour_widgets,
|
||
window_ride_music_widgets,
|
||
window_ride_measurements_widgets,
|
||
window_ride_graphs_widgets,
|
||
window_ride_income_widgets,
|
||
window_ride_customer_widgets,
|
||
};
|
||
|
||
static constexpr const uint64_t window_ride_page_hold_down_widgets[] = {
|
||
0,
|
||
(1ULL << WIDX_VEHICLE_TRAINS_INCREASE) |
|
||
(1ULL << WIDX_VEHICLE_TRAINS_DECREASE) |
|
||
(1ULL << WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE) |
|
||
(1ULL << WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE),
|
||
(1ULL << WIDX_MODE_TWEAK_INCREASE) |
|
||
(1ULL << WIDX_MODE_TWEAK_DECREASE) |
|
||
(1ULL << WIDX_LIFT_HILL_SPEED_INCREASE) |
|
||
(1ULL << WIDX_LIFT_HILL_SPEED_DECREASE) |
|
||
(1ULL << WIDX_MINIMUM_LENGTH_INCREASE) |
|
||
(1ULL << WIDX_MINIMUM_LENGTH_DECREASE) |
|
||
(1ULL << WIDX_MAXIMUM_LENGTH_INCREASE) |
|
||
(1ULL << WIDX_MAXIMUM_LENGTH_DECREASE) |
|
||
(1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE) |
|
||
(1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE),
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
(1ULL << WIDX_PRIMARY_PRICE_INCREASE) |
|
||
(1ULL << WIDX_PRIMARY_PRICE_DECREASE) |
|
||
(1ULL << WIDX_SECONDARY_PRICE_INCREASE) |
|
||
(1ULL << WIDX_SECONDARY_PRICE_DECREASE),
|
||
0,
|
||
};
|
||
// clang-format on
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Events
|
||
|
||
static void WindowRideInitViewport(rct_window* w);
|
||
|
||
static void WindowRideMainMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideMainResize(rct_window* w);
|
||
static void WindowRideMainMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideMainDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideMainUpdate(rct_window* w);
|
||
static void WindowRideMainTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text);
|
||
static void WindowRideMainViewportRotate(rct_window* w);
|
||
static void WindowRideMainInvalidate(rct_window* w);
|
||
static void WindowRideMainPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
static void WindowRideMainFollowRide(rct_window* w);
|
||
|
||
static void WindowRideVehicleMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideVehicleResize(rct_window* w);
|
||
static void WindowRideVehicleMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideVehicleDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideVehicleUpdate(rct_window* w);
|
||
static OpenRCT2String WindowRideVehicleTooltip(rct_window* const w, const rct_widgetindex widgetIndex, rct_string_id fallback);
|
||
static void WindowRideVehicleInvalidate(rct_window* w);
|
||
static void WindowRideVehiclePaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
static void WindowRideVehicleScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
|
||
|
||
static void WindowRideOperatingMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideOperatingResize(rct_window* w);
|
||
static void WindowRideOperatingMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideOperatingLengthWindow(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideOperatingTweakTextInput(rct_window* w, const Ride& ride);
|
||
static void WindowRideOperatingDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideOperatingUpdate(rct_window* w);
|
||
static void WindowRideOperatingTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text);
|
||
static void WindowRideOperatingInvalidate(rct_window* w);
|
||
static void WindowRideOperatingPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
|
||
static void WindowRideMaintenanceMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideMaintenanceResize(rct_window* w);
|
||
static void WindowRideMaintenanceMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideMaintenanceDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideMaintenanceUpdate(rct_window* w);
|
||
static void WindowRideMaintenanceInvalidate(rct_window* w);
|
||
static void WindowRideMaintenancePaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
|
||
static void WindowRideColourClose(rct_window* w);
|
||
static void WindowRideColourMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideColourResize(rct_window* w);
|
||
static void WindowRideColourMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideColourDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideColourUpdate(rct_window* w);
|
||
static void WindowRideColourTooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
|
||
static void WindowRideColourTooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
|
||
static void WindowRideColourInvalidate(rct_window* w);
|
||
static void WindowRideColourPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
static void WindowRideColourScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
|
||
|
||
static void WindowRideMusicMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideMusicResize(rct_window* w);
|
||
static void WindowRideMusicMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideMusicDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideMusicUpdate(rct_window* w);
|
||
static void WindowRideMusicInvalidate(rct_window* w);
|
||
static void WindowRideMusicPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
|
||
static void WindowRideMeasurementsClose(rct_window* w);
|
||
static void WindowRideMeasurementsMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideMeasurementsResize(rct_window* w);
|
||
static void WindowRideMeasurementsMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideMeasurementsDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
|
||
static void WindowRideMeasurementsUpdate(rct_window* w);
|
||
static void WindowRideMeasurementsTooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
|
||
static void WindowRideMeasurementsTooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
|
||
static void WindowRideMeasurementsToolabort(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideMeasurementsInvalidate(rct_window* w);
|
||
static void WindowRideMeasurementsPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
|
||
static void WindowRideGraphsMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideGraphsResize(rct_window* w);
|
||
static void WindowRideGraphsMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideGraphsUpdate(rct_window* w);
|
||
static void WindowRideGraphsScrollgetheight(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height);
|
||
static void WindowRideGraphs15(rct_window* w, int32_t scrollIndex, int32_t scrollAreaType);
|
||
static OpenRCT2String WindowRideGraphsTooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback);
|
||
static void WindowRideGraphsInvalidate(rct_window* w);
|
||
static void WindowRideGraphsPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
static void WindowRideGraphsScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
|
||
|
||
static void WindowRideIncomeMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideIncomeResize(rct_window* w);
|
||
static void WindowRideIncomeMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
|
||
static void WindowRideIncomeUpdate(rct_window* w);
|
||
static void WindowRideIncomeTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text);
|
||
static void WindowRideIncomeInvalidate(rct_window* w);
|
||
static void WindowRideIncomePaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
static bool WindowRideIncomeCanModifyPrimaryPrice(rct_window* w);
|
||
|
||
static void WindowRideCustomerMouseup(rct_window* w, rct_widgetindex widgetIndex);
|
||
static void WindowRideCustomerResize(rct_window* w);
|
||
static void WindowRideCustomerUpdate(rct_window* w);
|
||
static void WindowRideCustomerInvalidate(rct_window* w);
|
||
static void WindowRideCustomerPaint(rct_window* w, rct_drawpixelinfo* dpi);
|
||
|
||
static void WindowRideSetPage(rct_window* w, int32_t page);
|
||
|
||
// 0x0098DFD4
|
||
static rct_window_event_list window_ride_main_events([](auto& events) {
|
||
events.mouse_up = &WindowRideMainMouseup;
|
||
events.resize = &WindowRideMainResize;
|
||
events.mouse_down = &WindowRideMainMousedown;
|
||
events.dropdown = &WindowRideMainDropdown;
|
||
events.update = &WindowRideMainUpdate;
|
||
events.text_input = &WindowRideMainTextinput;
|
||
events.viewport_rotate = &WindowRideMainViewportRotate;
|
||
events.invalidate = &WindowRideMainInvalidate;
|
||
events.paint = &WindowRideMainPaint;
|
||
});
|
||
|
||
// 0x0098E204
|
||
static rct_window_event_list window_ride_vehicle_events([](auto& events) {
|
||
events.mouse_up = &WindowRideVehicleMouseup;
|
||
events.resize = &WindowRideVehicleResize;
|
||
events.mouse_down = &WindowRideVehicleMousedown;
|
||
events.dropdown = &WindowRideVehicleDropdown;
|
||
events.update = &WindowRideVehicleUpdate;
|
||
events.tooltip = &WindowRideVehicleTooltip;
|
||
events.invalidate = &WindowRideVehicleInvalidate;
|
||
events.paint = &WindowRideVehiclePaint;
|
||
events.scroll_paint = &WindowRideVehicleScrollpaint;
|
||
});
|
||
|
||
// 0x0098E0B4
|
||
static rct_window_event_list window_ride_operating_events([](auto& events) {
|
||
events.mouse_up = &WindowRideOperatingMouseup;
|
||
events.resize = &WindowRideOperatingResize;
|
||
events.mouse_down = &WindowRideOperatingMousedown;
|
||
events.dropdown = &WindowRideOperatingDropdown;
|
||
events.update = &WindowRideOperatingUpdate;
|
||
events.text_input = &WindowRideOperatingTextinput;
|
||
events.invalidate = &WindowRideOperatingInvalidate;
|
||
events.paint = &WindowRideOperatingPaint;
|
||
});
|
||
|
||
// 0x0098E124
|
||
static rct_window_event_list window_ride_maintenance_events([](auto& events) {
|
||
events.mouse_up = &WindowRideMaintenanceMouseup;
|
||
events.resize = &WindowRideMaintenanceResize;
|
||
events.mouse_down = &WindowRideMaintenanceMousedown;
|
||
events.dropdown = &WindowRideMaintenanceDropdown;
|
||
events.update = &WindowRideMaintenanceUpdate;
|
||
events.invalidate = &WindowRideMaintenanceInvalidate;
|
||
events.paint = &WindowRideMaintenancePaint;
|
||
});
|
||
|
||
// 0x0098E044
|
||
static rct_window_event_list window_ride_colour_events([](auto& events) {
|
||
events.close = &WindowRideColourClose;
|
||
events.mouse_up = &WindowRideColourMouseup;
|
||
events.resize = &WindowRideColourResize;
|
||
events.mouse_down = &WindowRideColourMousedown;
|
||
events.dropdown = &WindowRideColourDropdown;
|
||
events.update = &WindowRideColourUpdate;
|
||
events.tool_down = &WindowRideColourTooldown;
|
||
events.tool_drag = &WindowRideColourTooldrag;
|
||
events.invalidate = &WindowRideColourInvalidate;
|
||
events.paint = &WindowRideColourPaint;
|
||
events.scroll_paint = &WindowRideColourScrollpaint;
|
||
});
|
||
|
||
// 0x0098E194
|
||
static rct_window_event_list window_ride_music_events([](auto& events) {
|
||
events.mouse_up = &WindowRideMusicMouseup;
|
||
events.resize = &WindowRideMusicResize;
|
||
events.mouse_down = &WindowRideMusicMousedown;
|
||
events.dropdown = &WindowRideMusicDropdown;
|
||
events.update = &WindowRideMusicUpdate;
|
||
events.invalidate = &WindowRideMusicInvalidate;
|
||
events.paint = &WindowRideMusicPaint;
|
||
});
|
||
|
||
// 0x0098DE14
|
||
static rct_window_event_list window_ride_measurements_events([](auto& events) {
|
||
events.close = &WindowRideMeasurementsClose;
|
||
events.mouse_up = &WindowRideMeasurementsMouseup;
|
||
events.resize = &WindowRideMeasurementsResize;
|
||
events.mouse_down = &WindowRideMeasurementsMousedown;
|
||
events.dropdown = &WindowRideMeasurementsDropdown;
|
||
events.update = &WindowRideMeasurementsUpdate;
|
||
events.tool_down = &WindowRideMeasurementsTooldown;
|
||
events.tool_drag = &WindowRideMeasurementsTooldrag;
|
||
events.tool_abort = &WindowRideMeasurementsToolabort;
|
||
events.invalidate = &WindowRideMeasurementsInvalidate;
|
||
events.paint = &WindowRideMeasurementsPaint;
|
||
});
|
||
|
||
// 0x0098DF64
|
||
static rct_window_event_list window_ride_graphs_events([](auto& events) {
|
||
events.mouse_up = &WindowRideGraphsMouseup;
|
||
events.resize = &WindowRideGraphsResize;
|
||
events.mouse_down = &WindowRideGraphsMousedown;
|
||
events.update = &WindowRideGraphsUpdate;
|
||
events.get_scroll_size = &WindowRideGraphsScrollgetheight;
|
||
events.unknown_15 = &WindowRideGraphs15;
|
||
events.tooltip = &WindowRideGraphsTooltip;
|
||
events.invalidate = &WindowRideGraphsInvalidate;
|
||
events.paint = &WindowRideGraphsPaint;
|
||
events.scroll_paint = &WindowRideGraphsScrollpaint;
|
||
});
|
||
|
||
// 0x0098DEF4
|
||
static rct_window_event_list window_ride_income_events([](auto& events) {
|
||
events.mouse_up = &WindowRideIncomeMouseup;
|
||
events.resize = &WindowRideIncomeResize;
|
||
events.mouse_down = &WindowRideIncomeMousedown;
|
||
events.update = &WindowRideIncomeUpdate;
|
||
events.text_input = &WindowRideIncomeTextinput;
|
||
events.invalidate = &WindowRideIncomeInvalidate;
|
||
events.paint = &WindowRideIncomePaint;
|
||
});
|
||
|
||
// 0x0098DE84
|
||
static rct_window_event_list window_ride_customer_events([](auto& events) {
|
||
events.mouse_up = &WindowRideCustomerMouseup;
|
||
events.resize = &WindowRideCustomerResize;
|
||
events.update = &WindowRideCustomerUpdate;
|
||
events.invalidate = &WindowRideCustomerInvalidate;
|
||
events.paint = &WindowRideCustomerPaint;
|
||
});
|
||
|
||
// clang-format off
|
||
static rct_window_event_list *window_ride_page_events[] = {
|
||
&window_ride_main_events,
|
||
&window_ride_vehicle_events,
|
||
&window_ride_operating_events,
|
||
&window_ride_maintenance_events,
|
||
&window_ride_colour_events,
|
||
&window_ride_music_events,
|
||
&window_ride_measurements_events,
|
||
&window_ride_graphs_events,
|
||
&window_ride_income_events,
|
||
&window_ride_customer_events,
|
||
};
|
||
// clang-format on
|
||
|
||
#pragma endregion
|
||
|
||
static bool _collectTrackDesignScenery = false;
|
||
static int32_t _lastSceneryX = 0;
|
||
static int32_t _lastSceneryY = 0;
|
||
static std::unique_ptr<TrackDesign> _trackDesign;
|
||
|
||
// Cached overall view for each ride
|
||
// (Re)calculated when the ride window is opened
|
||
struct RideOverallView
|
||
{
|
||
CoordsXYZ loc;
|
||
ZoomLevel zoom;
|
||
};
|
||
|
||
static std::vector<RideOverallView> ride_overall_views = {};
|
||
|
||
static constexpr const int32_t window_ride_tab_animation_divisor[] = {
|
||
0, // WINDOW_RIDE_PAGE_MAIN
|
||
0, // WINDOW_RIDE_PAGE_VEHICLE
|
||
2, // WINDOW_RIDE_PAGE_OPERATING
|
||
2, // WINDOW_RIDE_PAGE_MAINTENANCE
|
||
4, // WINDOW_RIDE_PAGE_COLOUR
|
||
2, // WINDOW_RIDE_PAGE_MUSIC
|
||
8, // WINDOW_RIDE_PAGE_MEASUREMENTS
|
||
8, // WINDOW_RIDE_PAGE_GRAPHS
|
||
2, // WINDOW_RIDE_PAGE_INCOME
|
||
0, // WINDOW_RIDE_PAGE_CUSTOMER
|
||
};
|
||
static_assert(std::size(window_ride_tab_animation_divisor) == WINDOW_RIDE_PAGE_COUNT);
|
||
|
||
static constexpr const int32_t window_ride_tab_animation_frames[] = {
|
||
0, // WINDOW_RIDE_PAGE_MAIN
|
||
0, // WINDOW_RIDE_PAGE_VEHICLE
|
||
4, // WINDOW_RIDE_PAGE_OPERATING
|
||
16, // WINDOW_RIDE_PAGE_MAINTENANCE
|
||
8, // WINDOW_RIDE_PAGE_COLOUR
|
||
16, // WINDOW_RIDE_PAGE_MUSIC
|
||
8, // WINDOW_RIDE_PAGE_MEASUREMENTS
|
||
8, // WINDOW_RIDE_PAGE_GRAPHS
|
||
8, // WINDOW_RIDE_PAGE_INCOME
|
||
0, // WINDOW_RIDE_PAGE_CUSTOMER
|
||
};
|
||
static_assert(std::size(window_ride_tab_animation_frames) == WINDOW_RIDE_PAGE_COUNT);
|
||
|
||
// clang-format off
|
||
static constexpr const rct_string_id RatingNames[] = {
|
||
STR_RATING_LOW,
|
||
STR_RATING_MEDIUM,
|
||
STR_RATING_HIGH,
|
||
STR_RATING_VERY_HIGH,
|
||
STR_RATING_EXTREME,
|
||
STR_RATING_ULTRA_EXTREME,
|
||
};
|
||
// clang-format on
|
||
|
||
static constexpr const rct_string_id RideBreakdownReasonNames[] = {
|
||
STR_RIDE_BREAKDOWN_SAFETY_CUT_OUT, // BREAKDOWN_SAFETY_CUT_OUT
|
||
STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_CLOSED, // BREAKDOWN_RESTRAINTS_STUCK_CLOSED
|
||
STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_OPEN, // BREAKDOWN_RESTRAINTS_STUCK_OPEN
|
||
STR_RIDE_BREAKDOWN_DOORS_STUCK_CLOSED, // BREAKDOWN_DOORS_STUCK_CLOSED
|
||
STR_RIDE_BREAKDOWN_DOORS_STUCK_OPEN, // BREAKDOWN_DOORS_STUCK_OPEN
|
||
STR_RIDE_BREAKDOWN_VEHICLE_MALFUNCTION, // BREAKDOWN_VEHICLE_MALFUNCTION
|
||
STR_RIDE_BREAKDOWN_BRAKES_FAILURE, // BREAKDOWN_BRAKES_FAILURE
|
||
STR_RIDE_BREAKDOWN_CONTROL_FAILURE, // BREAKDOWN_CONTROL_FAILURE
|
||
};
|
||
static_assert(std::size(RideBreakdownReasonNames) == BREAKDOWN_COUNT);
|
||
|
||
const rct_string_id ColourSchemeNames[] = {
|
||
STR_MAIN_COLOUR_SCHEME, // RIDE_COLOUR_SCHEME_MAIN
|
||
STR_ALTERNATIVE_COLOUR_SCHEME_1, // RIDE_COLOUR_SCHEME_ADDITIONAL_1
|
||
STR_ALTERNATIVE_COLOUR_SCHEME_2, // RIDE_COLOUR_SCHEME_ADDITIONAL_2
|
||
STR_ALTERNATIVE_COLOUR_SCHEME_3, // RIDE_COLOUR_SCHEME_ADDITIONAL_3
|
||
};
|
||
static_assert(std::size(ColourSchemeNames) == RIDE_COLOUR_SCHEME_COUNT);
|
||
|
||
static constexpr const rct_string_id VehicleLoadNames[] = {
|
||
STR_QUARTER_LOAD, // WAIT_FOR_LOAD_QUARTER
|
||
STR_HALF_LOAD, // WAIT_FOR_LOAD_HALF
|
||
STR_THREE_QUARTER_LOAD, // WAIT_FOR_LOAD_THREE_QUARTER
|
||
STR_FULL_LOAD, // WAIT_FOR_LOAD_FULL
|
||
STR_ANY_LOAD, // WAIT_FOR_LOAD_ANY
|
||
};
|
||
static_assert(std::size(VehicleLoadNames) == WAIT_FOR_LOAD_COUNT);
|
||
|
||
static constexpr const rct_string_id VehicleColourSchemeNames[] = {
|
||
STR_ALL_VEHICLES_IN_SAME_COLOURS, // RIDE_COLOUR_SCHEME_MODE_ALL_SAME,
|
||
STR_DIFFERENT_COLOURS_PER, // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_TRAIN,
|
||
STR_DIFFERENT_COLOURS_PER_VEHICLE, // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR,
|
||
};
|
||
static_assert(std::size(VehicleColourSchemeNames) == RIDE_COLOUR_SCHEME_MODE_COUNT);
|
||
|
||
static constexpr const rct_string_id VehicleStatusNames[] = {
|
||
STR_MOVING_TO_END_OF, // Vehicle::Status::MovingToEndOfStation
|
||
STR_WAITING_FOR_PASSENGERS_AT, // Vehicle::Status::WaitingForPassengers
|
||
STR_WAITING_TO_DEPART, // Vehicle::Status::WaitingToDepart
|
||
STR_DEPARTING, // Vehicle::Status::Departing
|
||
STR_TRAVELLING_AT_0, // Vehicle::Status::Travelling
|
||
STR_ARRIVING_AT, // Vehicle::Status::Arriving
|
||
STR_UNLOADING_PASSENGERS_AT, // Vehicle::Status::UnloadingPassengers
|
||
STR_TRAVELLING_AT_1, // Vehicle::Status::TravellingBoat
|
||
STR_CRASHING, // Vehicle::Status::Crashing
|
||
STR_CRASHED_0, // Vehicle::Status::Crashed
|
||
STR_TRAVELLING_AT_2, // Vehicle::Status::TravellingDodgems
|
||
STR_SWINGING, // Vehicle::Status::Swinging
|
||
STR_ROTATING_0, // Vehicle::Status::Rotating
|
||
STR_ROTATING_1, // Vehicle::Status::FerrisWheelRotating
|
||
STR_OPERATING_0, // Vehicle::Status::SimulatorOperating
|
||
STR_SHOWING_FILM, // Vehicle::Status::ShowingFilm
|
||
STR_ROTATING_2, // Vehicle::Status::SpaceRingsOperating
|
||
STR_OPERATING_1, // Vehicle::Status::TopSpinOperating
|
||
STR_OPERATING_2, // Vehicle::Status::HauntedHouseOperating
|
||
STR_DOING_CIRCUS_SHOW, // Vehicle::Status::DoingCircusShow
|
||
STR_OPERATING_3, // Vehicle::Status::CrookedHouseOperating
|
||
STR_WAITING_FOR_CABLE_LIFT, // Vehicle::Status::WaitingForCableLift
|
||
STR_TRAVELLING_AT_3, // Vehicle::Status::TravellingCableLift
|
||
STR_STOPPING_0, // Vehicle::Status::Stopping
|
||
STR_WAITING_FOR_PASSENGERS, // Vehicle::Status::WaitingForPassengers17
|
||
STR_WAITING_TO_START, // Vehicle::Status::WaitingToStart
|
||
STR_STARTING, // Vehicle::Status::Starting
|
||
STR_OPERATING, // Vehicle::Status::Operating1A
|
||
STR_STOPPING_1, // Vehicle::Status::Stopping1B
|
||
STR_UNLOADING_PASSENGERS, // Vehicle::Status::UnloadingPassengers1C
|
||
STR_STOPPED_BY_BLOCK_BRAKES, // Vehicle::Status::StoppedByBlockBrakes
|
||
};
|
||
|
||
static constexpr const rct_string_id SingleSessionVehicleStatusNames[] = {
|
||
STR_STOPPING_0, // Vehicle::Status::MovingToEndOfStation
|
||
STR_WAITING_FOR_PASSENGERS, // Vehicle::Status::WaitingForPassengers
|
||
STR_WAITING_TO_START, // Vehicle::Status::WaitingToDepart
|
||
STR_STARTING, // Vehicle::Status::Departing
|
||
STR_OPERATING, // Vehicle::Status::Travelling
|
||
STR_STOPPING_1, // Vehicle::Status::Arriving
|
||
STR_UNLOADING_PASSENGERS, // Vehicle::Status::UnloadingPassengers
|
||
};
|
||
|
||
struct WindowRideMazeDesignOption
|
||
{
|
||
rct_string_id text;
|
||
uint32_t sprite;
|
||
};
|
||
|
||
static constexpr const WindowRideMazeDesignOption MazeOptions[] = {
|
||
{ STR_RIDE_DESIGN_MAZE_BRICK_WALLS, SPR_RIDE_DESIGN_PREVIEW_MAZE_BRICK_WALLS },
|
||
{ STR_RIDE_DESIGN_MAZE_HEDGES, SPR_RIDE_DESIGN_PREVIEW_MAZE_HEDGES },
|
||
{ STR_RIDE_DESIGN_MAZE_ICE_BLOCKS, SPR_RIDE_DESIGN_PREVIEW_MAZE_ICE_BLOCKS },
|
||
{ STR_RIDE_DESIGN_MAZE_WOODEN_FENCES, SPR_RIDE_DESIGN_PREVIEW_MAZE_WOODEN_FENCES },
|
||
};
|
||
|
||
struct GraphsYAxis
|
||
{
|
||
uint8_t interval;
|
||
int8_t unit;
|
||
int8_t unit_interval;
|
||
rct_string_id label;
|
||
};
|
||
|
||
/** rct2: 0x0098DD98 */
|
||
static constexpr const GraphsYAxis window_graphs_y_axi[] = {
|
||
{ 11, 0, 10, STR_RIDE_STATS_VELOCITY_FORMAT }, // GRAPH_VELOCITY
|
||
{ 10, 0, 15, STR_RIDE_STATS_ALTITUDE_FORMAT }, // GRAPH_ALTITUDE
|
||
{ 13, -3, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_VERTICAL
|
||
{ 13, -4, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_LATERAL
|
||
};
|
||
|
||
static constexpr auto RIDE_G_FORCES_RED_NEG_VERTICAL = -FIXED_2DP(2, 50);
|
||
static constexpr auto RIDE_G_FORCES_RED_LATERAL = FIXED_2DP(2, 80);
|
||
|
||
// Used for sorting the ride type cheat dropdown.
|
||
struct RideTypeLabel
|
||
{
|
||
uint8_t ride_type_id;
|
||
rct_string_id label_id;
|
||
const char* label_string;
|
||
};
|
||
|
||
static int32_t RideDropdownDataLanguage = LANGUAGE_UNDEFINED;
|
||
static std::vector<RideTypeLabel> RideDropdownData;
|
||
|
||
// Used for sorting the vehicle type dropdown.
|
||
struct VehicleTypeLabel
|
||
{
|
||
ObjectEntryIndex subtype_id;
|
||
rct_string_id label_id;
|
||
const char* label_string;
|
||
};
|
||
|
||
static int32_t VehicleDropdownDataLanguage = LANGUAGE_UNDEFINED;
|
||
static rct_ride_entry* VehicleDropdownRideType = nullptr;
|
||
static bool VehicleDropdownExpanded = false;
|
||
static std::vector<VehicleTypeLabel> VehicleDropdownData;
|
||
|
||
static void WindowRideDrawTabImage(rct_drawpixelinfo* dpi, rct_window* w, int32_t page, int32_t spriteIndex)
|
||
{
|
||
rct_widgetindex widgetIndex = WIDX_TAB_1 + page;
|
||
|
||
if (!WidgetIsDisabled(*w, widgetIndex))
|
||
{
|
||
if (w->page == page)
|
||
{
|
||
int32_t frame = w->frame_no / window_ride_tab_animation_divisor[w->page];
|
||
spriteIndex += (frame % window_ride_tab_animation_frames[w->page]);
|
||
}
|
||
|
||
const auto& widget = w->widgets[widgetIndex];
|
||
gfx_draw_sprite(dpi, ImageId(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.left, widget.top });
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2E88
|
||
*/
|
||
static void WindowRideDrawTabMain(rct_drawpixelinfo* dpi, rct_window* w)
|
||
{
|
||
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_MAIN;
|
||
if (!WidgetIsDisabled(*w, widgetIndex))
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
int32_t spriteIndex = 0;
|
||
switch (ride->GetClassification())
|
||
{
|
||
case RideClassification::Ride:
|
||
spriteIndex = SPR_TAB_RIDE_0;
|
||
if (w->page == WINDOW_RIDE_PAGE_MAIN)
|
||
spriteIndex += (w->frame_no / 4) % 16;
|
||
break;
|
||
case RideClassification::ShopOrStall:
|
||
spriteIndex = SPR_TAB_SHOPS_AND_STALLS_0;
|
||
if (w->page == WINDOW_RIDE_PAGE_MAIN)
|
||
spriteIndex += (w->frame_no / 4) % 16;
|
||
break;
|
||
case RideClassification::KioskOrFacility:
|
||
spriteIndex = SPR_TAB_KIOSKS_AND_FACILITIES_0;
|
||
if (w->page == WINDOW_RIDE_PAGE_MAIN)
|
||
spriteIndex += (w->frame_no / 4) % 8;
|
||
break;
|
||
}
|
||
|
||
const auto& widget = w->widgets[widgetIndex];
|
||
gfx_draw_sprite(dpi, ImageId(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.left, widget.top });
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2B68
|
||
*/
|
||
static void WindowRideDrawTabVehicle(rct_drawpixelinfo* dpi, rct_window* w)
|
||
{
|
||
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_VEHICLE;
|
||
const auto& widget = w->widgets[widgetIndex];
|
||
|
||
if (!WidgetIsDisabled(*w, widgetIndex))
|
||
{
|
||
auto screenCoords = ScreenCoordsXY{ widget.left + 1, widget.top + 1 };
|
||
int32_t width = widget.right - screenCoords.x;
|
||
int32_t height = widget.bottom - 3 - screenCoords.y;
|
||
if (w->page == WINDOW_RIDE_PAGE_VEHICLE)
|
||
height += 4;
|
||
|
||
screenCoords += w->windowPos;
|
||
|
||
rct_drawpixelinfo clipDPI;
|
||
if (!clip_drawpixelinfo(&clipDPI, dpi, screenCoords, width, height))
|
||
{
|
||
return;
|
||
}
|
||
|
||
screenCoords = ScreenCoordsXY{ widget.width() / 2, widget.height() - 12 };
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF)
|
||
{
|
||
clipDPI.zoom_level = ZoomLevel{ 1 };
|
||
clipDPI.width *= 2;
|
||
clipDPI.height *= 2;
|
||
screenCoords.x *= 2;
|
||
screenCoords.y *= 2;
|
||
clipDPI.x *= 2;
|
||
clipDPI.y *= 2;
|
||
}
|
||
|
||
// For any suspended rides, move image higher in the vehicle tab on the rides window
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SUSPENDED))
|
||
{
|
||
screenCoords.y /= 4;
|
||
}
|
||
|
||
const auto vehicle = ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, rideEntry->TabCar);
|
||
CarEntry* carEntry = &rideEntry->Cars[vehicle];
|
||
|
||
auto vehicleId = ((ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_VEHICLE) ? rideEntry->TabCar : 0;
|
||
VehicleColour vehicleColour = ride_get_vehicle_colour(ride, vehicleId);
|
||
|
||
// imageIndex represents a precision of 64
|
||
auto imageIndex = OpenRCT2::Entity::Yaw::YawFrom4(2) * 2;
|
||
if (w->page == WINDOW_RIDE_PAGE_VEHICLE)
|
||
imageIndex += w->frame_no;
|
||
imageIndex = carEntry->SpriteByYaw(imageIndex / 2, SpriteGroupType::SlopeFlat);
|
||
imageIndex &= carEntry->TabRotationMask;
|
||
imageIndex *= carEntry->base_num_frames;
|
||
imageIndex += carEntry->base_image_id;
|
||
auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary);
|
||
gfx_draw_sprite(&clipDPI, imageId, screenCoords);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2F42
|
||
*/
|
||
static void WindowRideDrawTabCustomer(rct_drawpixelinfo* dpi, rct_window* w)
|
||
{
|
||
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_CUSTOMER;
|
||
|
||
if (!WidgetIsDisabled(*w, widgetIndex))
|
||
{
|
||
const auto& widget = w->widgets[widgetIndex];
|
||
int32_t spriteIndex = 0;
|
||
if (w->page == WINDOW_RIDE_PAGE_CUSTOMER)
|
||
spriteIndex = w->picked_peep_frame & ~3;
|
||
|
||
spriteIndex += GetPeepAnimation(PeepSpriteType::Normal).base_image;
|
||
spriteIndex += 1;
|
||
spriteIndex |= 0xA9E00000;
|
||
|
||
gfx_draw_sprite(
|
||
dpi, ImageId::FromUInt32(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.midX(), widget.bottom - 6 });
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2B35
|
||
*/
|
||
static void WindowRideDrawTabImages(rct_drawpixelinfo* dpi, rct_window* w)
|
||
{
|
||
WindowRideDrawTabVehicle(dpi, w);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_OPERATING, SPR_TAB_GEARS_0);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MAINTENANCE, SPR_TAB_WRENCH_0);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_INCOME, SPR_TAB_ADMISSION_0);
|
||
WindowRideDrawTabMain(dpi, w);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MEASUREMENTS, SPR_TAB_TIMER_0);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_COLOUR, SPR_TAB_PAINT_0);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_GRAPHS, SPR_TAB_GRAPH_A_0);
|
||
WindowRideDrawTabCustomer(dpi, w);
|
||
WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MUSIC, SPR_TAB_MUSIC_0);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEB9F
|
||
*/
|
||
static void WindowRideDisableTabs(rct_window* w)
|
||
{
|
||
uint32_t disabled_tabs = 0;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
const auto& rtd = ride->GetRideTypeDescriptor();
|
||
|
||
if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING))
|
||
disabled_tabs |= (1ULL << WIDX_TAB_8); // 0x800
|
||
|
||
if (ride->type == RIDE_TYPE_MINI_GOLF)
|
||
disabled_tabs |= (1ULL << WIDX_TAB_2 | 1ULL << WIDX_TAB_3 | 1ULL << WIDX_TAB_4); // 0xE0
|
||
|
||
if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
|
||
disabled_tabs |= (1ULL << WIDX_TAB_2); // 0x20
|
||
|
||
if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL)
|
||
&& !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS)
|
||
&& !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
|
||
{
|
||
disabled_tabs |= (1ULL << WIDX_TAB_5); // 0x100
|
||
}
|
||
|
||
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
|
||
disabled_tabs |= (1ULL << WIDX_TAB_3 | 1ULL << WIDX_TAB_4 | 1ULL << WIDX_TAB_7); // 0x4C0
|
||
|
||
if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC))
|
||
{
|
||
disabled_tabs |= (1ULL << WIDX_TAB_6); // 0x200
|
||
}
|
||
|
||
if (ride->type == RIDE_TYPE_CASH_MACHINE || ride->type == RIDE_TYPE_FIRST_AID || (gParkFlags & PARK_FLAGS_NO_MONEY) != 0)
|
||
disabled_tabs |= (1ULL << WIDX_TAB_9); // 0x1000
|
||
|
||
if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0)
|
||
disabled_tabs |= (1ULL << WIDX_TAB_4 | 1ULL << WIDX_TAB_6 | 1ULL << WIDX_TAB_9 | 1ULL << WIDX_TAB_10); // 0x3280
|
||
|
||
rct_ride_entry* rideEntry = get_ride_entry(ride->subtype);
|
||
|
||
if (rideEntry == nullptr)
|
||
{
|
||
disabled_tabs |= 1ULL << WIDX_TAB_2 | 1ULL << WIDX_TAB_3 | 1ULL << WIDX_TAB_4 | 1ULL << WIDX_TAB_5 | 1ULL << WIDX_TAB_6
|
||
| 1ULL << WIDX_TAB_7 | 1ULL << WIDX_TAB_8 | 1ULL << WIDX_TAB_9 | 1ULL << WIDX_TAB_10;
|
||
}
|
||
else if ((rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB) != 0)
|
||
{
|
||
disabled_tabs |= (1ULL << WIDX_TAB_5);
|
||
}
|
||
|
||
w->disabled_widgets = disabled_tabs;
|
||
}
|
||
|
||
static void WindowRideUpdateOverallView(Ride* ride)
|
||
{
|
||
// Calculate x, y, z bounds of the entire ride using its track elements
|
||
tile_element_iterator it;
|
||
|
||
tile_element_iterator_begin(&it);
|
||
|
||
CoordsXYZ min = { std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max(),
|
||
std::numeric_limits<int32_t>::max() };
|
||
CoordsXYZ max = { std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::min(),
|
||
std::numeric_limits<int32_t>::min() };
|
||
|
||
while (tile_element_iterator_next(&it))
|
||
{
|
||
if (it.element->GetType() != TileElementType::Track)
|
||
continue;
|
||
|
||
if (it.element->AsTrack()->GetRideIndex() != ride->id)
|
||
continue;
|
||
|
||
auto location = TileCoordsXY(it.x, it.y).ToCoordsXY();
|
||
int32_t baseZ = it.element->GetBaseZ();
|
||
int32_t clearZ = it.element->GetClearanceZ();
|
||
|
||
min.x = std::min(min.x, location.x);
|
||
min.y = std::min(min.y, location.y);
|
||
min.z = std::min(min.z, baseZ);
|
||
|
||
max.x = std::max(max.x, location.x);
|
||
max.y = std::max(max.y, location.y);
|
||
max.z = std::max(max.z, clearZ);
|
||
}
|
||
|
||
const auto rideIndex = ride->id.ToUnderlying();
|
||
if (rideIndex >= ride_overall_views.size())
|
||
{
|
||
ride_overall_views.resize(rideIndex + 1);
|
||
}
|
||
|
||
auto& view = ride_overall_views[rideIndex];
|
||
view.loc = CoordsXYZ{ (min.x + max.x) / 2, (min.y + max.y) / 2, (min.z + max.z) / 2 } + CoordsXYZ{ 16, 16, -8 };
|
||
|
||
// Calculate size to determine from how far away to view the ride
|
||
const auto diff = max - min;
|
||
|
||
const int32_t size = static_cast<int32_t>(std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z));
|
||
|
||
if (size >= 80)
|
||
{
|
||
// Each farther zoom level shows twice as many tiles (log)
|
||
// Appropriate zoom is lowered by one to fill the entire view with the ride
|
||
const auto zoomValue = static_cast<int8_t>(std::ceil(std::log(size / 80)) - 1);
|
||
view.zoom = std::clamp(ZoomLevel{ zoomValue }, ZoomLevel{ 0 }, ZoomLevel::max());
|
||
}
|
||
else
|
||
{
|
||
// Small rides or stalls are zoomed in all the way.
|
||
view.zoom = ZoomLevel{ 0 };
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEAB4
|
||
*/
|
||
static rct_window* WindowRideOpen(Ride* ride)
|
||
{
|
||
rct_window* w;
|
||
|
||
w = WindowCreateAutoPos(316, 207, window_ride_page_events[0], WC_RIDE, WF_10 | WF_RESIZABLE);
|
||
w->widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN];
|
||
w->hold_down_widgets = window_ride_page_hold_down_widgets[WINDOW_RIDE_PAGE_MAIN];
|
||
w->rideId = ride->id;
|
||
|
||
w->page = WINDOW_RIDE_PAGE_MAIN;
|
||
w->vehicleIndex = 0;
|
||
w->frame_no = 0;
|
||
w->list_information_type = 0;
|
||
w->picked_peep_frame = 0;
|
||
w->ride_colour = 0;
|
||
WindowRideDisableTabs(w);
|
||
w->min_width = 316;
|
||
w->min_height = 180;
|
||
w->max_width = 500;
|
||
w->max_height = 450;
|
||
|
||
WindowRideUpdateOverallView(ride);
|
||
|
||
PopulateVehicleTypeDropdown(ride, true);
|
||
|
||
return w;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ACC28
|
||
*/
|
||
rct_window* WindowRideMainOpen(Ride* ride)
|
||
{
|
||
if (ride->type >= RIDE_TYPE_COUNT)
|
||
{
|
||
return nullptr;
|
||
}
|
||
|
||
rct_window* w = window_bring_to_front_by_number(WC_RIDE, ride->id.ToUnderlying());
|
||
if (w == nullptr)
|
||
{
|
||
w = WindowRideOpen(ride);
|
||
w->ride.var_482 = -1;
|
||
w->ride.view = 0;
|
||
}
|
||
else if (w->ride.view >= (1 + ride->num_vehicles + ride->num_stations))
|
||
{
|
||
w->ride.view = 0;
|
||
}
|
||
|
||
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
|
||
{
|
||
if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number)
|
||
{
|
||
tool_cancel();
|
||
}
|
||
}
|
||
|
||
if (w->page != WINDOW_RIDE_PAGE_MAIN)
|
||
{
|
||
WindowRideSetPage(w, WINDOW_RIDE_PAGE_MAIN);
|
||
}
|
||
|
||
WindowRideInitViewport(w);
|
||
return w;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ACCCE
|
||
*/
|
||
static rct_window* WindowRideOpenStation(Ride* ride, StationIndex stationIndex)
|
||
{
|
||
if (ride->type >= RIDE_TYPE_COUNT)
|
||
return nullptr;
|
||
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
|
||
return WindowRideMainOpen(ride);
|
||
|
||
auto w = window_bring_to_front_by_number(WC_RIDE, ride->id.ToUnderlying());
|
||
if (w == nullptr)
|
||
{
|
||
w = WindowRideOpen(ride);
|
||
w->ride.var_482 = -1;
|
||
}
|
||
|
||
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification
|
||
&& gCurrentToolWidget.window_number == w->number)
|
||
{
|
||
tool_cancel();
|
||
}
|
||
|
||
w->page = WINDOW_RIDE_PAGE_MAIN;
|
||
w->width = 316;
|
||
w->height = 180;
|
||
w->Invalidate();
|
||
|
||
w->widgets = window_ride_page_widgets[w->page];
|
||
w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page];
|
||
w->event_handlers = window_ride_page_events[w->page];
|
||
w->pressed_widgets = 0;
|
||
WindowRideDisableTabs(w);
|
||
WindowInitScrollWidgets(*w);
|
||
|
||
// View
|
||
for (int32_t i = stationIndex.ToUnderlying(); i >= 0; i--)
|
||
{
|
||
if (ride->GetStations()[i].Start.IsNull())
|
||
{
|
||
stationIndex = StationIndex::FromUnderlying(stationIndex.ToUnderlying() - 1);
|
||
}
|
||
}
|
||
|
||
w->ride.view = 1 + ride->num_vehicles + stationIndex.ToUnderlying();
|
||
WindowRideInitViewport(w);
|
||
|
||
return w;
|
||
}
|
||
|
||
rct_window* WindowRideOpenTrack(TileElement* tileElement)
|
||
{
|
||
assert(tileElement != nullptr);
|
||
auto rideIndex = tileElement->GetRideIndex();
|
||
if (!rideIndex.IsNull())
|
||
{
|
||
auto ride = get_ride(rideIndex);
|
||
if (ride != nullptr)
|
||
{
|
||
const auto type = tileElement->GetType();
|
||
if (type == TileElementType::Entrance)
|
||
{
|
||
// Open ride window in station view
|
||
auto entranceElement = tileElement->AsEntrance();
|
||
auto stationIndex = entranceElement->GetStationIndex();
|
||
return WindowRideOpenStation(ride, stationIndex);
|
||
}
|
||
else if (type == TileElementType::Track)
|
||
{
|
||
// Open ride window in station view
|
||
auto trackElement = tileElement->AsTrack();
|
||
auto trackType = trackElement->GetTrackType();
|
||
const auto& ted = GetTrackElementDescriptor(trackType);
|
||
if (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN)
|
||
{
|
||
auto stationIndex = trackElement->GetStationIndex();
|
||
return WindowRideOpenStation(ride, stationIndex);
|
||
}
|
||
}
|
||
|
||
// Open ride window in overview mode
|
||
return WindowRideMainOpen(ride);
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ACAC2
|
||
*/
|
||
rct_window* WindowRideOpenVehicle(Vehicle* vehicle)
|
||
{
|
||
if (vehicle == nullptr)
|
||
return nullptr;
|
||
|
||
Vehicle* headVehicle = vehicle->TrainHead();
|
||
if (headVehicle == nullptr)
|
||
return nullptr;
|
||
|
||
EntityId headVehicleSpriteIndex = headVehicle->sprite_index;
|
||
auto ride = headVehicle->GetRide();
|
||
if (ride == nullptr)
|
||
return nullptr;
|
||
|
||
// Get view index
|
||
int32_t view = 1;
|
||
for (int32_t i = 0; i <= OpenRCT2::Limits::MaxTrainsPerRide; i++)
|
||
{
|
||
if (ride->vehicles[i] == headVehicleSpriteIndex)
|
||
break;
|
||
|
||
view++;
|
||
}
|
||
|
||
rct_window* w = window_find_by_number(WC_RIDE, ride->id.ToUnderlying());
|
||
if (w != nullptr)
|
||
{
|
||
w->Invalidate();
|
||
|
||
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification
|
||
&& gCurrentToolWidget.window_number == w->number)
|
||
{
|
||
tool_cancel();
|
||
}
|
||
|
||
int32_t openedPeepWindow = 0;
|
||
if (w->ride.view == view)
|
||
{
|
||
int32_t numPeepsLeft = vehicle->num_peeps;
|
||
for (int32_t i = 0; i < 32 && numPeepsLeft > 0; i++)
|
||
{
|
||
Peep* peep = GetEntity<Guest>(vehicle->peep[i]);
|
||
if (peep == nullptr)
|
||
continue;
|
||
|
||
numPeepsLeft--;
|
||
rct_window* w2 = window_find_by_number(WC_PEEP, vehicle->peep[i]);
|
||
if (w2 == nullptr)
|
||
{
|
||
auto intent = Intent(WC_PEEP);
|
||
intent.putExtra(INTENT_EXTRA_PEEP, peep);
|
||
context_open_intent(&intent);
|
||
openedPeepWindow = 1;
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
w = openedPeepWindow ? window_find_by_number(WC_RIDE, ride->id.ToUnderlying())
|
||
: window_bring_to_front_by_number(WC_RIDE, ride->id.ToUnderlying());
|
||
}
|
||
|
||
if (w == nullptr)
|
||
{
|
||
w = WindowRideOpen(ride);
|
||
w->ride.var_482 = -1;
|
||
}
|
||
|
||
w->page = WINDOW_RIDE_PAGE_MAIN;
|
||
w->width = 316;
|
||
w->height = 180;
|
||
w->Invalidate();
|
||
|
||
w->widgets = window_ride_page_widgets[w->page];
|
||
w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page];
|
||
w->event_handlers = window_ride_page_events[w->page];
|
||
w->pressed_widgets = 0;
|
||
WindowRideDisableTabs(w);
|
||
WindowInitScrollWidgets(*w);
|
||
|
||
w->ride.view = view;
|
||
WindowRideInitViewport(w);
|
||
w->Invalidate();
|
||
|
||
return w;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF1D2
|
||
*/
|
||
static void WindowRideSetPage(rct_window* w, int32_t page)
|
||
{
|
||
int32_t listen;
|
||
|
||
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
|
||
if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number)
|
||
tool_cancel();
|
||
|
||
if (page == WINDOW_RIDE_PAGE_VEHICLE)
|
||
{
|
||
auto constructionWindow = window_find_by_class(WC_RIDE_CONSTRUCTION);
|
||
if (constructionWindow != nullptr && constructionWindow->number == w->number)
|
||
{
|
||
window_close_by_class(WC_RIDE_CONSTRUCTION);
|
||
// Closing the construction window sets the tab to the first page, which we don't want here,
|
||
// as user just clicked the Vehicle page
|
||
WindowRideSetPage(w, WINDOW_RIDE_PAGE_VEHICLE);
|
||
}
|
||
}
|
||
|
||
// Set listen only to viewport
|
||
listen = 0;
|
||
if (page == WINDOW_RIDE_PAGE_MAIN && w->page == WINDOW_RIDE_PAGE_MAIN && w->viewport != nullptr
|
||
&& !(w->viewport->flags & VIEWPORT_FLAG_SOUND_ON))
|
||
listen++;
|
||
|
||
w->page = page;
|
||
w->frame_no = 0;
|
||
w->picked_peep_frame = 0;
|
||
|
||
// There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, so
|
||
// I've removed it if (page == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride);
|
||
//}
|
||
|
||
w->RemoveViewport();
|
||
|
||
w->hold_down_widgets = window_ride_page_hold_down_widgets[page];
|
||
w->event_handlers = window_ride_page_events[page];
|
||
w->pressed_widgets = 0;
|
||
w->widgets = window_ride_page_widgets[page];
|
||
WindowRideDisableTabs(w);
|
||
w->Invalidate();
|
||
|
||
window_event_resize_call(w);
|
||
window_event_invalidate_call(w);
|
||
WindowInitScrollWidgets(*w);
|
||
w->Invalidate();
|
||
|
||
if (listen != 0 && w->viewport != nullptr)
|
||
w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON;
|
||
}
|
||
|
||
static void WindowRideSetPressedTab(rct_window* w)
|
||
{
|
||
int32_t i;
|
||
for (i = 0; i < WINDOW_RIDE_PAGE_COUNT; i++)
|
||
w->pressed_widgets &= ~(1 << (WIDX_TAB_1 + i));
|
||
w->pressed_widgets |= 1LL << (WIDX_TAB_1 + w->page);
|
||
}
|
||
|
||
static void WindowRideAnchorBorderWidgets(rct_window* w)
|
||
{
|
||
w->widgets[WIDX_BACKGROUND].right = w->width - 1;
|
||
w->widgets[WIDX_BACKGROUND].bottom = w->height - 1;
|
||
w->widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1;
|
||
w->widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1;
|
||
w->widgets[WIDX_TITLE].right = w->width - 2;
|
||
w->widgets[WIDX_CLOSE].left = w->width - 13;
|
||
w->widgets[WIDX_CLOSE].right = w->width - 3;
|
||
}
|
||
|
||
#pragma region Main
|
||
|
||
static std::optional<StationIndex> GetStationIndexFromViewSelection(const rct_window& w)
|
||
{
|
||
const auto* ride = get_ride(RideId::FromUnderlying(w.number));
|
||
if (ride == nullptr)
|
||
return std::nullopt;
|
||
|
||
int32_t viewSelectionIndex = w.ride.view - 1 - ride->num_vehicles;
|
||
if (viewSelectionIndex < 0)
|
||
{
|
||
return std::nullopt;
|
||
}
|
||
|
||
for (const auto& station : ride->GetStations())
|
||
{
|
||
if (!station.Start.IsNull() && viewSelectionIndex-- == 0)
|
||
{
|
||
const auto stationIndex = ride->GetStationIndex(&station);
|
||
return std::make_optional(stationIndex);
|
||
}
|
||
}
|
||
return std::nullopt;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF994
|
||
*/
|
||
static void WindowRideInitViewport(rct_window* w)
|
||
{
|
||
if (w->page != WINDOW_RIDE_PAGE_MAIN)
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
int32_t viewSelectionIndex = w->ride.view - 1;
|
||
|
||
std::optional<Focus> focus;
|
||
|
||
if (viewSelectionIndex >= 0 && viewSelectionIndex < ride->num_vehicles && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)
|
||
{
|
||
auto vehId = ride->vehicles[viewSelectionIndex];
|
||
rct_ride_entry* ride_entry = ride->GetRideEntry();
|
||
if (ride_entry != nullptr && ride_entry->TabCar != 0)
|
||
{
|
||
Vehicle* vehicle = GetEntity<Vehicle>(vehId);
|
||
if (vehicle == nullptr)
|
||
{
|
||
vehId = EntityId::GetNull();
|
||
}
|
||
else if (!vehicle->next_vehicle_on_train.IsNull())
|
||
{
|
||
vehId = vehicle->next_vehicle_on_train;
|
||
}
|
||
}
|
||
if (!vehId.IsNull())
|
||
{
|
||
focus = Focus(vehId);
|
||
}
|
||
}
|
||
else if (viewSelectionIndex >= ride->num_vehicles && viewSelectionIndex < (ride->num_vehicles + ride->num_stations))
|
||
{
|
||
auto stationIndex = GetStationIndexFromViewSelection(*w);
|
||
if (stationIndex)
|
||
{
|
||
const auto location = ride->GetStation(*stationIndex).GetStart();
|
||
focus = Focus(location);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (viewSelectionIndex > 0)
|
||
{
|
||
w->ride.view = 0;
|
||
}
|
||
if (w->number < ride_overall_views.size())
|
||
{
|
||
const auto& view = ride_overall_views[w->number];
|
||
focus = Focus(view.loc, view.zoom);
|
||
}
|
||
}
|
||
|
||
uint16_t viewport_flags = 0;
|
||
if (w->viewport != nullptr)
|
||
{
|
||
if (focus == w->focus)
|
||
{
|
||
return;
|
||
}
|
||
viewport_flags = w->viewport->flags;
|
||
w->RemoveViewport();
|
||
}
|
||
else if (gConfigGeneral.always_show_gridlines)
|
||
{
|
||
viewport_flags |= VIEWPORT_FLAG_GRIDLINES;
|
||
}
|
||
|
||
window_event_invalidate_call(w);
|
||
|
||
w->focus = focus;
|
||
|
||
// rct2: 0x006aec9c only used here so brought it into the function
|
||
if (w->viewport == nullptr && !ride->overall_view.IsNull() && w->focus.has_value())
|
||
{
|
||
const auto& view_widget = w->widgets[WIDX_VIEWPORT];
|
||
|
||
auto screenPos = w->windowPos + ScreenCoordsXY{ view_widget.left + 1, view_widget.top + 1 };
|
||
int32_t width = view_widget.width() - 1;
|
||
int32_t height = view_widget.height() - 1;
|
||
|
||
viewport_create(w, screenPos, width, height, w->focus.value());
|
||
|
||
w->flags |= WF_NO_SCROLLING;
|
||
w->Invalidate();
|
||
}
|
||
if (w->viewport != nullptr)
|
||
{
|
||
w->viewport->flags = viewport_flags;
|
||
w->Invalidate();
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF315
|
||
*/
|
||
static void WindowRideRename(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
auto rideName = ride->GetName();
|
||
WindowTextInputRawOpen(
|
||
w, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, {}, rideName.c_str(), 32);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF17E
|
||
*/
|
||
static void WindowRideMainMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_CONSTRUCTION:
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
ride_construct(ride);
|
||
if (window_find_by_number(WC_RIDE_CONSTRUCTION, ride->id.ToUnderlying()) != nullptr)
|
||
{
|
||
window_close(w);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case WIDX_RENAME:
|
||
WindowRideRename(w);
|
||
break;
|
||
case WIDX_DEMOLISH:
|
||
context_open_detail_window(WD_DEMOLISH_RIDE, w->number);
|
||
break;
|
||
case WIDX_CLOSE_LIGHT:
|
||
case WIDX_SIMULATE_LIGHT:
|
||
case WIDX_TEST_LIGHT:
|
||
case WIDX_OPEN_LIGHT:
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
RideStatus status;
|
||
switch (widgetIndex)
|
||
{
|
||
default:
|
||
case WIDX_CLOSE_LIGHT:
|
||
status = RideStatus::Closed;
|
||
break;
|
||
case WIDX_SIMULATE_LIGHT:
|
||
status = RideStatus::Simulating;
|
||
break;
|
||
case WIDX_TEST_LIGHT:
|
||
status = RideStatus::Testing;
|
||
break;
|
||
case WIDX_OPEN_LIGHT:
|
||
status = RideStatus::Open;
|
||
break;
|
||
}
|
||
ride_set_status(ride, status);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF4A2
|
||
*/
|
||
static void WindowRideMainResize(rct_window* w)
|
||
{
|
||
int32_t minHeight = 180;
|
||
if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
|
||
{
|
||
minHeight += 20 + RCT1_LIGHT_OFFSET;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
#ifdef __SIMULATE_IN_RIDE_WINDOW__
|
||
if (ride->SupportsStatus(RideStatus::Simulating))
|
||
{
|
||
minHeight += 14;
|
||
}
|
||
#endif
|
||
if (ride->SupportsStatus(RideStatus::Testing))
|
||
{
|
||
minHeight += 14;
|
||
}
|
||
}
|
||
}
|
||
if (gCheatsAllowArbitraryRideTypeChanges)
|
||
{
|
||
minHeight += 15;
|
||
}
|
||
|
||
w->flags |= WF_RESIZABLE;
|
||
window_set_resize(w, 316, minHeight, 500, 450);
|
||
// Unlike with other windows, the focus needs to be recentred so it’s best to just reset it.
|
||
w->focus = std::nullopt;
|
||
WindowRideInitViewport(w);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF825
|
||
*/
|
||
static void WindowRideShowViewDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
rct_widget* dropdownWidget = widget - 1;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
int32_t numItems = 1;
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
|
||
{
|
||
numItems += ride->num_stations;
|
||
numItems += ride->num_vehicles;
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, 0, numItems, widget->right - dropdownWidget->left);
|
||
|
||
// First item
|
||
gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[0].Args = STR_OVERALL_VIEW;
|
||
int32_t currentItem = 1;
|
||
|
||
const auto& rtd = ride->GetRideTypeDescriptor();
|
||
|
||
// Vehicles
|
||
int32_t name = GetRideComponentName(rtd.NameConvention.vehicle).number;
|
||
for (int32_t i = 1; i <= ride->num_vehicles; i++)
|
||
{
|
||
gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[currentItem].Args = name | (currentItem << 16);
|
||
currentItem++;
|
||
}
|
||
|
||
// Stations
|
||
name = GetRideComponentName(rtd.NameConvention.station).number;
|
||
for (int32_t i = 1; i <= ride->num_stations; i++)
|
||
{
|
||
gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[currentItem].Args = name | (i << 16);
|
||
currentItem++;
|
||
}
|
||
|
||
// Set highlighted item
|
||
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
|
||
{
|
||
for (int32_t i = 0; i < ride->num_vehicles; i++)
|
||
{
|
||
// The +1 is to skip 'Overall view'
|
||
Dropdown::SetDisabled(i + 1, true);
|
||
}
|
||
}
|
||
|
||
// Set checked item
|
||
Dropdown::SetChecked(w->ride.view, true);
|
||
}
|
||
|
||
static RideStatus WindowRideGetNextDefaultStatus(const Ride* ride)
|
||
{
|
||
switch (ride->status)
|
||
{
|
||
default:
|
||
case RideStatus::Closed:
|
||
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)
|
||
|| (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE))
|
||
{
|
||
return RideStatus::Closed;
|
||
}
|
||
if (ride->SupportsStatus(RideStatus::Testing) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
|
||
{
|
||
return RideStatus::Testing;
|
||
}
|
||
return RideStatus::Open;
|
||
case RideStatus::Simulating:
|
||
return RideStatus::Testing;
|
||
case RideStatus::Testing:
|
||
return (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RideStatus::Open : RideStatus::Closed;
|
||
case RideStatus::Open:
|
||
return RideStatus::Closed;
|
||
}
|
||
}
|
||
|
||
struct RideStatusDropdownInfo
|
||
{
|
||
struct Ride* Ride{};
|
||
RideStatus CurrentStatus{};
|
||
RideStatus DefaultStatus{};
|
||
|
||
int32_t NumItems{};
|
||
int32_t CheckedIndex = -1;
|
||
int32_t DefaultIndex = -1;
|
||
};
|
||
|
||
static void WindowRideSetDropdown(RideStatusDropdownInfo& info, RideStatus status, rct_string_id text)
|
||
{
|
||
if (info.Ride->SupportsStatus(status))
|
||
{
|
||
auto index = info.NumItems;
|
||
gDropdownItems[index].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[index].Args = text;
|
||
if (info.CurrentStatus == status)
|
||
{
|
||
info.CheckedIndex = index;
|
||
}
|
||
if (info.DefaultStatus == status)
|
||
{
|
||
info.DefaultIndex = index;
|
||
}
|
||
info.NumItems++;
|
||
}
|
||
}
|
||
|
||
static void WindowRideShowOpenDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
RideStatusDropdownInfo info;
|
||
info.Ride = get_ride(w->rideId);
|
||
if (info.Ride == nullptr)
|
||
return;
|
||
|
||
info.CurrentStatus = info.Ride->status;
|
||
info.DefaultStatus = WindowRideGetNextDefaultStatus(info.Ride);
|
||
WindowRideSetDropdown(info, RideStatus::Closed, STR_CLOSE_RIDE);
|
||
#ifdef __SIMULATE_IN_RIDE_WINDOW__
|
||
window_ride_set_dropdown(info, RideStatus::Simulating, STR_SIMULATE_RIDE);
|
||
#endif
|
||
WindowRideSetDropdown(info, RideStatus::Testing, STR_TEST_RIDE);
|
||
WindowRideSetDropdown(info, RideStatus::Open, STR_OPEN_RIDE);
|
||
WindowDropdownShowText(
|
||
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, info.NumItems);
|
||
Dropdown::SetChecked(info.CheckedIndex, true);
|
||
gDropdownDefaultIndex = info.DefaultIndex;
|
||
}
|
||
|
||
static rct_string_id GetRideTypeNameForDropdown(uint8_t rideType)
|
||
{
|
||
switch (rideType)
|
||
{
|
||
case RIDE_TYPE_1D:
|
||
return STR_RIDE_NAME_1D;
|
||
case RIDE_TYPE_1F:
|
||
return STR_RIDE_NAME_1F;
|
||
case RIDE_TYPE_22:
|
||
return STR_RIDE_NAME_22;
|
||
case RIDE_TYPE_50:
|
||
return STR_RIDE_NAME_50;
|
||
case RIDE_TYPE_52:
|
||
return STR_RIDE_NAME_52;
|
||
case RIDE_TYPE_53:
|
||
return STR_RIDE_NAME_53;
|
||
case RIDE_TYPE_54:
|
||
return STR_RIDE_NAME_54;
|
||
case RIDE_TYPE_55:
|
||
return STR_RIDE_NAME_55;
|
||
case RIDE_TYPE_59:
|
||
return STR_RIDE_NAME_59;
|
||
default:
|
||
return GetRideTypeDescriptor(rideType).Naming.Name;
|
||
}
|
||
}
|
||
|
||
static void PopulateRideTypeDropdown()
|
||
{
|
||
auto& ls = OpenRCT2::GetContext()->GetLocalisationService();
|
||
if (RideDropdownDataLanguage == ls.GetCurrentLanguage())
|
||
return;
|
||
|
||
RideDropdownData.clear();
|
||
|
||
for (uint8_t i = 0; i < RIDE_TYPE_COUNT; i++)
|
||
{
|
||
auto name = GetRideTypeNameForDropdown(i);
|
||
RideDropdownData.push_back({ i, name, ls.GetString(name) });
|
||
}
|
||
|
||
std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) {
|
||
return String::Compare(a.label_string, b.label_string, true) < 0;
|
||
});
|
||
|
||
RideDropdownDataLanguage = ls.GetCurrentLanguage();
|
||
}
|
||
|
||
static void WindowRideShowRideTypeDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
PopulateRideTypeDropdown();
|
||
|
||
for (size_t i = 0; i < RideDropdownData.size(); i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = RideDropdownData[i].label_id;
|
||
}
|
||
|
||
rct_widget* dropdownWidget = widget - 1;
|
||
WindowDropdownShowText(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], Dropdown::Flag::StayOpen, RIDE_TYPE_COUNT);
|
||
|
||
// Find the current ride type in the ordered list.
|
||
int32_t pos = 0;
|
||
for (int32_t i = 0; i < RIDE_TYPE_COUNT; i++)
|
||
{
|
||
if (RideDropdownData[i].ride_type_id == ride->type)
|
||
{
|
||
pos = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
gDropdownHighlightedIndex = pos;
|
||
gDropdownDefaultIndex = pos;
|
||
Dropdown::SetChecked(pos, true);
|
||
}
|
||
|
||
static void WindowRideShowLocateDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
gDropdownItems[0].Format = STR_LOCATE_SUBJECT_TIP;
|
||
gDropdownItems[1].Format = STR_FOLLOW_SUBJECT_TIP;
|
||
|
||
WindowDropdownShowText(
|
||
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, 2);
|
||
gDropdownDefaultIndex = 0;
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK) || w->ride.view == 0
|
||
|| w->ride.view > ride->num_vehicles)
|
||
{
|
||
// Disable if we're a flat ride, 'overall view' is selected or a station is selected
|
||
Dropdown::SetDisabled(1, true);
|
||
}
|
||
}
|
||
|
||
static void WindowRideMainFollowRide(rct_window* w)
|
||
{
|
||
auto* ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN))
|
||
{
|
||
if (w->ride.view > 0)
|
||
{
|
||
if (w->ride.view <= ride->num_vehicles)
|
||
{
|
||
Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[w->ride.view - 1]);
|
||
if (vehicle != nullptr)
|
||
{
|
||
auto headVehicleSpriteIndex = vehicle->sprite_index;
|
||
rct_window* w_main = window_get_main();
|
||
window_follow_sprite(w_main, headVehicleSpriteIndex);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void PopulateVehicleTypeDropdown(Ride* ride, bool forceRefresh)
|
||
{
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
rct_ride_entry* rideEntry = ride->GetRideEntry();
|
||
|
||
bool selectionShouldBeExpanded;
|
||
int32_t rideTypeIterator, rideTypeIteratorMax;
|
||
if (gCheatsShowVehiclesFromOtherTrackTypes
|
||
&& !(
|
||
ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) || ride->type == RIDE_TYPE_MAZE
|
||
|| ride->type == RIDE_TYPE_MINI_GOLF))
|
||
{
|
||
selectionShouldBeExpanded = true;
|
||
rideTypeIterator = 0;
|
||
rideTypeIteratorMax = RIDE_TYPE_COUNT - 1;
|
||
}
|
||
else
|
||
{
|
||
selectionShouldBeExpanded = false;
|
||
rideTypeIterator = ride->type;
|
||
rideTypeIteratorMax = ride->type;
|
||
}
|
||
|
||
// Don't repopulate the list if we just did.
|
||
auto& ls = OpenRCT2::GetContext()->GetLocalisationService();
|
||
if (!forceRefresh && VehicleDropdownExpanded == selectionShouldBeExpanded && VehicleDropdownRideType == rideEntry
|
||
&& VehicleDropdownDataLanguage == ls.GetCurrentLanguage())
|
||
return;
|
||
|
||
VehicleDropdownData.clear();
|
||
|
||
for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++)
|
||
{
|
||
if (selectionShouldBeExpanded && GetRideTypeDescriptor(rideTypeIterator).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
||
continue;
|
||
if (selectionShouldBeExpanded && (rideTypeIterator == RIDE_TYPE_MAZE || rideTypeIterator == RIDE_TYPE_MINI_GOLF))
|
||
continue;
|
||
|
||
auto& rideEntries = objManager.GetAllRideEntries(rideTypeIterator);
|
||
for (auto rideEntryIndex : rideEntries)
|
||
{
|
||
const auto* currentRideEntry = get_ride_entry(rideEntryIndex);
|
||
if (currentRideEntry == nullptr)
|
||
continue;
|
||
|
||
// Skip if vehicle type has not been invented yet
|
||
if (!ride_entry_is_invented(rideEntryIndex) && !gCheatsIgnoreResearchStatus)
|
||
continue;
|
||
|
||
VehicleDropdownData.push_back(
|
||
{ rideEntryIndex, currentRideEntry->naming.Name, ls.GetString(currentRideEntry->naming.Name) });
|
||
}
|
||
}
|
||
|
||
std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) {
|
||
return String::Compare(a.label_string, b.label_string, true) < 0;
|
||
});
|
||
|
||
VehicleDropdownExpanded = selectionShouldBeExpanded;
|
||
VehicleDropdownRideType = rideEntry;
|
||
VehicleDropdownDataLanguage = ls.GetCurrentLanguage();
|
||
}
|
||
|
||
static void WindowRideShowVehicleTypeDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
PopulateVehicleTypeDropdown(ride);
|
||
|
||
size_t numItems = std::min<size_t>(VehicleDropdownData.size(), Dropdown::ItemsMaxSize);
|
||
|
||
for (size_t i = 0; i < numItems; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = VehicleDropdownData[i].label_id;
|
||
}
|
||
|
||
rct_widget* dropdownWidget = widget - 1;
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
|
||
|
||
// Find the current vehicle type in the ordered list.
|
||
int32_t pos = 0;
|
||
for (int32_t i = 0; i < static_cast<int32_t>(VehicleDropdownData.size()); i++)
|
||
{
|
||
if (VehicleDropdownData[i].subtype_id == ride->subtype)
|
||
{
|
||
pos = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
gDropdownHighlightedIndex = pos;
|
||
gDropdownDefaultIndex = pos;
|
||
Dropdown::SetChecked(pos, true);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF1BD
|
||
*/
|
||
static void WindowRideMainMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_VIEW_DROPDOWN:
|
||
WindowRideShowViewDropdown(w, widget);
|
||
break;
|
||
case WIDX_OPEN:
|
||
WindowRideShowOpenDropdown(w, widget);
|
||
break;
|
||
case WIDX_RIDE_TYPE_DROPDOWN:
|
||
WindowRideShowRideTypeDropdown(w, widget);
|
||
break;
|
||
case WIDX_LOCATE:
|
||
WindowRideShowLocateDropdown(w, widget);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF300
|
||
*/
|
||
static void WindowRideMainDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_VIEW_DROPDOWN:
|
||
if (dropdownIndex == -1)
|
||
{
|
||
dropdownIndex = w->ride.view + 1;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
if (dropdownIndex != 0 && dropdownIndex <= ride->num_vehicles
|
||
&& !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
|
||
{
|
||
dropdownIndex = ride->num_vehicles + 1;
|
||
}
|
||
if (dropdownIndex >= gDropdownNumItems)
|
||
{
|
||
dropdownIndex = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
w->ride.view = dropdownIndex;
|
||
WindowRideInitViewport(w);
|
||
w->Invalidate();
|
||
break;
|
||
case WIDX_OPEN:
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
auto status = RideStatus::Closed;
|
||
if (dropdownIndex < 0)
|
||
{
|
||
dropdownIndex = gDropdownHighlightedIndex;
|
||
}
|
||
if (dropdownIndex < static_cast<int32_t>(std::size(gDropdownItems)))
|
||
{
|
||
switch (gDropdownItems[dropdownIndex].Args)
|
||
{
|
||
case STR_CLOSE_RIDE:
|
||
status = RideStatus::Closed;
|
||
break;
|
||
case STR_SIMULATE_RIDE:
|
||
status = RideStatus::Simulating;
|
||
break;
|
||
case STR_TEST_RIDE:
|
||
status = RideStatus::Testing;
|
||
break;
|
||
case STR_OPEN_RIDE:
|
||
status = RideStatus::Open;
|
||
break;
|
||
}
|
||
}
|
||
ride_set_status(ride, status);
|
||
}
|
||
break;
|
||
}
|
||
case WIDX_RIDE_TYPE_DROPDOWN:
|
||
if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT)
|
||
{
|
||
auto rideLabelId = std::clamp(dropdownIndex, 0, RIDE_TYPE_COUNT - 1);
|
||
auto rideType = RideDropdownData[rideLabelId].ride_type_id;
|
||
if (rideType < RIDE_TYPE_COUNT)
|
||
{
|
||
auto rideSetSetting = RideSetSettingAction(w->rideId, RideSetSetting::RideType, rideType);
|
||
rideSetSetting.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
|
||
// Reset ghost track if ride construction window is open, prevents a crash
|
||
// Will get set to the correct Alternative variable during set_default_next_piece.
|
||
// TODO: Rework construction window to prevent the need for this.
|
||
_currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES;
|
||
ride_construction_set_default_next_piece();
|
||
});
|
||
GameActions::Execute(&rideSetSetting);
|
||
}
|
||
}
|
||
break;
|
||
case WIDX_LOCATE:
|
||
{
|
||
if (dropdownIndex == 0)
|
||
{
|
||
w->ScrollToViewport();
|
||
}
|
||
else if (dropdownIndex == 1)
|
||
{
|
||
WindowRideMainFollowRide(w);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF40F
|
||
*/
|
||
static void WindowRideMainUpdate(rct_window* w)
|
||
{
|
||
// Update tab animation
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_1);
|
||
|
||
// Update status
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN))
|
||
{
|
||
if (w->ride.view == 0)
|
||
return;
|
||
|
||
if (w->ride.view <= ride->num_vehicles)
|
||
{
|
||
Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[w->ride.view - 1]);
|
||
if (vehicle == nullptr
|
||
|| (vehicle->status != Vehicle::Status::Travelling
|
||
&& vehicle->status != Vehicle::Status::TravellingCableLift
|
||
&& vehicle->status != Vehicle::Status::TravellingDodgems
|
||
&& vehicle->status != Vehicle::Status::TravellingBoat))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN;
|
||
}
|
||
widget_invalidate(w, WIDX_STATUS);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF2F9
|
||
*/
|
||
static void WindowRideMainTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
|
||
{
|
||
if (widgetIndex != WIDX_RENAME || text == nullptr)
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
ride_set_name(ride, text, 0);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF55A
|
||
*/
|
||
static void WindowRideMainViewportRotate(rct_window* w)
|
||
{
|
||
WindowRideInitViewport(w);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AECF6
|
||
*/
|
||
static void WindowRideMainInvalidate(rct_window* w)
|
||
{
|
||
rct_widget* widgets;
|
||
int32_t i, height;
|
||
|
||
widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
w->disabled_widgets &= ~((1ULL << WIDX_DEMOLISH) | (1ULL << WIDX_CONSTRUCTION));
|
||
if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK))
|
||
w->disabled_widgets |= (1ULL << WIDX_DEMOLISH);
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
uint32_t spriteIds[] = {
|
||
SPR_CLOSED,
|
||
SPR_OPEN,
|
||
SPR_TESTING,
|
||
SPR_G2_SIMULATE,
|
||
};
|
||
window_ride_main_widgets[WIDX_OPEN].image = spriteIds[EnumValue(ride->status)];
|
||
|
||
#ifdef __SIMULATE_IN_RIDE_WINDOW__
|
||
window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2
|
||
+ WidgetIsPressed(*w, WIDX_CLOSE_LIGHT);
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0
|
||
+ (ride->status == RideStatus::Simulating) * 2 + WidgetIsPressed(*w, WIDX_SIMULATE_LIGHT);
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RideStatus::Testing) * 2
|
||
+ WidgetIsPressed(*w, WIDX_TEST_LIGHT);
|
||
#else
|
||
window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2
|
||
+ WidgetIsPressed(*w, WIDX_CLOSE_LIGHT);
|
||
|
||
auto baseSprite = ride->status == RideStatus::Simulating ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0;
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].image = baseSprite
|
||
+ (ride->status == RideStatus::Testing || ride->status == RideStatus::Simulating) * 2
|
||
+ WidgetIsPressed(*w, WIDX_TEST_LIGHT);
|
||
#endif
|
||
window_ride_main_widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RideStatus::Open) * 2
|
||
+ WidgetIsPressed(*w, WIDX_OPEN_LIGHT);
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
|
||
const int32_t offset = gCheatsAllowArbitraryRideTypeChanges ? 15 : 0;
|
||
// Anchor main page specific widgets
|
||
window_ride_main_widgets[WIDX_VIEWPORT].right = w->width - 26;
|
||
window_ride_main_widgets[WIDX_VIEWPORT].bottom = w->height - (14 + offset);
|
||
window_ride_main_widgets[WIDX_STATUS].right = w->width - 26;
|
||
window_ride_main_widgets[WIDX_STATUS].top = w->height - (13 + offset);
|
||
window_ride_main_widgets[WIDX_STATUS].bottom = w->height - (3 + offset);
|
||
window_ride_main_widgets[WIDX_VIEW].right = w->width - 60;
|
||
window_ride_main_widgets[WIDX_VIEW_DROPDOWN].right = w->width - 61;
|
||
window_ride_main_widgets[WIDX_VIEW_DROPDOWN].left = w->width - 71;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].right = w->width - 26;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].top = w->height - 17;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].bottom = w->height - 4;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].left = w->width - 37;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].right = w->width - 27;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].top = w->height - 16;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].bottom = w->height - 5;
|
||
|
||
if (!gCheatsAllowArbitraryRideTypeChanges)
|
||
{
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
else
|
||
{
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name;
|
||
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button;
|
||
}
|
||
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
|
||
if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
|
||
{
|
||
window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn;
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty;
|
||
#ifdef __SIMULATE_IN_RIDE_WINDOW__
|
||
if (ride->SupportsStatus(RideStatus::Simulating))
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn;
|
||
#endif
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RideStatus::Testing) ? WindowWidgetType::ImgBtn
|
||
: WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn;
|
||
|
||
height = 62;
|
||
if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty)
|
||
{
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = height;
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = height + 13;
|
||
height += 14;
|
||
}
|
||
if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty)
|
||
{
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].top = height;
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].bottom = height + 13;
|
||
height += 14;
|
||
}
|
||
window_ride_main_widgets[WIDX_OPEN_LIGHT].top = height;
|
||
window_ride_main_widgets[WIDX_OPEN_LIGHT].bottom = height + 13;
|
||
height += 14 - 24 + RCT1_LIGHT_OFFSET;
|
||
}
|
||
else
|
||
{
|
||
window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn;
|
||
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty;
|
||
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty;
|
||
height = 46;
|
||
}
|
||
for (i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++)
|
||
{
|
||
window_ride_main_widgets[i].left = w->width - 20;
|
||
window_ride_main_widgets[i].right = w->width - 7;
|
||
}
|
||
for (i = WIDX_OPEN; i <= WIDX_DEMOLISH; i++, height += 24)
|
||
{
|
||
window_ride_main_widgets[i].left = w->width - 25;
|
||
window_ride_main_widgets[i].right = w->width - 2;
|
||
window_ride_main_widgets[i].top = height;
|
||
window_ride_main_widgets[i].bottom = height + 23;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF10A
|
||
*/
|
||
static rct_string_id WindowRideGetStatusOverallView(rct_window* w, Formatter& ft)
|
||
{
|
||
auto stringId = STR_NONE;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
ride->FormatStatusTo(ft);
|
||
stringId = STR_BLACK_STRING;
|
||
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
|
||
{
|
||
stringId = STR_RED_OUTLINED_STRING;
|
||
}
|
||
}
|
||
return stringId;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEFEF
|
||
*/
|
||
static rct_string_id WindowRideGetStatusVehicle(rct_window* w, Formatter& ft)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return STR_EMPTY;
|
||
|
||
auto vehicle = GetEntity<Vehicle>(ride->vehicles[w->ride.view - 1]);
|
||
if (vehicle == nullptr)
|
||
return STR_EMPTY;
|
||
|
||
if (vehicle->status != Vehicle::Status::Crashing && vehicle->status != Vehicle::Status::Crashed)
|
||
{
|
||
auto trackType = vehicle->GetTrackType();
|
||
if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::CableLiftHill
|
||
|| trackType == TrackElemType::Up25ToFlat || trackType == TrackElemType::Up60ToFlat
|
||
|| trackType == TrackElemType::DiagUp25ToFlat || trackType == TrackElemType::DiagUp60ToFlat)
|
||
{
|
||
if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_BLOCK_BRAKES) && vehicle->velocity == 0)
|
||
{
|
||
ft.Add<rct_string_id>(STR_STOPPED_BY_BLOCK_BRAKES);
|
||
return STR_BLACK_STRING;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ride->type == RIDE_TYPE_MINI_GOLF)
|
||
return STR_EMPTY;
|
||
|
||
auto stringId = VehicleStatusNames[static_cast<size_t>(vehicle->status)];
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION)
|
||
&& vehicle->status <= Vehicle::Status::UnloadingPassengers)
|
||
{
|
||
stringId = SingleSessionVehicleStatusNames[static_cast<size_t>(vehicle->status)];
|
||
}
|
||
|
||
ft.Add<rct_string_id>(stringId);
|
||
uint16_t speedInMph = (abs(vehicle->velocity) * 9) >> 18;
|
||
ft.Add<uint16_t>(speedInMph);
|
||
const RideComponentName stationName = GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station);
|
||
ft.Add<rct_string_id>(ride->num_stations > 1 ? stationName.number : stationName.singular);
|
||
ft.Add<uint16_t>(vehicle->current_station.ToUnderlying() + 1);
|
||
return stringId != STR_CRASHING && stringId != STR_CRASHED_0 ? STR_BLACK_STRING : STR_RED_OUTLINED_STRING;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEF65
|
||
*/
|
||
static rct_string_id WindowRideGetStatusStation(rct_window* w, Formatter& ft)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return STR_NONE;
|
||
|
||
const auto stationIndex = GetStationIndexFromViewSelection(*w);
|
||
if (!stationIndex)
|
||
{
|
||
return STR_NONE;
|
||
}
|
||
|
||
const auto& station = ride->GetStation(*stationIndex);
|
||
rct_string_id stringId = STR_EMPTY;
|
||
// Entrance / exit
|
||
if (ride->status == RideStatus::Closed)
|
||
{
|
||
if (station.Entrance.IsNull())
|
||
stringId = STR_NO_ENTRANCE;
|
||
else if (station.Exit.IsNull())
|
||
stringId = STR_NO_EXIT;
|
||
}
|
||
else
|
||
{
|
||
if (station.Entrance.IsNull())
|
||
stringId = STR_EXIT_ONLY;
|
||
}
|
||
// Queue length
|
||
if (stringId == STR_EMPTY)
|
||
{
|
||
stringId = STR_QUEUE_EMPTY;
|
||
uint16_t queueLength = ride->GetStation(*stationIndex).QueueLength;
|
||
if (queueLength == 1)
|
||
stringId = STR_QUEUE_ONE_PERSON;
|
||
else if (queueLength > 1)
|
||
stringId = STR_QUEUE_PEOPLE;
|
||
|
||
ft.Add<rct_string_id>(stringId);
|
||
ft.Add<uint16_t>(queueLength);
|
||
}
|
||
else
|
||
{
|
||
ft.Add<rct_string_id>(stringId);
|
||
}
|
||
|
||
return STR_BLACK_STRING;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEE73
|
||
*/
|
||
static rct_string_id WindowRideGetStatus(rct_window* w, Formatter& ft)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (w->ride.view == 0)
|
||
return WindowRideGetStatusOverallView(w, ft);
|
||
if (ride != nullptr && w->ride.view <= ride->num_vehicles)
|
||
return WindowRideGetStatusVehicle(w, ft);
|
||
if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
|
||
return WindowRideGetStatusOverallView(w, ft);
|
||
return WindowRideGetStatusStation(w, ft);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEE73
|
||
*/
|
||
static void WindowRideMainPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
rct_widget* widget;
|
||
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
// Viewport and ear icon
|
||
if (w->viewport != nullptr)
|
||
{
|
||
window_draw_viewport(dpi, w);
|
||
if (w->viewport->flags & VIEWPORT_FLAG_SOUND_ON)
|
||
gfx_draw_sprite(dpi, ImageId(SPR_HEARING_VIEWPORT), w->windowPos + ScreenCoordsXY{ 2, 2 });
|
||
}
|
||
|
||
// View dropdown
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter();
|
||
if (w->ride.view != 0)
|
||
{
|
||
if (w->ride.view > ride->num_vehicles)
|
||
{
|
||
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number);
|
||
ft.Add<uint16_t>(w->ride.view - ride->num_vehicles);
|
||
}
|
||
else
|
||
{
|
||
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number);
|
||
ft.Add<uint16_t>(w->ride.view);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ft.Add<rct_string_id>(STR_OVERALL_VIEW);
|
||
}
|
||
|
||
widget = &window_ride_main_widgets[WIDX_VIEW];
|
||
DrawTextBasic(
|
||
dpi, { w->windowPos.x + (widget->left + widget->right - 11) / 2, w->windowPos.y + widget->top },
|
||
STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE });
|
||
|
||
// Status
|
||
ft = Formatter();
|
||
widget = &window_ride_main_widgets[WIDX_STATUS];
|
||
rct_string_id rideStatus = WindowRideGetStatus(w, ft);
|
||
DrawTextEllipsised(
|
||
dpi, w->windowPos + ScreenCoordsXY{ (widget->left + widget->right) / 2, widget->top }, widget->width(), rideStatus, ft,
|
||
{ TextAlignment::CENTRE });
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Vehicle
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B272D
|
||
*/
|
||
static void WindowRideVehicleMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2ABB
|
||
*/
|
||
static void WindowRideVehicleResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 214, 316, 214);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2748
|
||
*/
|
||
static void WindowRideVehicleMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_VEHICLE_TYPE_DROPDOWN:
|
||
WindowRideShowVehicleTypeDropdown(w, &w->widgets[widgetIndex]);
|
||
break;
|
||
case WIDX_VEHICLE_TRAINS_INCREASE:
|
||
if (ride->num_vehicles < OpenRCT2::Limits::MaxTrainsPerRide)
|
||
ride->SetNumVehicles(ride->num_vehicles + 1);
|
||
break;
|
||
case WIDX_VEHICLE_TRAINS_DECREASE:
|
||
if (ride->num_vehicles > 1)
|
||
ride->SetNumVehicles(ride->num_vehicles - 1);
|
||
break;
|
||
case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE:
|
||
if (ride->num_cars_per_train < OpenRCT2::Limits::MaxCarsPerTrain)
|
||
ride->SetNumCarsPerVehicle(ride->num_cars_per_train + 1);
|
||
break;
|
||
case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE:
|
||
rct_ride_entry* rideEntry = ride->GetRideEntry();
|
||
if (ride->num_cars_per_train > rideEntry->zero_cars + 1)
|
||
ride->SetNumCarsPerVehicle(ride->num_cars_per_train - 1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2767
|
||
*/
|
||
static void WindowRideVehicleDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (dropdownIndex == -1)
|
||
return;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_VEHICLE_TYPE_DROPDOWN:
|
||
if (dropdownIndex >= 0 && static_cast<std::size_t>(dropdownIndex) < VehicleDropdownData.size())
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id;
|
||
ride->SetRideEntry(newRideType);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2AA1
|
||
*/
|
||
static void WindowRideVehicleUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_2);
|
||
}
|
||
|
||
static OpenRCT2String WindowRideVehicleTooltip(rct_window* const w, const rct_widgetindex widgetIndex, rct_string_id fallback)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return { STR_NONE, {} };
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_VEHICLE_TRAINS:
|
||
case WIDX_VEHICLE_TRAINS_DECREASE:
|
||
case WIDX_VEHICLE_TRAINS_INCREASE:
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Increment(12);
|
||
|
||
RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle;
|
||
rct_string_id stringId = GetRideComponentName(vehicleType).count;
|
||
if (ride->max_trains > 1)
|
||
{
|
||
stringId = GetRideComponentName(vehicleType).count_plural;
|
||
}
|
||
ft.Add<rct_string_id>(stringId);
|
||
ft.Add<uint16_t>(ride->max_trains);
|
||
return { fallback, ft };
|
||
}
|
||
case WIDX_VEHICLE_CARS_PER_TRAIN:
|
||
case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE:
|
||
case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE:
|
||
{
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return { STR_NONE, {} };
|
||
|
||
auto ft = Formatter();
|
||
ft.Increment(16);
|
||
ft.Add<uint16_t>(std::max(uint8_t(1), ride->MaxCarsPerTrain) - rideEntry->zero_cars);
|
||
|
||
rct_string_id stringId = GetRideComponentName(RideComponentType::Car).singular;
|
||
if (ride->MaxCarsPerTrain - rideEntry->zero_cars > 1)
|
||
{
|
||
stringId = GetRideComponentName(RideComponentType::Car).plural;
|
||
}
|
||
ft.Add<rct_string_id>(stringId);
|
||
return { fallback, ft };
|
||
}
|
||
}
|
||
return { fallback, {} };
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B222C
|
||
*/
|
||
static void WindowRideVehicleInvalidate(rct_window* w)
|
||
{
|
||
rct_widget* widgets;
|
||
rct_ride_entry* rideEntry;
|
||
rct_string_id stringId;
|
||
int32_t carsPerTrain;
|
||
|
||
widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
rideEntry = ride->GetRideEntry();
|
||
|
||
w->widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID;
|
||
|
||
// Widget setup
|
||
carsPerTrain = ride->num_cars_per_train - rideEntry->zero_cars;
|
||
|
||
// Vehicle type
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name;
|
||
|
||
// Trains
|
||
if (rideEntry->cars_per_flat_ride > 1 || gCheatsDisableTrainLengthLimit)
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button;
|
||
}
|
||
else
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Cars per train
|
||
if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || gCheatsDisableTrainLengthLimit)
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button;
|
||
}
|
||
else
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
auto ft = Formatter::Common();
|
||
ft.Increment(6);
|
||
ft.Add<uint16_t>(carsPerTrain);
|
||
RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle;
|
||
stringId = GetRideComponentName(vehicleType).count;
|
||
if (ride->num_vehicles > 1)
|
||
{
|
||
stringId = GetRideComponentName(vehicleType).count_plural;
|
||
}
|
||
ft.Add<rct_string_id>(stringId);
|
||
ft.Add<uint16_t>(ride->num_vehicles);
|
||
|
||
ft.Increment(8);
|
||
|
||
ride->FormatNameTo(ft);
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
|
||
if (ride->num_cars_per_train > (rideEntry->zero_cars + 1))
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_X_CARS_PER_TRAIN;
|
||
}
|
||
else
|
||
{
|
||
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B23DC
|
||
*/
|
||
static void WindowRideVehiclePaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
auto screenCoords = w->windowPos + ScreenCoordsXY{ 8, 64 };
|
||
|
||
// Description
|
||
auto ft = Formatter();
|
||
ft.Add<rct_string_id>(rideEntry->naming.Description);
|
||
screenCoords.y += DrawTextWrapped(dpi, screenCoords, 300, STR_BLACK_STRING, ft, { TextAlignment::LEFT });
|
||
screenCoords.y += 2;
|
||
|
||
// Capacity
|
||
ft = Formatter();
|
||
ft.Add<rct_string_id>(rideEntry->capacity);
|
||
DrawTextBasic(dpi, screenCoords, STR_CAPACITY, ft);
|
||
|
||
// Excitement Factor
|
||
if (rideEntry->excitement_multiplier > 0)
|
||
{
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(rideEntry->excitement_multiplier);
|
||
DrawTextBasic(dpi, screenCoords, STR_EXCITEMENT_FACTOR, ft);
|
||
}
|
||
|
||
// Intensity Factor
|
||
if (rideEntry->intensity_multiplier > 0)
|
||
{
|
||
int32_t lineHeight = font_get_line_height(FontSpriteBase::MEDIUM);
|
||
if (lineHeight != 10)
|
||
screenCoords.x += 150;
|
||
else
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(rideEntry->intensity_multiplier);
|
||
DrawTextBasic(dpi, screenCoords, STR_INTENSITY_FACTOR, ft);
|
||
|
||
if (lineHeight != 10)
|
||
screenCoords.x -= 150;
|
||
}
|
||
|
||
// Nausea Factor
|
||
if (rideEntry->nausea_multiplier > 0)
|
||
{
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(rideEntry->nausea_multiplier);
|
||
DrawTextBasic(dpi, screenCoords, STR_NAUSEA_FACTOR, ft);
|
||
}
|
||
}
|
||
|
||
struct VehicleDrawInfo
|
||
{
|
||
int16_t x;
|
||
int16_t y;
|
||
ImageId imageId;
|
||
};
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2502
|
||
*/
|
||
static void WindowRideVehicleScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
rct_ride_entry* rideEntry = ride->GetRideEntry();
|
||
|
||
// Background
|
||
gfx_fill_rect(dpi, { { dpi->x, dpi->y }, { dpi->x + dpi->width, dpi->y + dpi->height } }, PALETTE_INDEX_12);
|
||
|
||
rct_widget* widget = &window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_PREVIEW];
|
||
int32_t startX = std::max(2, (widget->width() - ((ride->num_vehicles - 1) * 36)) / 2 - 25);
|
||
int32_t startY = widget->height() - 4;
|
||
|
||
CarEntry* carEntry = &rideEntry->Cars[ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, 0)];
|
||
startY += carEntry->tab_height;
|
||
|
||
// For each train
|
||
for (int32_t i = 0; i < ride->num_vehicles; i++)
|
||
{
|
||
VehicleDrawInfo trainCarImages[OpenRCT2::Limits::MaxCarsPerTrain];
|
||
VehicleDrawInfo* nextSpriteToDraw = trainCarImages;
|
||
int32_t x = startX;
|
||
int32_t y = startY;
|
||
|
||
// For each car in train
|
||
static_assert(std::numeric_limits<decltype(ride->num_cars_per_train)>::max() <= std::size(trainCarImages));
|
||
for (int32_t j = 0; j < ride->num_cars_per_train; j++)
|
||
{
|
||
carEntry = &rideEntry->Cars[ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, j)];
|
||
x += carEntry->spacing / 17432;
|
||
y -= (carEntry->spacing / 2) / 17432;
|
||
|
||
// Get colour of vehicle
|
||
int32_t vehicleColourIndex = 0;
|
||
switch (ride->colour_scheme_type & 3)
|
||
{
|
||
case VEHICLE_COLOUR_SCHEME_SAME:
|
||
vehicleColourIndex = 0;
|
||
break;
|
||
case VEHICLE_COLOUR_SCHEME_PER_TRAIN:
|
||
vehicleColourIndex = i;
|
||
break;
|
||
case VEHICLE_COLOUR_SCHEME_PER_VEHICLE:
|
||
vehicleColourIndex = j;
|
||
break;
|
||
}
|
||
VehicleColour vehicleColour = ride_get_vehicle_colour(ride, vehicleColourIndex);
|
||
|
||
ImageIndex imageIndex = carEntry->SpriteByYaw(OpenRCT2::Entity::Yaw::BaseRotation / 2, SpriteGroupType::SlopeFlat);
|
||
imageIndex &= carEntry->TabRotationMask;
|
||
imageIndex *= carEntry->base_num_frames;
|
||
imageIndex += carEntry->base_image_id;
|
||
|
||
auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary);
|
||
|
||
nextSpriteToDraw->x = x;
|
||
nextSpriteToDraw->y = y;
|
||
nextSpriteToDraw->imageId = imageId;
|
||
nextSpriteToDraw++;
|
||
|
||
x += carEntry->spacing / 17432;
|
||
y -= (carEntry->spacing / 2) / 17432;
|
||
}
|
||
|
||
if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER)
|
||
{
|
||
VehicleDrawInfo tmp = *(nextSpriteToDraw - 1);
|
||
*(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2);
|
||
*(nextSpriteToDraw - 2) = tmp;
|
||
}
|
||
|
||
VehicleDrawInfo* current = nextSpriteToDraw;
|
||
while (--current >= trainCarImages)
|
||
gfx_draw_sprite(dpi, current->imageId, { current->x, current->y });
|
||
|
||
startX += 36;
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Operating
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B11D5
|
||
*/
|
||
static void WindowRideModeTweakIncrease(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings;
|
||
uint8_t maxValue = operatingSettings.MaxValue;
|
||
uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue;
|
||
|
||
if (gCheatsUnlockOperatingLimits)
|
||
{
|
||
maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit;
|
||
}
|
||
|
||
uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1;
|
||
|
||
set_operating_setting(
|
||
w->rideId, RideSetSetting::Operation, std::clamp<int16_t>(ride->operation_option + increment, minValue, maxValue));
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B120A
|
||
*/
|
||
static void WindowRideModeTweakDecrease(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings;
|
||
uint8_t maxValue = operatingSettings.MaxValue;
|
||
uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue;
|
||
if (gCheatsUnlockOperatingLimits)
|
||
{
|
||
maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit;
|
||
}
|
||
|
||
uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1;
|
||
|
||
set_operating_setting(
|
||
w->rideId, RideSetSetting::Operation, std::clamp<int16_t>(ride->operation_option - decrement, minValue, maxValue));
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1631
|
||
*/
|
||
static void WindowRideModeDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
rct_widget* dropdownWidget;
|
||
|
||
dropdownWidget = widget - 1;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto availableModes = ride->GetAvailableModes();
|
||
|
||
// Create dropdown list
|
||
auto numAvailableModes = 0;
|
||
auto checkedIndex = -1;
|
||
for (auto i = 0; i < static_cast<uint8_t>(RideMode::Count); i++)
|
||
{
|
||
if (availableModes & (1ULL << i))
|
||
{
|
||
gDropdownItems[numAvailableModes].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[numAvailableModes].Args = RideModeNames[i];
|
||
|
||
if (ride->mode == static_cast<RideMode>(i))
|
||
checkedIndex = numAvailableModes;
|
||
|
||
numAvailableModes++;
|
||
}
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left);
|
||
|
||
if (checkedIndex != -1)
|
||
{
|
||
Dropdown::SetChecked(checkedIndex, true);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B15C0
|
||
*/
|
||
static void WindowRideLoadDropdown(rct_window* w, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto dropdownWidget = widget - 1;
|
||
for (auto i = 0; i < 5; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = VehicleLoadNames[i];
|
||
}
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B10A7
|
||
*/
|
||
static void WindowRideOperatingMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_LOAD_CHECKBOX:
|
||
set_operating_setting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_LOAD);
|
||
break;
|
||
case WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX:
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES);
|
||
break;
|
||
case WIDX_MINIMUM_LENGTH_CHECKBOX:
|
||
set_operating_setting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH);
|
||
break;
|
||
case WIDX_MAXIMUM_LENGTH_CHECKBOX:
|
||
set_operating_setting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH);
|
||
break;
|
||
case WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX:
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1715
|
||
*/
|
||
static void WindowRideOperatingResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 186, 316, 186);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B10F4
|
||
*/
|
||
static void WindowRideOperatingMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
uint8_t upper_bound, lower_bound;
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_MODE_TWEAK:
|
||
WindowRideOperatingTweakTextInput(w, *ride);
|
||
break;
|
||
case WIDX_MODE_TWEAK_INCREASE:
|
||
WindowRideModeTweakIncrease(w);
|
||
break;
|
||
case WIDX_MODE_TWEAK_DECREASE:
|
||
WindowRideModeTweakDecrease(w);
|
||
break;
|
||
case WIDX_LIFT_HILL_SPEED_INCREASE:
|
||
upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit
|
||
: ride->GetRideTypeDescriptor().LiftData.maximum_speed;
|
||
lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::LiftHillSpeed,
|
||
std::clamp<int16_t>(ride->lift_hill_speed + 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_LIFT_HILL_SPEED_DECREASE:
|
||
upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit
|
||
: ride->GetRideTypeDescriptor().LiftData.maximum_speed;
|
||
lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::LiftHillSpeed,
|
||
std::clamp<int16_t>(ride->lift_hill_speed - 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_MINIMUM_LENGTH:
|
||
WindowRideOperatingLengthWindow(w, WIDX_MINIMUM_LENGTH);
|
||
break;
|
||
case WIDX_MAXIMUM_LENGTH:
|
||
WindowRideOperatingLengthWindow(w, WIDX_MAXIMUM_LENGTH);
|
||
break;
|
||
case WIDX_MINIMUM_LENGTH_INCREASE:
|
||
upper_bound = OpenRCT2::Limits::MaxWaitingTime;
|
||
lower_bound = 0;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::MinWaitingTime,
|
||
std::clamp<int16_t>(ride->min_waiting_time + 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_MINIMUM_LENGTH_DECREASE:
|
||
upper_bound = OpenRCT2::Limits::MaxWaitingTime;
|
||
lower_bound = 0;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::MinWaitingTime,
|
||
std::clamp<int16_t>(ride->min_waiting_time - 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_MAXIMUM_LENGTH_INCREASE:
|
||
upper_bound = OpenRCT2::Limits::MaxWaitingTime;
|
||
lower_bound = 0;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::MaxWaitingTime,
|
||
std::clamp<int16_t>(ride->max_waiting_time + 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_MAXIMUM_LENGTH_DECREASE:
|
||
upper_bound = OpenRCT2::Limits::MaxWaitingTime;
|
||
lower_bound = 0;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::MaxWaitingTime,
|
||
std::clamp<int16_t>(ride->max_waiting_time - 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_MODE_DROPDOWN:
|
||
WindowRideModeDropdown(w, widget);
|
||
break;
|
||
case WIDX_LOAD_DROPDOWN:
|
||
WindowRideLoadDropdown(w, widget);
|
||
break;
|
||
case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE:
|
||
upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit
|
||
: OpenRCT2::Limits::MaxCircuitsPerRide;
|
||
lower_bound = 1;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::NumCircuits, std::clamp<int16_t>(ride->num_circuits + 1, lower_bound, upper_bound));
|
||
break;
|
||
case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE:
|
||
upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit
|
||
: OpenRCT2::Limits::MaxCircuitsPerRide;
|
||
lower_bound = 1;
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::NumCircuits, std::clamp<int16_t>(ride->num_circuits - 1, lower_bound, upper_bound));
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void WindowRideOperatingLengthWindow(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
uint8_t upperBound = OpenRCT2::Limits::MaxWaitingTime;
|
||
uint8_t lowerBound = 0;
|
||
Formatter ft;
|
||
ft.Add<int16_t>(lowerBound);
|
||
ft.Add<int16_t>(upperBound);
|
||
auto title = (widgetIndex == WIDX_MINIMUM_LENGTH) ? STR_MINIMUM_WAITING_TIME : STR_MAXIMUM_WAITING_TIME;
|
||
auto currentValue = (widgetIndex == WIDX_MINIMUM_LENGTH) ? ride->min_waiting_time : ride->max_waiting_time;
|
||
char buffer[5]{};
|
||
snprintf(buffer, std::size(buffer), "%u", currentValue);
|
||
WindowTextInputRawOpen(w, widgetIndex, title, STR_ENTER_VALUE, ft, buffer, 4);
|
||
}
|
||
|
||
static void WindowRideOperatingTweakTextInput(rct_window* w, const Ride& ride)
|
||
{
|
||
switch (ride.mode)
|
||
{
|
||
case RideMode::PoweredLaunchPasstrough:
|
||
case RideMode::PoweredLaunch:
|
||
case RideMode::UpwardLaunch:
|
||
case RideMode::PoweredLaunchBlockSectioned:
|
||
case RideMode::StationToStation:
|
||
case RideMode::Dodgems:
|
||
return;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
const auto& operatingSettings = ride.GetRideTypeDescriptor().OperatingSettings;
|
||
int16_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit : operatingSettings.MaxValue;
|
||
int16_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue;
|
||
|
||
const auto& title = window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text;
|
||
Formatter ft;
|
||
ft.Add<int16_t>(minValue * operatingSettings.OperatingSettingMultiplier);
|
||
ft.Add<int16_t>(maxValue * operatingSettings.OperatingSettingMultiplier);
|
||
|
||
uint16_t currentValue = static_cast<uint16_t>(ride.operation_option) * operatingSettings.OperatingSettingMultiplier;
|
||
char buffer[6]{};
|
||
snprintf(buffer, std::size(buffer), "%u", currentValue);
|
||
|
||
WindowTextInputRawOpen(w, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1165
|
||
*/
|
||
static void WindowRideOperatingDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (dropdownIndex == -1)
|
||
return;
|
||
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_MODE_DROPDOWN:
|
||
{
|
||
RideMode rideMode = RideMode::NullMode;
|
||
auto availableModes = ride->GetAvailableModes();
|
||
auto modeInDropdownIndex = -1;
|
||
for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++)
|
||
{
|
||
if (availableModes & EnumToFlag(rideModeIndex))
|
||
{
|
||
modeInDropdownIndex++;
|
||
if (modeInDropdownIndex == dropdownIndex)
|
||
{
|
||
rideMode = rideModeIndex;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (rideMode != RideMode::NullMode)
|
||
set_operating_setting(rideId, RideSetSetting::Mode, static_cast<uint8_t>(rideMode));
|
||
break;
|
||
}
|
||
case WIDX_LOAD_DROPDOWN:
|
||
set_operating_setting(
|
||
rideId, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B178E
|
||
*/
|
||
static void WindowRideOperatingUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_3);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING)
|
||
{
|
||
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING;
|
||
w->Invalidate();
|
||
}
|
||
}
|
||
|
||
static void WindowRideOperatingTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
|
||
{
|
||
if (text == nullptr)
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
if (widgetIndex == WIDX_MODE_TWEAK)
|
||
{
|
||
const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings;
|
||
uint32_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit
|
||
: operatingSettings.MaxValue;
|
||
uint32_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue;
|
||
auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier;
|
||
|
||
try
|
||
{
|
||
uint32_t origSize = std::stol(std::string(text)) / multiplier;
|
||
uint8_t size = static_cast<uint8_t>(std::clamp(origSize, minValue, maxValue));
|
||
set_operating_setting(ride->id, RideSetSetting::Operation, size);
|
||
}
|
||
catch (const std::logic_error&)
|
||
{
|
||
// std::stol can throw std::out_of_range or std::invalid_argument
|
||
}
|
||
}
|
||
else if (widgetIndex == WIDX_MINIMUM_LENGTH || widgetIndex == WIDX_MAXIMUM_LENGTH)
|
||
{
|
||
try
|
||
{
|
||
auto rideSetSetting = widgetIndex == WIDX_MINIMUM_LENGTH ? RideSetSetting::MinWaitingTime
|
||
: RideSetSetting::MaxWaitingTime;
|
||
|
||
uint16_t upperBound = OpenRCT2::Limits::MaxWaitingTime;
|
||
uint16_t lowerBound = 0;
|
||
uint16_t size = std::stol(std::string(text));
|
||
size = std::clamp(size, lowerBound, upperBound);
|
||
set_operating_setting(ride->id, rideSetSetting, size);
|
||
}
|
||
catch (const std::logic_error&)
|
||
{
|
||
// std::stol can throw std::out_of_range or std::invalid_argument
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B0B30
|
||
*/
|
||
static void WindowRideOperatingInvalidate(rct_window* w)
|
||
{
|
||
rct_widget* widgets;
|
||
rct_string_id format, caption, tooltip;
|
||
|
||
widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
// Widget setup
|
||
w->pressed_widgets &= ~(
|
||
(1ULL << WIDX_LOAD_CHECKBOX) | (1ULL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX)
|
||
| (1ULL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1ULL << WIDX_MAXIMUM_LENGTH_CHECKBOX)
|
||
| (1ULL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX));
|
||
|
||
// Sometimes, only one of the alternatives support lift hill pieces. Make sure to check both.
|
||
bool hasAlternativeType = ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE);
|
||
if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_LIFT_HILL)
|
||
|| (hasAlternativeType
|
||
&& GetRideTypeDescriptor(ride->GetRideTypeDescriptor().AlternateType).SupportsTrackPiece(TRACK_LIFT_HILL)))
|
||
{
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button;
|
||
ft.Rewind();
|
||
ft.Increment(20);
|
||
ft.Add<uint16_t>(ride->lift_hill_speed);
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Number of circuits
|
||
if (ride->CanHaveMultipleCircuits())
|
||
{
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button;
|
||
ft.Rewind();
|
||
ft.Increment(22);
|
||
ft.Add<uint16_t>(ride->num_circuits);
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Leave if another vehicle arrives at station
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LEAVE_WHEN_ANOTHER_VEHICLE_ARRIVES_AT_STATION)
|
||
&& ride->num_vehicles > 1 && !ride->IsBlockSectioned())
|
||
{
|
||
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip
|
||
= STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP;
|
||
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].text = ride->GetRideTypeDescriptor()
|
||
.NameConvention.vehicle
|
||
== RideComponentType::Boat
|
||
? STR_LEAVE_IF_ANOTHER_BOAT_ARRIVES
|
||
: STR_LEAVE_IF_ANOTHER_TRAIN_ARRIVES;
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Synchronise with adjacent stations
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS))
|
||
{
|
||
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].image
|
||
= STR_SYNCHRONISE_WITH_ADJACENT_STATIONS;
|
||
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip
|
||
= STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP;
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Mode
|
||
window_ride_operating_widgets[WIDX_MODE].text = RideModeNames[static_cast<int>(ride->mode)];
|
||
|
||
// Waiting
|
||
window_ride_operating_widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)];
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS))
|
||
{
|
||
window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button;
|
||
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button;
|
||
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button;
|
||
|
||
ft.Rewind();
|
||
ft.Increment(10);
|
||
ft.Add<rct_string_id>(STR_FORMAT_SECONDS);
|
||
ft.Add<uint16_t>(ride->min_waiting_time);
|
||
ft.Add<rct_string_id>(STR_FORMAT_SECONDS);
|
||
ft.Add<uint16_t>(ride->max_waiting_time);
|
||
|
||
if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD)
|
||
w->pressed_widgets |= (1ULL << WIDX_LOAD_CHECKBOX);
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty;
|
||
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty;
|
||
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES)
|
||
w->pressed_widgets |= (1ULL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX);
|
||
if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)
|
||
w->pressed_widgets |= (1ULL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX);
|
||
if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH)
|
||
w->pressed_widgets |= (1ULL << WIDX_MINIMUM_LENGTH_CHECKBOX);
|
||
if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH)
|
||
w->pressed_widgets |= (1ULL << WIDX_MAXIMUM_LENGTH_CHECKBOX);
|
||
|
||
// Mode specific functionality
|
||
auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier;
|
||
ft.Rewind();
|
||
ft.Increment(18);
|
||
ft.Add<uint16_t>(static_cast<uint16_t>(ride->operation_option) * multiplier);
|
||
switch (ride->mode)
|
||
{
|
||
case RideMode::PoweredLaunchPasstrough:
|
||
case RideMode::PoweredLaunch:
|
||
case RideMode::UpwardLaunch:
|
||
case RideMode::PoweredLaunchBlockSectioned:
|
||
ft.Rewind();
|
||
ft.Increment(18);
|
||
ft.Add<uint16_t>((ride->launch_speed * 9) / 4);
|
||
format = STR_RIDE_MODE_SPEED_VALUE;
|
||
caption = STR_LAUNCH_SPEED;
|
||
tooltip = STR_LAUNCH_SPEED_TIP;
|
||
break;
|
||
case RideMode::StationToStation:
|
||
ft.Rewind();
|
||
ft.Increment(18);
|
||
ft.Add<uint16_t>((ride->speed * 9) / 4);
|
||
format = STR_RIDE_MODE_SPEED_VALUE;
|
||
caption = STR_SPEED;
|
||
tooltip = STR_SPEED_TIP;
|
||
break;
|
||
case RideMode::Race:
|
||
ft.Rewind();
|
||
ft.Increment(18);
|
||
ft.Add<uint16_t>(ride->NumLaps);
|
||
format = STR_NUMBER_OF_LAPS_VALUE;
|
||
caption = STR_NUMBER_OF_LAPS;
|
||
tooltip = STR_NUMBER_OF_LAPS_TIP;
|
||
break;
|
||
case RideMode::Dodgems:
|
||
format = STR_RIDE_MODE_TIME_LIMIT_VALUE;
|
||
caption = STR_TIME_LIMIT;
|
||
tooltip = STR_TIME_LIMIT_TIP;
|
||
break;
|
||
case RideMode::Swing:
|
||
format = STR_RIDE_MODE_NUMBER_OF_SWINGS_VALUE;
|
||
caption = STR_NUMBER_OF_SWINGS;
|
||
tooltip = STR_NUMBER_OF_SWINGS_TIP;
|
||
break;
|
||
case RideMode::Rotation:
|
||
case RideMode::ForwardRotation:
|
||
case RideMode::BackwardRotation:
|
||
format = STR_NUMBER_OF_ROTATIONS_VALUE;
|
||
caption = STR_NUMBER_OF_ROTATIONS;
|
||
tooltip = STR_NUMBER_OF_ROTATIONS_TIP;
|
||
break;
|
||
default:
|
||
format = STR_MAX_PEOPLE_ON_RIDE_VALUE;
|
||
caption = STR_MAX_PEOPLE_ON_RIDE;
|
||
tooltip = STR_MAX_PEOPLE_ON_RIDE_TIP;
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
|
||
format = 0;
|
||
break;
|
||
}
|
||
|
||
if (format != 0)
|
||
{
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text = caption;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button;
|
||
w->pressed_widgets &= ~(1ULL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX);
|
||
}
|
||
else
|
||
{
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1001
|
||
*/
|
||
static void WindowRideOperatingPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
// Horizontal rule between mode settings and depart settings
|
||
gfx_fill_rect_inset(
|
||
dpi,
|
||
{ w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 },
|
||
w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } },
|
||
w->colours[1], INSET_RECT_FLAG_BORDER_INSET);
|
||
|
||
// Number of block sections
|
||
if (ride->IsBlockSectioned())
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Add<uint16_t>(ride->num_block_brakes + ride->num_stations);
|
||
DrawTextBasic(
|
||
dpi, w->windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 },
|
||
STR_BLOCK_SECTIONS, ft, COLOUR_BLACK);
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Maintenance
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1AE4
|
||
*/
|
||
static void WindowRideLocateMechanic(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
// First check if there is a mechanic assigned
|
||
Peep* mechanic = ride_get_assigned_mechanic(ride);
|
||
|
||
// Otherwise find the closest mechanic
|
||
if (mechanic == nullptr)
|
||
mechanic = ride_find_closest_mechanic(ride, 1);
|
||
|
||
if (mechanic == nullptr)
|
||
context_show_error(STR_UNABLE_TO_LOCATE_MECHANIC, STR_NONE, {});
|
||
else
|
||
{
|
||
auto intent = Intent(WC_PEEP);
|
||
intent.putExtra(INTENT_EXTRA_PEEP, mechanic);
|
||
context_open_intent(&intent);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B7D08
|
||
*/
|
||
static void WindowRideMaintenanceDrawBar(
|
||
rct_window* w, rct_drawpixelinfo* dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour)
|
||
{
|
||
gfx_fill_rect_inset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, w->colours[1], INSET_RECT_F_30);
|
||
if (colour & BAR_BLINK)
|
||
{
|
||
colour &= ~BAR_BLINK;
|
||
if (game_is_not_paused() && (gCurrentRealTimeTicks & 8))
|
||
return;
|
||
}
|
||
|
||
value = ((186 * ((value * 2) & 0xFF)) >> 8) & 0xFF;
|
||
if (value > 2)
|
||
{
|
||
gfx_fill_rect_inset(dpi, { coords + ScreenCoordsXY{ 2, 1 }, coords + ScreenCoordsXY{ value + 1, 7 } }, colour, 0);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1AAD
|
||
*/
|
||
static void WindowRideMaintenanceMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_LOCATE_MECHANIC:
|
||
WindowRideLocateMechanic(w);
|
||
break;
|
||
case WIDX_REFURBISH_RIDE:
|
||
context_open_detail_window(WD_REFURBISH_RIDE, w->number);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1D70
|
||
*/
|
||
static void WindowRideMaintenanceResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 135, 316, 135);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1ACE
|
||
*/
|
||
static void WindowRideMaintenanceMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
rct_widget* dropdownWidget = widget;
|
||
int32_t j, num_items;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_INSPECTION_INTERVAL_DROPDOWN:
|
||
dropdownWidget--;
|
||
for (int32_t i = 0; i < 7; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = RideInspectionIntervalNames[i];
|
||
}
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, 7, widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(ride->inspection_interval, true);
|
||
break;
|
||
|
||
case WIDX_FORCE_BREAKDOWN:
|
||
num_items = 1;
|
||
for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++)
|
||
{
|
||
if (rideEntry->ride_type[j] != RIDE_TYPE_NULL)
|
||
break;
|
||
}
|
||
gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[0].Args = STR_DEBUG_FIX_RIDE;
|
||
for (int32_t i = 0; i < 8; i++)
|
||
{
|
||
assert(j < static_cast<int32_t>(std::size(rideEntry->ride_type)));
|
||
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
|
||
{
|
||
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
|
||
{
|
||
if (ride->num_vehicles != 1)
|
||
continue;
|
||
}
|
||
gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[num_items].Args = RideBreakdownReasonNames[i];
|
||
num_items++;
|
||
}
|
||
}
|
||
if (num_items == 1)
|
||
{
|
||
context_show_error(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {});
|
||
}
|
||
else
|
||
{
|
||
WindowDropdownShowText(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top },
|
||
dropdownWidget->height() + 1, w->colours[1], Dropdown::Flag::StayOpen, num_items);
|
||
|
||
num_items = 1;
|
||
int32_t breakdownReason = ride->breakdown_reason_pending;
|
||
if (breakdownReason != BREAKDOWN_NONE && (ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING))
|
||
{
|
||
for (int32_t i = 0; i < 8; i++)
|
||
{
|
||
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
|
||
{
|
||
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
|
||
{
|
||
if (ride->num_vehicles != 1)
|
||
continue;
|
||
}
|
||
if (i == breakdownReason)
|
||
{
|
||
Dropdown::SetChecked(num_items, true);
|
||
break;
|
||
}
|
||
gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[num_items].Args = RideBreakdownReasonNames[i];
|
||
num_items++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0)
|
||
{
|
||
Dropdown::SetDisabled(0, true);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1AD9
|
||
*/
|
||
static void WindowRideMaintenanceDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (dropdownIndex == -1)
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_INSPECTION_INTERVAL_DROPDOWN:
|
||
set_operating_setting(w->rideId, RideSetSetting::InspectionInterval, dropdownIndex);
|
||
break;
|
||
|
||
case WIDX_FORCE_BREAKDOWN:
|
||
if (dropdownIndex == 0)
|
||
{
|
||
Vehicle* vehicle;
|
||
switch (ride->breakdown_reason_pending)
|
||
{
|
||
case BREAKDOWN_SAFETY_CUT_OUT:
|
||
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
|
||
break;
|
||
for (int32_t i = 0; i < ride->num_vehicles; ++i)
|
||
{
|
||
for (vehicle = GetEntity<Vehicle>(ride->vehicles[i]); vehicle != nullptr;
|
||
vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
|
||
{
|
||
vehicle->ClearUpdateFlag(
|
||
VEHICLE_UPDATE_FLAG_BROKEN_CAR | VEHICLE_UPDATE_FLAG_ZERO_VELOCITY
|
||
| VEHICLE_UPDATE_FLAG_BROKEN_TRAIN);
|
||
}
|
||
}
|
||
break;
|
||
case BREAKDOWN_RESTRAINTS_STUCK_CLOSED:
|
||
case BREAKDOWN_RESTRAINTS_STUCK_OPEN:
|
||
case BREAKDOWN_DOORS_STUCK_CLOSED:
|
||
case BREAKDOWN_DOORS_STUCK_OPEN:
|
||
vehicle = GetEntity<Vehicle>(ride->vehicles[ride->broken_vehicle]);
|
||
if (vehicle != nullptr)
|
||
{
|
||
vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_CAR);
|
||
}
|
||
break;
|
||
case BREAKDOWN_VEHICLE_MALFUNCTION:
|
||
vehicle = GetEntity<Vehicle>(ride->vehicles[ride->broken_vehicle]);
|
||
if (vehicle != nullptr)
|
||
{
|
||
vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN);
|
||
}
|
||
break;
|
||
}
|
||
ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN);
|
||
window_invalidate_by_number(WC_RIDE, w->number);
|
||
break;
|
||
}
|
||
if (ride->lifecycle_flags
|
||
& (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
|
||
{
|
||
context_show_error(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_ALREADY_BROKEN, {});
|
||
}
|
||
else if (ride->status == RideStatus::Closed)
|
||
{
|
||
context_show_error(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_IS_CLOSED, {});
|
||
}
|
||
else
|
||
{
|
||
int32_t j;
|
||
for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++)
|
||
{
|
||
if (rideEntry->ride_type[j] != RIDE_TYPE_NULL)
|
||
break;
|
||
}
|
||
int32_t i;
|
||
int32_t num_items = 1;
|
||
for (i = 0; i < BREAKDOWN_COUNT; i++)
|
||
{
|
||
assert(j < static_cast<int32_t>(std::size(rideEntry->ride_type)));
|
||
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
|
||
{
|
||
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
|
||
{
|
||
if (ride->num_vehicles != 1)
|
||
continue;
|
||
}
|
||
if (num_items == dropdownIndex)
|
||
break;
|
||
num_items++;
|
||
}
|
||
}
|
||
ride_prepare_breakdown(ride, i);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1D37
|
||
*/
|
||
static void WindowRideMaintenanceUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_4);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE)
|
||
{
|
||
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE;
|
||
w->Invalidate();
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B17C8
|
||
*/
|
||
static void WindowRideMaintenanceInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval];
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
|
||
if (gConfigGeneral.debugging_tools && network_get_mode() == NETWORK_MODE_NONE)
|
||
{
|
||
window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn;
|
||
}
|
||
else
|
||
{
|
||
window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED))
|
||
{
|
||
w->disabled_widgets |= (1ULL << WIDX_REFURBISH_RIDE);
|
||
window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED;
|
||
}
|
||
else
|
||
{
|
||
w->disabled_widgets &= ~(1ULL << WIDX_REFURBISH_RIDE);
|
||
window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1877
|
||
*/
|
||
static void WindowRideMaintenancePaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
// Locate mechanic button image
|
||
rct_widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC];
|
||
auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top };
|
||
gfx_draw_sprite(
|
||
dpi, (gStaffMechanicColour << 24) | IMAGE_TYPE_REMAP | IMAGE_TYPE_REMAP_2_PLUS | SPR_MECHANIC, screenCoords, 0);
|
||
|
||
// Inspection label
|
||
widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL];
|
||
screenCoords = w->windowPos + ScreenCoordsXY{ 4, widget->top + 1 };
|
||
DrawTextBasic(dpi, screenCoords, STR_INSPECTION);
|
||
|
||
// Reliability
|
||
widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND];
|
||
screenCoords = w->windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 };
|
||
|
||
uint16_t reliability = ride->reliability_percentage;
|
||
auto ft = Formatter();
|
||
ft.Add<uint16_t>(reliability);
|
||
DrawTextBasic(dpi, screenCoords, STR_RELIABILITY_LABEL_1757, ft);
|
||
WindowRideMaintenanceDrawBar(
|
||
w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, std::max<int32_t>(10, reliability), COLOUR_BRIGHT_GREEN);
|
||
screenCoords.y += 11;
|
||
|
||
uint16_t downTime = ride->downtime;
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(downTime);
|
||
DrawTextBasic(dpi, screenCoords, STR_DOWN_TIME_LABEL_1889, ft);
|
||
WindowRideMaintenanceDrawBar(w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, downTime, COLOUR_BRIGHT_RED);
|
||
screenCoords.y += 26;
|
||
|
||
// Last inspection
|
||
rct_string_id stringId;
|
||
if (ride->last_inspection <= 1)
|
||
stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTE;
|
||
else if (ride->last_inspection <= 240)
|
||
stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTES;
|
||
else
|
||
stringId = STR_TIME_SINCE_LAST_INSPECTION_MORE_THAN_4_HOURS;
|
||
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(ride->last_inspection);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += 12;
|
||
|
||
// Last / current breakdown
|
||
if (ride->breakdown_reason == BREAKDOWN_NONE)
|
||
return;
|
||
|
||
stringId = (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) ? STR_CURRENT_BREAKDOWN : STR_LAST_BREAKDOWN;
|
||
ft = Formatter();
|
||
ft.Add<rct_string_id>(RideBreakdownReasonNames[ride->breakdown_reason]);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += 12;
|
||
|
||
// Mechanic status
|
||
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
|
||
{
|
||
switch (ride->mechanic_status)
|
||
{
|
||
case RIDE_MECHANIC_STATUS_CALLING:
|
||
{
|
||
stringId = STR_NO_MECHANICS_ARE_HIRED_MESSAGE;
|
||
|
||
for (auto peep : EntityList<Staff>())
|
||
{
|
||
if (peep->IsMechanic())
|
||
{
|
||
stringId = STR_CALLING_MECHANIC;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case RIDE_MECHANIC_STATUS_HEADING:
|
||
stringId = STR_MEHCANIC_IS_HEADING_FOR_THE_RIDE;
|
||
break;
|
||
case RIDE_MECHANIC_STATUS_FIXING:
|
||
case RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES:
|
||
stringId = STR_MEHCANIC_IS_FIXING_THE_RIDE;
|
||
break;
|
||
default:
|
||
stringId = STR_EMPTY;
|
||
break;
|
||
}
|
||
|
||
if (stringId != STR_EMPTY)
|
||
{
|
||
if (stringId == STR_CALLING_MECHANIC || stringId == STR_NO_MECHANICS_ARE_HIRED_MESSAGE)
|
||
{
|
||
DrawTextWrapped(dpi, screenCoords, 280, stringId, {}, { TextAlignment::LEFT });
|
||
}
|
||
else
|
||
{
|
||
auto staff = GetEntity<Staff>(ride->mechanic);
|
||
if (staff != nullptr && staff->IsMechanic())
|
||
{
|
||
ft = Formatter();
|
||
staff->FormatNameTo(ft);
|
||
DrawTextWrapped(dpi, screenCoords, 280, stringId, ft, { TextAlignment::LEFT });
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Colour
|
||
|
||
static uint32_t WindowRideGetColourButtonImage(int32_t colour)
|
||
{
|
||
return IMAGE_TYPE_TRANSPARENT | SPRITE_ID_PALETTE_COLOUR_1(colour) | SPR_PALETTE_BTN;
|
||
}
|
||
|
||
static int32_t WindowRideHasTrackColour(Ride* ride, int32_t trackColour)
|
||
{
|
||
// Get station flags (shops don't have them)
|
||
auto stationObjFlags = 0;
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
|
||
{
|
||
auto stationObj = ride->GetStationObject();
|
||
if (stationObj != nullptr)
|
||
{
|
||
stationObjFlags = stationObj->Flags;
|
||
}
|
||
}
|
||
|
||
switch (trackColour)
|
||
{
|
||
case 0:
|
||
return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR)
|
||
|| ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN);
|
||
case 1:
|
||
return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR)
|
||
|| ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL);
|
||
case 2:
|
||
return ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS);
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static void WindowRideSetTrackColourScheme(rct_window* w, const ScreenCoordsXY& screenPos)
|
||
{
|
||
auto newColourScheme = static_cast<uint8_t>(w->ride_colour);
|
||
auto info = get_map_coordinates_from_pos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride));
|
||
|
||
if (info.SpriteType != ViewportInteractionItem::Ride)
|
||
return;
|
||
if (info.Element->GetType() != TileElementType::Track)
|
||
return;
|
||
if (info.Element->AsTrack()->GetRideIndex() != w->rideId)
|
||
return;
|
||
if (info.Element->AsTrack()->GetColourScheme() == newColourScheme)
|
||
return;
|
||
|
||
auto z = info.Element->GetBaseZ();
|
||
auto direction = info.Element->GetDirection();
|
||
auto gameAction = RideSetColourSchemeAction(
|
||
CoordsXYZD{ info.Loc, z, static_cast<Direction>(direction) }, info.Element->AsTrack()->GetTrackType(), newColourScheme);
|
||
GameActions::Execute(&gameAction);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B04FA
|
||
*/
|
||
static void WindowRideColourClose(rct_window* w)
|
||
{
|
||
if (!(input_test_flag(INPUT_FLAG_TOOL_ACTIVE)))
|
||
return;
|
||
|
||
if (gCurrentToolWidget.window_classification != w->classification)
|
||
return;
|
||
|
||
if (gCurrentToolWidget.window_number != w->number)
|
||
return;
|
||
|
||
tool_cancel();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B02A1
|
||
*/
|
||
static void WindowRideColourMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_PAINT_INDIVIDUAL_AREA:
|
||
tool_set(*w, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown);
|
||
break;
|
||
case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX:
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS);
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
w->rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B0AB6
|
||
*/
|
||
static void WindowRideColourResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 207, 316, 207);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B02C6
|
||
*/
|
||
static void WindowRideColourMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
VehicleColour vehicleColour;
|
||
int32_t i, numItems;
|
||
rct_string_id stringId;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
auto colourSchemeIndex = w->ride_colour;
|
||
auto dropdownWidget = widget - 1;
|
||
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN:
|
||
for (i = 0; i < OpenRCT2::Limits::NumColourSchemes; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = ColourSchemeNames[i];
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(colourSchemeIndex, true);
|
||
break;
|
||
case WIDX_TRACK_MAIN_COLOUR:
|
||
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main);
|
||
break;
|
||
case WIDX_TRACK_ADDITIONAL_COLOUR:
|
||
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional);
|
||
break;
|
||
case WIDX_TRACK_SUPPORT_COLOUR:
|
||
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports);
|
||
break;
|
||
case WIDX_MAZE_STYLE_DROPDOWN:
|
||
for (i = 0; i < 4; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = MazeOptions[i].text;
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true);
|
||
break;
|
||
case WIDX_ENTRANCE_STYLE_DROPDOWN:
|
||
{
|
||
auto ddIndex = 0;
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
for (i = 0; i < MAX_STATION_OBJECTS; i++)
|
||
{
|
||
auto stationObj = static_cast<StationObject*>(objManager.GetLoadedObject(ObjectType::Station, i));
|
||
if (stationObj != nullptr)
|
||
{
|
||
gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[ddIndex].Args = stationObj->NameStringId;
|
||
if (ride->entrance_style == i)
|
||
{
|
||
gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL_SELECTED;
|
||
}
|
||
ddIndex++;
|
||
}
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widget->right - dropdownWidget->left);
|
||
break;
|
||
}
|
||
case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN:
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = (GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular
|
||
<< 16)
|
||
| VehicleColourSchemeNames[i];
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, rideEntry->max_cars_in_train > 1 ? 3 : 2,
|
||
widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(ride->colour_scheme_type & 3, true);
|
||
break;
|
||
case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN:
|
||
numItems = ride->num_vehicles;
|
||
if ((ride->colour_scheme_type & 3) != VEHICLE_COLOUR_SCHEME_PER_TRAIN)
|
||
numItems = ride->num_cars_per_train;
|
||
|
||
stringId = (ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_TRAIN ? STR_RIDE_COLOUR_TRAIN_OPTION
|
||
: STR_RIDE_COLOUR_VEHICLE_OPTION;
|
||
for (i = 0; i < std::min(numItems, Dropdown::ItemsMaxSize); i++)
|
||
{
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = (static_cast<int64_t>(i + 1) << 32)
|
||
| ((GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised) << 16)
|
||
| stringId;
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
|
||
|
||
Dropdown::SetChecked(w->vehicleIndex, true);
|
||
break;
|
||
case WIDX_VEHICLE_BODY_COLOR:
|
||
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
|
||
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Body);
|
||
break;
|
||
case WIDX_VEHICLE_TRIM_COLOUR:
|
||
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
|
||
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Trim);
|
||
break;
|
||
case WIDX_VEHICLE_TERNARY_COLOUR:
|
||
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
|
||
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Tertiary);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B0331
|
||
*/
|
||
static void WindowRideColourDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (dropdownIndex == -1)
|
||
return;
|
||
|
||
const auto rideId = w->rideId;
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN:
|
||
w->ride_colour = static_cast<uint16_t>(dropdownIndex);
|
||
w->Invalidate();
|
||
break;
|
||
case WIDX_TRACK_MAIN_COLOUR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::TrackColourMain, dropdownIndex, w->ride_colour);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_TRACK_ADDITIONAL_COLOUR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::TrackColourAdditional, dropdownIndex, w->ride_colour);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_TRACK_SUPPORT_COLOUR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::TrackColourSupports, dropdownIndex, w->ride_colour);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_MAZE_STYLE_DROPDOWN:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, w->ride_colour);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_ENTRANCE_STYLE_DROPDOWN:
|
||
{
|
||
auto ddIndex = 0;
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
for (auto i = 0; i < MAX_STATION_OBJECTS; i++)
|
||
{
|
||
auto stationObj = static_cast<StationObject*>(objManager.GetLoadedObject(ObjectType::Station, i));
|
||
if (stationObj != nullptr)
|
||
{
|
||
if (ddIndex == dropdownIndex)
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::EntranceStyle, ddIndex, 0);
|
||
rideSetAppearanceAction.SetCallback([ddIndex](const GameAction*, const GameActions::Result* res) {
|
||
if (res->Error != GameActions::Status::Ok)
|
||
return;
|
||
gLastEntranceStyle = ddIndex;
|
||
});
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
break;
|
||
}
|
||
ddIndex++;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
w->vehicleIndex = 0;
|
||
}
|
||
break;
|
||
case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN:
|
||
w->vehicleIndex = dropdownIndex;
|
||
w->Invalidate();
|
||
break;
|
||
case WIDX_VEHICLE_BODY_COLOR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::VehicleColourBody, dropdownIndex, w->vehicleIndex);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_VEHICLE_TRIM_COLOUR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::VehicleColourTrim, dropdownIndex, w->vehicleIndex);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
case WIDX_VEHICLE_TERNARY_COLOUR:
|
||
{
|
||
auto rideSetAppearanceAction = RideSetAppearanceAction(
|
||
rideId, RideSetAppearanceType::VehicleColourTernary, dropdownIndex, w->vehicleIndex);
|
||
GameActions::Execute(&rideSetAppearanceAction);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B0A8F
|
||
*/
|
||
static void WindowRideColourUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_5);
|
||
widget_invalidate(w, WIDX_VEHICLE_PREVIEW);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B04EC
|
||
*/
|
||
static void WindowRideColourTooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
|
||
{
|
||
if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA)
|
||
WindowRideSetTrackColourScheme(w, screenCoords);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B04F3
|
||
*/
|
||
static void WindowRideColourTooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
|
||
{
|
||
if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA)
|
||
WindowRideSetTrackColourScheme(w, screenCoords);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AFB36
|
||
*/
|
||
static void WindowRideColourInvalidate(rct_window* w)
|
||
{
|
||
TrackColour trackColour;
|
||
VehicleColour vehicleColour;
|
||
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID;
|
||
auto ft = Formatter::Common();
|
||
ft.Increment(16);
|
||
ride->FormatNameTo(ft);
|
||
|
||
// Track colours
|
||
int32_t colourScheme = w->ride_colour;
|
||
trackColour = ride_get_track_colour(ride, colourScheme);
|
||
|
||
// Maze style
|
||
if (ride->type == RIDE_TYPE_MAZE)
|
||
{
|
||
window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button;
|
||
window_ride_colour_widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text;
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Track, multiple colour schemes
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR))
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button;
|
||
window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn;
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Track main colour
|
||
if (WindowRideHasTrackColour(ride, 0))
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].image = WindowRideGetColourButtonImage(trackColour.main);
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Track additional colour
|
||
if (WindowRideHasTrackColour(ride, 1))
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = WindowRideGetColourButtonImage(trackColour.additional);
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Selling item random colour checkbox
|
||
if (ride->HasRecolourableShopItems())
|
||
{
|
||
window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox;
|
||
if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS))
|
||
{
|
||
w->pressed_widgets |= (1ULL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX);
|
||
}
|
||
else
|
||
{
|
||
w->pressed_widgets &= ~(1ULL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Track supports colour
|
||
if (WindowRideHasTrackColour(ride, 2) && ride->type != RIDE_TYPE_MAZE)
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].image = WindowRideGetColourButtonImage(trackColour.supports);
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Track preview
|
||
if (ride->GetRideTypeDescriptor().HasFlag(
|
||
RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL
|
||
| RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS))
|
||
window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner;
|
||
else
|
||
window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty;
|
||
|
||
// Entrance style
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
|
||
{
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner;
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button;
|
||
|
||
auto stringId = STR_NONE;
|
||
auto stationObj = ride->GetStationObject();
|
||
if (stationObj != nullptr)
|
||
{
|
||
stringId = stationObj->NameStringId;
|
||
}
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].text = stringId;
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Vehicle colours
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)
|
||
&& ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS))
|
||
{
|
||
int32_t vehicleColourSchemeType = ride->colour_scheme_type & 3;
|
||
if (vehicleColourSchemeType == 0)
|
||
w->vehicleIndex = 0;
|
||
|
||
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
|
||
|
||
window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOR].image = WindowRideGetColourButtonImage(vehicleColour.Body);
|
||
|
||
bool allowChangingTrimColour = false;
|
||
bool allowChangingTernaryColour = false;
|
||
|
||
for (int32_t i = 0; i < ride->num_cars_per_train; i++)
|
||
{
|
||
uint8_t vehicleTypeIndex = ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, i);
|
||
|
||
if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TRIM_COLOUR)
|
||
{
|
||
allowChangingTrimColour = true;
|
||
}
|
||
if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TERNARY_COLOUR)
|
||
{
|
||
allowChangingTernaryColour = true;
|
||
}
|
||
}
|
||
|
||
// Additional colours
|
||
if (allowChangingTrimColour)
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].image = WindowRideGetColourButtonImage(vehicleColour.Trim);
|
||
if (allowChangingTernaryColour)
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::ColourBtn;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].image = WindowRideGetColourButtonImage(
|
||
vehicleColour.Tertiary);
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Vehicle colour scheme type
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_VEHICLE_IS_INTEGRAL)
|
||
&& (ride->num_cars_per_train | ride->num_vehicles) > 1)
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button;
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
ft.Rewind();
|
||
ft.Increment(6);
|
||
ft.Add<rct_string_id>(VehicleColourSchemeNames[vehicleColourSchemeType]);
|
||
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular);
|
||
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised);
|
||
ft.Add<uint16_t>(w->vehicleIndex + 1);
|
||
|
||
// Vehicle index
|
||
if (vehicleColourSchemeType != 0)
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1
|
||
? STR_RIDE_COLOUR_TRAIN_VALUE
|
||
: STR_RIDE_COLOUR_VEHICLE_VALUE;
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOR].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty;
|
||
window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
ft.Rewind();
|
||
ft.Increment(14);
|
||
ft.Add<rct_string_id>(ColourSchemeNames[colourScheme]);
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AFF3E
|
||
*/
|
||
static void WindowRideColourPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
// TODO: This should use lists and identified sprites
|
||
rct_drawpixelinfo clippedDpi;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
// Track / shop item preview
|
||
const auto& trackPreviewWidget = window_ride_colour_widgets[WIDX_TRACK_PREVIEW];
|
||
if (trackPreviewWidget.type != WindowWidgetType::Empty)
|
||
gfx_fill_rect(
|
||
dpi,
|
||
{ { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } },
|
||
{ w->windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } },
|
||
PALETTE_INDEX_12);
|
||
|
||
auto trackColour = ride_get_track_colour(ride, w->ride_colour);
|
||
|
||
//
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None)
|
||
{
|
||
auto screenCoords = w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top };
|
||
|
||
// Track
|
||
if (ride->type == RIDE_TYPE_MAZE)
|
||
{
|
||
gfx_draw_sprite(dpi, ImageId(MazeOptions[trackColour.supports].sprite), screenCoords);
|
||
}
|
||
else
|
||
{
|
||
auto typeDescriptor = ride->GetRideTypeDescriptor();
|
||
int32_t spriteIndex = typeDescriptor.ColourPreview.Track;
|
||
if (spriteIndex != 0)
|
||
{
|
||
gfx_draw_sprite(dpi, ImageId(spriteIndex, trackColour.main, trackColour.additional), screenCoords);
|
||
}
|
||
|
||
// Supports
|
||
spriteIndex = typeDescriptor.ColourPreview.Supports;
|
||
if (spriteIndex != 0)
|
||
{
|
||
gfx_draw_sprite(dpi, ImageId(spriteIndex, trackColour.supports), screenCoords);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
auto screenCoords = w->windowPos
|
||
+ ScreenCoordsXY{ (trackPreviewWidget.left + trackPreviewWidget.right) / 2 - 8,
|
||
(trackPreviewWidget.bottom + trackPreviewWidget.top) / 2 - 6 };
|
||
|
||
ShopItem shopItem = rideEntry->shop_item[1] == ShopItem::None ? rideEntry->shop_item[0] : rideEntry->shop_item[1];
|
||
if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS))
|
||
{
|
||
static colour_t spriteColour = COLOUR_BLACK;
|
||
// Limit update rate of preview to avoid making people dizzy.
|
||
if ((gCurrentTicks % 64) == 0)
|
||
{
|
||
spriteColour++;
|
||
if (spriteColour >= COLOUR_COUNT)
|
||
{
|
||
spriteColour = COLOUR_BLACK;
|
||
}
|
||
}
|
||
|
||
gfx_draw_sprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, spriteColour), screenCoords);
|
||
}
|
||
else
|
||
{
|
||
gfx_draw_sprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, ride->track_colour[0].main), screenCoords);
|
||
}
|
||
}
|
||
|
||
// Entrance preview
|
||
trackColour = ride_get_track_colour(ride, 0);
|
||
const auto& entrancePreviewWidget = w->widgets[WIDX_ENTRANCE_PREVIEW];
|
||
if (entrancePreviewWidget.type != WindowWidgetType::Empty)
|
||
{
|
||
if (clip_drawpixelinfo(
|
||
&clippedDpi, dpi,
|
||
w->windowPos + ScreenCoordsXY{ entrancePreviewWidget.left + 1, entrancePreviewWidget.top + 1 },
|
||
entrancePreviewWidget.width(), entrancePreviewWidget.height()))
|
||
{
|
||
gfx_clear(&clippedDpi, PALETTE_INDEX_12);
|
||
|
||
auto stationObj = ride->GetStationObject();
|
||
if (stationObj != nullptr && stationObj->BaseImageId != ImageIndexUndefined)
|
||
{
|
||
auto imageId = ImageId(stationObj->BaseImageId, trackColour.main, trackColour.additional);
|
||
|
||
// Back
|
||
gfx_draw_sprite(&clippedDpi, imageId, { 34, 20 });
|
||
|
||
// Front
|
||
gfx_draw_sprite(&clippedDpi, imageId.WithIndexOffset(4), { 34, 20 });
|
||
|
||
// Glass
|
||
if (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT)
|
||
{
|
||
auto glassImageId = ImageId(stationObj->BaseImageId + 20).WithTransparancy(trackColour.main);
|
||
gfx_draw_sprite(&clippedDpi, glassImageId, { 34, 20 });
|
||
}
|
||
}
|
||
}
|
||
|
||
DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + 103 }, 97, STR_STATION_STYLE, {});
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B0192
|
||
*/
|
||
static void WindowRideColourScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
auto vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW];
|
||
auto vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
|
||
|
||
// Background colour
|
||
gfx_fill_rect(dpi, { { dpi->x, dpi->y }, { dpi->x + dpi->width - 1, dpi->y + dpi->height - 1 } }, PALETTE_INDEX_12);
|
||
|
||
// ?
|
||
auto screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 };
|
||
|
||
// ?
|
||
auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? w->vehicleIndex
|
||
: rideEntry->TabCar;
|
||
|
||
CarEntry* carEntry = &rideEntry->Cars[ride_entry_get_vehicle_at_position(
|
||
ride->subtype, ride->num_cars_per_train, trainCarIndex)];
|
||
|
||
screenCoords.y += carEntry->tab_height;
|
||
|
||
// Draw the coloured spinning vehicle
|
||
// w->frame_no represents a SpritePrecision of 64
|
||
ImageIndex imageIndex = carEntry->SpriteByYaw(w->frame_no / 2, SpriteGroupType::SlopeFlat);
|
||
imageIndex &= carEntry->TabRotationMask;
|
||
imageIndex *= carEntry->base_num_frames;
|
||
imageIndex += carEntry->base_image_id;
|
||
auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary);
|
||
gfx_draw_sprite(dpi, imageId, screenCoords);
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Music
|
||
static std::vector<ObjectEntryIndex> window_ride_current_music_style_order;
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B215D
|
||
*/
|
||
static void WindowRideToggleMusic(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1;
|
||
set_operating_setting(w->rideId, RideSetSetting::Music, activateMusic);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1ED7
|
||
*/
|
||
static void WindowRideMusicMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_PLAY_MUSIC:
|
||
WindowRideToggleMusic(w);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AF4A2
|
||
*/
|
||
static void WindowRideMusicResize(rct_window* w)
|
||
{
|
||
w->flags |= WF_RESIZABLE;
|
||
window_set_resize(w, 316, 81, 316, 81);
|
||
}
|
||
|
||
static std::string GetMusicString(ObjectEntryIndex musicObjectIndex)
|
||
{
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex));
|
||
|
||
auto name = musicObj->NameStringId;
|
||
return format_string(name, {});
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1EFC
|
||
*/
|
||
static void WindowRideMusicMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
if (widgetIndex != WIDX_MUSIC_DROPDOWN)
|
||
return;
|
||
|
||
auto dropdownWidget = widget - 1;
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
// Construct list of available music
|
||
auto& musicOrder = window_ride_current_music_style_order;
|
||
musicOrder.clear();
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
for (ObjectEntryIndex i = 0; i < MAX_MUSIC_OBJECTS; i++)
|
||
{
|
||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, i));
|
||
if (musicObj != nullptr)
|
||
{
|
||
// Hide custom music if the WAV file does not exist
|
||
auto originalStyleId = musicObj->GetOriginalStyleId();
|
||
if (originalStyleId.has_value()
|
||
&& (originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_1 || originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_2))
|
||
{
|
||
auto numTracks = musicObj->GetTrackCount();
|
||
if (numTracks != 0)
|
||
{
|
||
auto track0 = musicObj->GetTrack(0);
|
||
if (!track0->Asset.IsAvailable())
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (gCheatsUnlockOperatingLimits || musicObj->SupportsRideType(ride->type))
|
||
{
|
||
musicOrder.push_back(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Sort available music by the alphabetical order
|
||
std::stable_sort(musicOrder.begin(), musicOrder.end(), [](const ObjectEntryIndex& a, const ObjectEntryIndex& b) {
|
||
return String::Compare(GetMusicString(b), GetMusicString(a), false) > 0;
|
||
});
|
||
|
||
// Setup dropdown list
|
||
auto numItems = musicOrder.size();
|
||
for (size_t i = 0; i < numItems; i++)
|
||
{
|
||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i]));
|
||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||
gDropdownItems[i].Args = musicObj->NameStringId;
|
||
}
|
||
|
||
WindowDropdownShowTextCustomWidth(
|
||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
|
||
|
||
// Set currently checked item
|
||
for (size_t i = 0; i < numItems; i++)
|
||
{
|
||
if (musicOrder[i] == ride->music)
|
||
{
|
||
Dropdown::SetChecked(static_cast<int32_t>(i), true);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1F03
|
||
*/
|
||
static void WindowRideMusicDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0
|
||
&& static_cast<size_t>(dropdownIndex) < window_ride_current_music_style_order.size())
|
||
{
|
||
auto musicStyle = window_ride_current_music_style_order[dropdownIndex];
|
||
set_operating_setting(w->rideId, RideSetSetting::MusicType, musicStyle);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B2198
|
||
*/
|
||
static void WindowRideMusicUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_6);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1DEA
|
||
*/
|
||
static void WindowRideMusicInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
// Set selected music
|
||
rct_string_id musicName = STR_NONE;
|
||
auto& objManager = GetContext()->GetObjectManager();
|
||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, ride->music));
|
||
if (musicObj != nullptr)
|
||
{
|
||
musicName = musicObj->NameStringId;
|
||
}
|
||
window_ride_music_widgets[WIDX_MUSIC].text = musicName;
|
||
|
||
// Set music activated
|
||
auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0;
|
||
if (isMusicActivated)
|
||
{
|
||
w->pressed_widgets |= (1ULL << WIDX_PLAY_MUSIC);
|
||
w->disabled_widgets &= ~(1ULL << WIDX_MUSIC);
|
||
w->disabled_widgets &= ~(1ULL << WIDX_MUSIC_DROPDOWN);
|
||
}
|
||
else
|
||
{
|
||
w->pressed_widgets &= ~(1ULL << WIDX_PLAY_MUSIC);
|
||
w->disabled_widgets |= (1ULL << WIDX_MUSIC);
|
||
w->disabled_widgets |= (1ULL << WIDX_MUSIC_DROPDOWN);
|
||
}
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006B1ECC
|
||
*/
|
||
static void WindowRideMusicPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Measurements
|
||
|
||
static rct_string_id GetRatingName(ride_rating rating)
|
||
{
|
||
int32_t index = std::clamp<int32_t>(rating >> 8, 0, static_cast<int32_t>(std::size(RatingNames)) - 1);
|
||
return RatingNames[index];
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006D2804
|
||
when al == 0*/
|
||
static void CancelScenerySelection()
|
||
{
|
||
gGamePaused &= ~GAME_PAUSED_SAVING_TRACK;
|
||
gTrackDesignSaveMode = false;
|
||
OpenRCT2::Audio::Resume();
|
||
|
||
rct_window* main_w = window_get_main();
|
||
if (main_w != nullptr)
|
||
{
|
||
main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE);
|
||
}
|
||
|
||
gfx_invalidate_screen();
|
||
tool_cancel();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006D27A3
|
||
*/
|
||
static void SetupScenerySelection(rct_window* w)
|
||
{
|
||
if (gTrackDesignSaveMode)
|
||
{
|
||
CancelScenerySelection();
|
||
}
|
||
|
||
while (tool_set(*w, WIDX_BACKGROUND, Tool::Crosshair))
|
||
;
|
||
|
||
gTrackDesignSaveRideIndex = w->rideId;
|
||
|
||
track_design_save_init();
|
||
gGamePaused |= GAME_PAUSED_SAVING_TRACK;
|
||
gTrackDesignSaveMode = true;
|
||
|
||
OpenRCT2::Audio::StopAll();
|
||
|
||
rct_window* w_main = window_get_main();
|
||
if (w_main != nullptr)
|
||
{
|
||
w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE);
|
||
}
|
||
|
||
gfx_invalidate_screen();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006D3026
|
||
*/
|
||
static void WindowRideMeasurementsDesignReset()
|
||
{
|
||
track_design_save_reset_scenery();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006D303D
|
||
*/
|
||
static void WindowRideMeasurementsDesignSelectNearbyScenery()
|
||
{
|
||
track_design_save_select_nearby_scenery(gTrackDesignSaveRideIndex);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4DA
|
||
*/
|
||
void WindowRideMeasurementsDesignCancel()
|
||
{
|
||
if (gTrackDesignSaveMode)
|
||
{
|
||
CancelScenerySelection();
|
||
}
|
||
}
|
||
|
||
static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path)
|
||
{
|
||
if (result == MODAL_RESULT_OK)
|
||
{
|
||
track_repository_scan();
|
||
}
|
||
gfx_invalidate_screen();
|
||
};
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4CD
|
||
*/
|
||
static void WindowRideMeasurementsDesignSave(rct_window* w)
|
||
{
|
||
TrackDesignState tds{};
|
||
|
||
Ride* ride = get_ride(w->rideId);
|
||
_trackDesign = ride->SaveToTrackDesign(tds);
|
||
if (!_trackDesign)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (gTrackDesignSaveMode)
|
||
{
|
||
auto errMessage = _trackDesign->CreateTrackDesignScenery(tds);
|
||
if (errMessage != STR_NONE)
|
||
{
|
||
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage, {});
|
||
return;
|
||
}
|
||
}
|
||
|
||
auto trackName = ride->GetName();
|
||
auto intent = Intent(WC_LOADSAVE);
|
||
intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK);
|
||
intent.putExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get());
|
||
intent.putExtra(INTENT_EXTRA_PATH, trackName);
|
||
intent.putExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast<void*>(&TrackDesignCallback));
|
||
|
||
context_open_intent(&intent);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4DA
|
||
*/
|
||
static void WindowRideMeasurementsClose(rct_window* w)
|
||
{
|
||
WindowRideMeasurementsDesignCancel();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD478
|
||
*/
|
||
static void WindowRideMeasurementsMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_SELECT_NEARBY_SCENERY:
|
||
WindowRideMeasurementsDesignSelectNearbyScenery();
|
||
break;
|
||
case WIDX_RESET_SELECTION:
|
||
WindowRideMeasurementsDesignReset();
|
||
break;
|
||
case WIDX_SAVE_DESIGN:
|
||
WindowRideMeasurementsDesignSave(w);
|
||
break;
|
||
case WIDX_CANCEL_DESIGN:
|
||
WindowRideMeasurementsDesignCancel();
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD564
|
||
*/
|
||
static void WindowRideMeasurementsResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 234, 316, 234);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4AB
|
||
*/
|
||
static void WindowRideMeasurementsMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
if (widgetIndex != WIDX_SAVE_TRACK_DESIGN)
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
gDropdownItems[0].Format = STR_SAVE_TRACK_DESIGN_ITEM;
|
||
gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM;
|
||
|
||
WindowDropdownShowText(
|
||
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1],
|
||
Dropdown::Flag::StayOpen, 2);
|
||
gDropdownDefaultIndex = 0;
|
||
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))
|
||
{
|
||
// Disable saving without scenery if we're a flat ride
|
||
Dropdown::SetDisabled(0, true);
|
||
gDropdownDefaultIndex = 1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4B2
|
||
*/
|
||
static void WindowRideMeasurementsDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||
{
|
||
if (widgetIndex != WIDX_SAVE_TRACK_DESIGN)
|
||
return;
|
||
|
||
if (dropdownIndex == -1)
|
||
dropdownIndex = gDropdownHighlightedIndex;
|
||
|
||
if (dropdownIndex == 0)
|
||
{
|
||
WindowRideMeasurementsDesignSave(w);
|
||
}
|
||
else
|
||
SetupScenerySelection(w);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD5DD
|
||
*/
|
||
static void WindowRideMeasurementsUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_7);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006D2AE7
|
||
*/
|
||
static void WindowRideMeasurementsTooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
|
||
{
|
||
_lastSceneryX = screenCoords.x;
|
||
_lastSceneryY = screenCoords.y;
|
||
_collectTrackDesignScenery = true; // Default to true in case user does not select anything valid
|
||
|
||
constexpr auto flags = EnumsToFlags(
|
||
ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall,
|
||
ViewportInteractionItem::LargeScenery);
|
||
auto info = get_map_coordinates_from_pos(screenCoords, flags);
|
||
switch (info.SpriteType)
|
||
{
|
||
case ViewportInteractionItem::Scenery:
|
||
case ViewportInteractionItem::LargeScenery:
|
||
case ViewportInteractionItem::Wall:
|
||
case ViewportInteractionItem::Footpath:
|
||
_collectTrackDesignScenery = !track_design_save_contains_tile_element(info.Element);
|
||
track_design_save_select_tile_element(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void WindowRideMeasurementsTooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
|
||
{
|
||
if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY)
|
||
return;
|
||
_lastSceneryX = screenCoords.x;
|
||
_lastSceneryY = screenCoords.y;
|
||
|
||
auto flags = EnumsToFlags(
|
||
ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall,
|
||
ViewportInteractionItem::LargeScenery);
|
||
auto info = get_map_coordinates_from_pos(screenCoords, flags);
|
||
switch (info.SpriteType)
|
||
{
|
||
case ViewportInteractionItem::Scenery:
|
||
case ViewportInteractionItem::LargeScenery:
|
||
case ViewportInteractionItem::Wall:
|
||
case ViewportInteractionItem::Footpath:
|
||
track_design_save_select_tile_element(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD4DA
|
||
*/
|
||
static void WindowRideMeasurementsToolabort(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
WindowRideMeasurementsDesignCancel();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ACDBC
|
||
*/
|
||
static void WindowRideMeasurementsInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE;
|
||
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty;
|
||
if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == rideId)
|
||
{
|
||
window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button;
|
||
window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button;
|
||
window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button;
|
||
window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button;
|
||
}
|
||
else
|
||
{
|
||
window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty;
|
||
window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty;
|
||
window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty;
|
||
window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty;
|
||
|
||
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn;
|
||
w->disabled_widgets |= (1ULL << WIDX_SAVE_TRACK_DESIGN);
|
||
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
|
||
{
|
||
if (ride->excitement != RIDE_RATING_UNDEFINED)
|
||
{
|
||
w->disabled_widgets &= ~(1ULL << WIDX_SAVE_TRACK_DESIGN);
|
||
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN;
|
||
}
|
||
}
|
||
}
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ACF07
|
||
*/
|
||
static void WindowRideMeasurementsPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button)
|
||
{
|
||
rct_widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND];
|
||
|
||
ScreenCoordsXY widgetCoords(w->windowPos.x + widget->width() / 2, w->windowPos.y + widget->top + 40);
|
||
DrawTextWrapped(dpi, widgetCoords, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE });
|
||
|
||
widgetCoords.x = w->windowPos.x + 4;
|
||
widgetCoords.y = w->windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17;
|
||
gfx_fill_rect_inset(
|
||
dpi, { widgetCoords, { w->windowPos.x + 312, widgetCoords.y + 1 } }, w->colours[1], INSET_RECT_FLAG_BORDER_INSET);
|
||
}
|
||
else
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto screenCoords = w->windowPos
|
||
+ ScreenCoordsXY{ window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4,
|
||
window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4 };
|
||
|
||
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
|
||
{
|
||
// Excitement
|
||
rct_string_id ratingName = GetRatingName(ride->excitement);
|
||
auto ft = Formatter();
|
||
ft.Add<uint32_t>(ride->excitement);
|
||
ft.Add<rct_string_id>(ratingName);
|
||
rct_string_id stringId = !ride_has_ratings(ride) ? STR_EXCITEMENT_RATING_NOT_YET_AVAILABLE : STR_EXCITEMENT_RATING;
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Intensity
|
||
ratingName = GetRatingName(ride->intensity);
|
||
ft = Formatter();
|
||
ft.Add<uint32_t>(ride->intensity);
|
||
ft.Add<rct_string_id>(ratingName);
|
||
|
||
stringId = STR_INTENSITY_RATING;
|
||
if (!ride_has_ratings(ride))
|
||
stringId = STR_INTENSITY_RATING_NOT_YET_AVAILABLE;
|
||
else if (ride->intensity >= RIDE_RATING(10, 00))
|
||
stringId = STR_INTENSITY_RATING_RED;
|
||
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Nausea
|
||
ratingName = GetRatingName(ride->nausea);
|
||
ft = Formatter();
|
||
ft.Add<uint32_t>(ride->nausea);
|
||
ft.Add<rct_string_id>(ratingName);
|
||
stringId = !ride_has_ratings(ride) ? STR_NAUSEA_RATING_NOT_YET_AVAILABLE : STR_NAUSEA_RATING;
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += 2 * LIST_ROW_HEIGHT;
|
||
|
||
// Horizontal rule
|
||
gfx_fill_rect_inset(
|
||
dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, w->colours[1],
|
||
INSET_RECT_FLAG_BORDER_INSET);
|
||
|
||
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS))
|
||
{
|
||
if (ride->type == RIDE_TYPE_MINI_GOLF)
|
||
{
|
||
// Holes
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(ride->holes);
|
||
DrawTextBasic(dpi, screenCoords, STR_HOLES, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
else
|
||
{
|
||
// Max speed
|
||
ft = Formatter();
|
||
ft.Add<int32_t>((ride->max_speed * 9) >> 18);
|
||
DrawTextBasic(dpi, screenCoords, STR_MAX_SPEED, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Average speed
|
||
ft = Formatter();
|
||
ft.Add<int32_t>((ride->average_speed * 9) >> 18);
|
||
DrawTextBasic(dpi, screenCoords, STR_AVERAGE_SPEED, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Ride time
|
||
ft = Formatter();
|
||
int32_t numTimes = 0;
|
||
for (int32_t i = 0; i < ride->num_stations; i++)
|
||
{
|
||
StationIndex stationIndex = StationIndex::FromUnderlying(numTimes);
|
||
auto time = ride->GetStation(stationIndex).SegmentTime;
|
||
if (time != 0)
|
||
{
|
||
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY_WITH_SEPARATOR);
|
||
ft.Add<uint16_t>(time);
|
||
numTimes++;
|
||
}
|
||
}
|
||
if (numTimes == 0)
|
||
{
|
||
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY);
|
||
ft.Add<uint16_t>(0);
|
||
numTimes++;
|
||
}
|
||
else
|
||
{
|
||
// sadly, STR_RIDE_TIME_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry
|
||
// therefore we set the last entry to use the no-separator format now, post-format
|
||
ft.Rewind();
|
||
ft.Increment((numTimes - 1) * 4);
|
||
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY);
|
||
}
|
||
ft.Rewind();
|
||
ft.Increment(numTimes * 4);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_TIME, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
// Ride length
|
||
ft = Formatter();
|
||
int32_t numLengths = 0;
|
||
for (int32_t i = 0; i < ride->num_stations; i++)
|
||
{
|
||
StationIndex stationIndex = StationIndex::FromUnderlying(i);
|
||
auto length = ride->GetStation(stationIndex).SegmentLength;
|
||
if (length != 0)
|
||
{
|
||
length >>= 16;
|
||
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR);
|
||
ft.Add<uint16_t>(length & 0xFFFF);
|
||
numLengths++;
|
||
}
|
||
}
|
||
if (numLengths == 0)
|
||
{
|
||
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY);
|
||
ft.Add<uint16_t>(0);
|
||
numLengths++;
|
||
}
|
||
else
|
||
{
|
||
// sadly, STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry
|
||
// therefore we set the last entry to use the no-separator format now, post-format
|
||
ft.Rewind();
|
||
ft.Increment((numLengths - 1) * 4);
|
||
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY);
|
||
}
|
||
ft.Rewind();
|
||
ft.Increment(numLengths * 4);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
ft.Add<uint16_t>(0);
|
||
DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_LENGTH, ft);
|
||
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES))
|
||
{
|
||
// Max. positive vertical G's
|
||
stringId = STR_MAX_POSITIVE_VERTICAL_G;
|
||
|
||
ft = Formatter();
|
||
ft.Add<fixed16_2dp>(ride->max_positive_vertical_g);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Max. negative vertical G's
|
||
stringId = ride->max_negative_vertical_g <= RIDE_G_FORCES_RED_NEG_VERTICAL ? STR_MAX_NEGATIVE_VERTICAL_G_RED
|
||
: STR_MAX_NEGATIVE_VERTICAL_G;
|
||
ft = Formatter();
|
||
ft.Add<int32_t>(ride->max_negative_vertical_g);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Max lateral G's
|
||
stringId = ride->max_lateral_g > RIDE_G_FORCES_RED_LATERAL ? STR_MAX_LATERAL_G_RED : STR_MAX_LATERAL_G;
|
||
ft = Formatter();
|
||
ft.Add<fixed16_2dp>(ride->max_lateral_g);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Total 'air' time
|
||
ft = Formatter();
|
||
ft.Add<fixed32_2dp>(ride->total_air_time * 3);
|
||
DrawTextBasic(dpi, screenCoords, STR_TOTAL_AIR_TIME, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_DROPS))
|
||
{
|
||
// Drops
|
||
auto drops = ride->drops & 0x3F;
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(drops);
|
||
DrawTextBasic(dpi, screenCoords, STR_DROPS, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Highest drop height
|
||
auto highestDropHeight = (ride->highest_drop_height * 3) / 4;
|
||
ft = Formatter();
|
||
ft.Add<int32_t>(highestDropHeight);
|
||
DrawTextBasic(dpi, screenCoords, STR_HIGHEST_DROP_HEIGHT, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
if (ride->type != RIDE_TYPE_MINI_GOLF)
|
||
{
|
||
// Inversions
|
||
if (ride->inversions != 0)
|
||
{
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(ride->inversions);
|
||
DrawTextBasic(dpi, screenCoords, STR_INVERSIONS, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DrawTextBasic(dpi, screenCoords, STR_NO_TEST_RESULTS_YET);
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Graphs
|
||
|
||
enum
|
||
{
|
||
GRAPH_VELOCITY,
|
||
GRAPH_ALTITUDE,
|
||
GRAPH_VERTICAL,
|
||
GRAPH_LATERAL
|
||
};
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE8A6
|
||
*/
|
||
static void WindowRideSetGraph(rct_window* w, int32_t type)
|
||
{
|
||
if ((w->list_information_type & 0xFF) == type)
|
||
{
|
||
w->list_information_type ^= 0x8000;
|
||
}
|
||
else
|
||
{
|
||
w->list_information_type &= 0xFF00;
|
||
w->list_information_type |= type;
|
||
}
|
||
w->Invalidate();
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE85D
|
||
*/
|
||
static void WindowRideGraphsMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE8DA
|
||
*/
|
||
static void WindowRideGraphsResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 182, 500, 450);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE878
|
||
*/
|
||
static void WindowRideGraphsMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_GRAPH_VELOCITY:
|
||
WindowRideSetGraph(w, GRAPH_VELOCITY);
|
||
break;
|
||
case WIDX_GRAPH_ALTITUDE:
|
||
WindowRideSetGraph(w, GRAPH_ALTITUDE);
|
||
break;
|
||
case WIDX_GRAPH_VERTICAL:
|
||
WindowRideSetGraph(w, GRAPH_VERTICAL);
|
||
break;
|
||
case WIDX_GRAPH_LATERAL:
|
||
WindowRideSetGraph(w, GRAPH_LATERAL);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE95D
|
||
*/
|
||
static void WindowRideGraphsUpdate(rct_window* w)
|
||
{
|
||
rct_widget* widget;
|
||
int32_t x;
|
||
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_8);
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_GRAPH);
|
||
|
||
widget = &window_ride_graphs_widgets[WIDX_GRAPH];
|
||
x = w->scrolls[0].h_left;
|
||
if (!(w->list_information_type & 0x8000))
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
RideMeasurement* measurement{};
|
||
std::tie(measurement, std::ignore) = ride->GetMeasurement();
|
||
x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3);
|
||
}
|
||
}
|
||
|
||
w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - (widget->width() - 2));
|
||
WidgetScrollUpdateThumbs(*w, WIDX_GRAPH);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEA75
|
||
*/
|
||
static void WindowRideGraphsScrollgetheight(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
|
||
{
|
||
window_event_invalidate_call(w);
|
||
|
||
// Set minimum size
|
||
*width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2;
|
||
|
||
// Get measurement size
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
RideMeasurement* measurement{};
|
||
std::tie(measurement, std::ignore) = ride->GetMeasurement();
|
||
if (measurement != nullptr)
|
||
{
|
||
*width = std::max<int32_t>(*width, measurement->num_items);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE953
|
||
*/
|
||
static void WindowRideGraphs15(rct_window* w, int32_t scrollIndex, int32_t scrollAreaType)
|
||
{
|
||
w->list_information_type |= 0x8000;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AEA05
|
||
*/
|
||
static OpenRCT2String WindowRideGraphsTooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback)
|
||
{
|
||
if (widgetIndex == WIDX_GRAPH)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
auto [measurement, message] = ride->GetMeasurement();
|
||
if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING))
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Increment(2);
|
||
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number);
|
||
ft.Add<uint16_t>(measurement->vehicle_index + 1);
|
||
return { fallback, ft };
|
||
}
|
||
|
||
return message;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return { STR_NONE, {} };
|
||
}
|
||
return { fallback, {} };
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE372
|
||
*/
|
||
static void WindowRideGraphsInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
// Set pressed graph button type
|
||
w->pressed_widgets &= ~(1ULL << WIDX_GRAPH_VELOCITY);
|
||
w->pressed_widgets &= ~(1ULL << WIDX_GRAPH_ALTITUDE);
|
||
w->pressed_widgets &= ~(1ULL << WIDX_GRAPH_VERTICAL);
|
||
w->pressed_widgets &= ~(1ULL << WIDX_GRAPH_LATERAL);
|
||
w->pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (w->list_information_type & 0xFF)));
|
||
|
||
// Hide graph buttons that are not applicable
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES))
|
||
{
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button;
|
||
}
|
||
else
|
||
{
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty;
|
||
}
|
||
|
||
// Anchor graph widget
|
||
auto x = w->width - 4;
|
||
auto y = w->height - BUTTON_FACE_HEIGHT - 8;
|
||
|
||
window_ride_graphs_widgets[WIDX_GRAPH].right = x;
|
||
window_ride_graphs_widgets[WIDX_GRAPH].bottom = y;
|
||
y += 3;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].top = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].top = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].top = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].top = y;
|
||
y += BUTTON_FACE_HEIGHT + 1;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].bottom = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].bottom = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].bottom = y;
|
||
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].bottom = y;
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE4BC
|
||
*/
|
||
static void WindowRideGraphsPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE4C7
|
||
*/
|
||
static void WindowRideGraphsScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
|
||
{
|
||
gfx_clear(dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker);
|
||
|
||
auto widget = &window_ride_graphs_widgets[WIDX_GRAPH];
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
{
|
||
return;
|
||
}
|
||
|
||
auto [measurement, message] = ride->GetMeasurement();
|
||
|
||
if (measurement == nullptr)
|
||
{
|
||
// No measurement message
|
||
ScreenCoordsXY stringCoords(widget->width() / 2, widget->height() / 2 - 5);
|
||
int32_t width = widget->width() - 2;
|
||
DrawTextWrapped(dpi, stringCoords, width, message.str, message.args, { TextAlignment::CENTRE });
|
||
return;
|
||
}
|
||
|
||
// Vertical grid lines
|
||
const uint8_t lightColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_light;
|
||
const uint8_t darkColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_dark;
|
||
|
||
int32_t time = 0;
|
||
for (int32_t x = 0; x < dpi->x + dpi->width; x += 80)
|
||
{
|
||
if (x + 80 >= dpi->x)
|
||
{
|
||
auto coord1 = ScreenCoordsXY{ x, dpi->y };
|
||
auto coord2 = ScreenCoordsXY{ x, dpi->y + dpi->height - 1 };
|
||
gfx_fill_rect(dpi, { coord1, coord2 }, lightColour);
|
||
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 16, 0 }, coord2 + ScreenCoordsXY{ 16, 0 } }, darkColour);
|
||
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 32, 0 }, coord2 + ScreenCoordsXY{ 32, 0 } }, darkColour);
|
||
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 48, 0 }, coord2 + ScreenCoordsXY{ 48, 0 } }, darkColour);
|
||
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 64, 0 }, coord2 + ScreenCoordsXY{ 64, 0 } }, darkColour);
|
||
}
|
||
time += 5;
|
||
}
|
||
|
||
// Horizontal grid lines
|
||
int32_t listType = w->list_information_type & 0xFF;
|
||
int16_t yUnit = window_graphs_y_axi[listType].unit;
|
||
rct_string_id stringID = window_graphs_y_axi[listType].label;
|
||
int16_t yUnitInterval = window_graphs_y_axi[listType].unit_interval;
|
||
int16_t yInterval = window_graphs_y_axi[listType].interval;
|
||
|
||
// Scale modifier
|
||
if (listType == GRAPH_ALTITUDE)
|
||
{
|
||
yUnit -= gMapBaseZ * 3;
|
||
}
|
||
|
||
for (int32_t y = widget->height() - 13; y >= 8; y -= yInterval, yUnit += yUnitInterval)
|
||
{
|
||
// Minor / major line
|
||
int32_t colour = yUnit == 0 ? lightColour : darkColour;
|
||
gfx_fill_rect(dpi, { { dpi->x, y }, { dpi->x + dpi->width - 1, y } }, colour);
|
||
|
||
int16_t scaled_yUnit = yUnit;
|
||
// Scale modifier
|
||
if (listType == GRAPH_ALTITUDE)
|
||
scaled_yUnit /= 2;
|
||
|
||
auto ft = Formatter();
|
||
ft.Add<int16_t>(scaled_yUnit);
|
||
|
||
DrawTextBasic(dpi, { w->scrolls[0].h_left + 1, y - 4 }, stringID, ft, { FontSpriteBase::SMALL });
|
||
}
|
||
|
||
// Time marks
|
||
time = 0;
|
||
for (int32_t x = 0; x < dpi->x + dpi->width; x += 80)
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Add<int32_t>(time);
|
||
if (x + 80 >= dpi->x)
|
||
DrawTextBasic(dpi, { x + 2, 1 }, STR_RIDE_STATS_TIME, ft, { FontSpriteBase::SMALL });
|
||
time += 5;
|
||
}
|
||
|
||
// Plot
|
||
int32_t x = dpi->x;
|
||
int32_t firstPoint, secondPoint;
|
||
// Uses the force limits (used to draw extreme G's in red on measurement tab) to determine if line should be drawn red.
|
||
int32_t intensityThresholdPositive = 0;
|
||
int32_t intensityThresholdNegative = 0;
|
||
for (int32_t width = 0; width < dpi->width; width++, x++)
|
||
{
|
||
if (x < 0 || x >= measurement->num_items - 1)
|
||
continue;
|
||
|
||
constexpr int32_t VerticalGraphHeightOffset = 39;
|
||
constexpr int32_t LateralGraphHeightOffset = 52;
|
||
|
||
switch (listType)
|
||
{
|
||
case GRAPH_VELOCITY:
|
||
firstPoint = measurement->velocity[x] / 2;
|
||
secondPoint = measurement->velocity[x + 1] / 2;
|
||
break;
|
||
case GRAPH_ALTITUDE:
|
||
firstPoint = measurement->altitude[x];
|
||
secondPoint = measurement->altitude[x + 1];
|
||
break;
|
||
case GRAPH_VERTICAL:
|
||
firstPoint = measurement->vertical[x] + VerticalGraphHeightOffset;
|
||
secondPoint = measurement->vertical[x + 1] + VerticalGraphHeightOffset;
|
||
intensityThresholdNegative = (RIDE_G_FORCES_RED_NEG_VERTICAL / 8) + VerticalGraphHeightOffset;
|
||
break;
|
||
case GRAPH_LATERAL:
|
||
firstPoint = measurement->lateral[x] + LateralGraphHeightOffset;
|
||
secondPoint = measurement->lateral[x + 1] + LateralGraphHeightOffset;
|
||
intensityThresholdPositive = (RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset;
|
||
intensityThresholdNegative = -(RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset;
|
||
break;
|
||
default:
|
||
log_error("Wrong graph type %d", listType);
|
||
firstPoint = secondPoint = 0;
|
||
break;
|
||
}
|
||
|
||
// Adjust line to match graph widget position.
|
||
firstPoint = widget->height() - firstPoint - 13;
|
||
secondPoint = widget->height() - secondPoint - 13;
|
||
if (firstPoint > secondPoint)
|
||
{
|
||
std::swap(firstPoint, secondPoint);
|
||
}
|
||
|
||
// Adjust threshold line position as well
|
||
if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL)
|
||
{
|
||
intensityThresholdPositive = widget->height() - intensityThresholdPositive - 13;
|
||
intensityThresholdNegative = widget->height() - intensityThresholdNegative - 13;
|
||
}
|
||
|
||
const bool previousMeasurement = x > measurement->current_item;
|
||
|
||
// Draw the current line in grey.
|
||
gfx_fill_rect(
|
||
dpi, { { x, firstPoint }, { x, secondPoint } }, previousMeasurement ? PALETTE_INDEX_17 : PALETTE_INDEX_21);
|
||
|
||
// Draw red over extreme values (if supported by graph type).
|
||
if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL)
|
||
{
|
||
const auto redLineColour = previousMeasurement ? PALETTE_INDEX_171 : PALETTE_INDEX_173;
|
||
|
||
// Line exceeds negative threshold (at bottom of graph).
|
||
if (secondPoint >= intensityThresholdNegative)
|
||
{
|
||
const auto redLineTop = ScreenCoordsXY{ x, std::max(firstPoint, intensityThresholdNegative) };
|
||
const auto redLineBottom = ScreenCoordsXY{ x, std::max(secondPoint, intensityThresholdNegative) };
|
||
gfx_fill_rect(dpi, { redLineTop, redLineBottom }, redLineColour);
|
||
}
|
||
|
||
// Line exceeds positive threshold (at top of graph).
|
||
if (listType == GRAPH_LATERAL && firstPoint < intensityThresholdPositive)
|
||
{
|
||
const auto redLineTop = ScreenCoordsXY{ x, std::min(firstPoint, intensityThresholdPositive) };
|
||
const auto redLineBottom = ScreenCoordsXY{ x, std::min(secondPoint, intensityThresholdPositive) };
|
||
gfx_fill_rect(dpi, { redLineTop, redLineBottom }, redLineColour);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Income
|
||
|
||
static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH];
|
||
|
||
static void UpdateSamePriceThroughoutFlags(ShopItem shop_item)
|
||
{
|
||
uint64_t newFlags;
|
||
|
||
if (GetShopItemDescriptor(shop_item).IsPhoto())
|
||
{
|
||
newFlags = gSamePriceThroughoutPark;
|
||
newFlags ^= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4);
|
||
auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags);
|
||
GameActions::Execute(&parkSetParameter);
|
||
}
|
||
else
|
||
{
|
||
newFlags = gSamePriceThroughoutPark;
|
||
newFlags ^= EnumToFlag(shop_item);
|
||
auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags);
|
||
GameActions::Execute(&parkSetParameter);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADEFD
|
||
*/
|
||
static void WindowRideIncomeTogglePrimaryPrice(rct_window* w)
|
||
{
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
ShopItem shop_item;
|
||
if (ride->type == RIDE_TYPE_TOILETS)
|
||
{
|
||
shop_item = ShopItem::Admission;
|
||
}
|
||
else
|
||
{
|
||
auto rideEntry = get_ride_entry(ride->subtype);
|
||
if (rideEntry != nullptr)
|
||
{
|
||
shop_item = rideEntry->shop_item[0];
|
||
if (shop_item == ShopItem::None)
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
UpdateSamePriceThroughoutFlags(shop_item);
|
||
|
||
auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[0], true);
|
||
GameActions::Execute(&rideSetPriceAction);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE06E
|
||
*/
|
||
static void WindowRideIncomeToggleSecondaryPrice(rct_window* w)
|
||
{
|
||
const auto rideId = w->rideId;
|
||
auto ride = get_ride(rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = get_ride_entry(ride->subtype);
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
auto shop_item = rideEntry->shop_item[1];
|
||
if (shop_item == ShopItem::None)
|
||
shop_item = ride->GetRideTypeDescriptor().PhotoItem;
|
||
|
||
UpdateSamePriceThroughoutFlags(shop_item);
|
||
|
||
auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[1], false);
|
||
GameActions::Execute(&rideSetPriceAction);
|
||
}
|
||
|
||
static void WindowRideIncomeSetPrimaryPrice(rct_window* w, money16 price)
|
||
{
|
||
auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, true);
|
||
GameActions::Execute(&rideSetPriceAction);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE1E4
|
||
*/
|
||
static void WindowRideIncomeIncreasePrimaryPrice(rct_window* w)
|
||
{
|
||
if (!WindowRideIncomeCanModifyPrimaryPrice(w))
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
money16 price = ride->price[0];
|
||
if (price < 20.00_GBP)
|
||
price++;
|
||
|
||
WindowRideIncomeSetPrimaryPrice(w, price);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE237
|
||
*/
|
||
static void WindowRideIncomeDecreasePrimaryPrice(rct_window* w)
|
||
{
|
||
if (!WindowRideIncomeCanModifyPrimaryPrice(w))
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
money16 price = ride->price[0];
|
||
if (price > 0.00_GBP)
|
||
price--;
|
||
|
||
WindowRideIncomeSetPrimaryPrice(w, price);
|
||
}
|
||
|
||
static money16 WindowRideIncomeGetSecondaryPrice(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return 0;
|
||
|
||
money16 price = ride->price[1];
|
||
return price;
|
||
}
|
||
|
||
static void WindowRideIncomeSetSecondaryPrice(rct_window* w, money16 price)
|
||
{
|
||
auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, false);
|
||
GameActions::Execute(&rideSetPriceAction);
|
||
}
|
||
|
||
static bool WindowRideIncomeCanModifyPrimaryPrice(rct_window* w)
|
||
{
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return false;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
return park_ride_prices_unlocked() || ride->type == RIDE_TYPE_TOILETS
|
||
|| (rideEntry != nullptr && rideEntry->shop_item[0] != ShopItem::None);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE269
|
||
*/
|
||
static void WindowRideIncomeIncreaseSecondaryPrice(rct_window* w)
|
||
{
|
||
money16 price = WindowRideIncomeGetSecondaryPrice(w);
|
||
|
||
if (price < 20.00_GBP)
|
||
price++;
|
||
|
||
WindowRideIncomeSetSecondaryPrice(w, price);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE28D
|
||
*/
|
||
static void WindowRideIncomeDecreaseSecondaryPrice(rct_window* w)
|
||
{
|
||
money16 price = WindowRideIncomeGetSecondaryPrice(w);
|
||
|
||
if (price > 0.00_GBP)
|
||
price--;
|
||
|
||
WindowRideIncomeSetSecondaryPrice(w, price);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADEA9
|
||
*/
|
||
static void WindowRideIncomeMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_PRIMARY_PRICE:
|
||
{
|
||
if (!WindowRideIncomeCanModifyPrimaryPrice(w))
|
||
return;
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
money_to_string(static_cast<money32>(ride->price[0]), _moneyInputText, MONEY_STRING_MAXLENGTH, true);
|
||
WindowTextInputRawOpen(
|
||
w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText,
|
||
MONEY_STRING_MAXLENGTH);
|
||
}
|
||
break;
|
||
}
|
||
case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK:
|
||
WindowRideIncomeTogglePrimaryPrice(w);
|
||
break;
|
||
case WIDX_SECONDARY_PRICE:
|
||
{
|
||
money32 price32 = static_cast<money32>(WindowRideIncomeGetSecondaryPrice(w));
|
||
|
||
money_to_string(price32, _moneyInputText, MONEY_STRING_MAXLENGTH, true);
|
||
WindowTextInputRawOpen(
|
||
w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH);
|
||
}
|
||
break;
|
||
case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK:
|
||
WindowRideIncomeToggleSecondaryPrice(w);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE2F8
|
||
*/
|
||
static void WindowRideIncomeResize(rct_window* w)
|
||
{
|
||
window_set_resize(w, 316, 194, 316, 194);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADED4
|
||
*/
|
||
static void WindowRideIncomeMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_PRIMARY_PRICE_INCREASE:
|
||
WindowRideIncomeIncreasePrimaryPrice(w);
|
||
break;
|
||
case WIDX_PRIMARY_PRICE_DECREASE:
|
||
WindowRideIncomeDecreasePrimaryPrice(w);
|
||
break;
|
||
case WIDX_SECONDARY_PRICE_INCREASE:
|
||
WindowRideIncomeIncreaseSecondaryPrice(w);
|
||
break;
|
||
case WIDX_SECONDARY_PRICE_DECREASE:
|
||
WindowRideIncomeDecreaseSecondaryPrice(w);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AE2BF
|
||
*/
|
||
static void WindowRideIncomeUpdate(rct_window* w)
|
||
{
|
||
w->frame_no++;
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_9);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME)
|
||
{
|
||
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME;
|
||
w->Invalidate();
|
||
}
|
||
}
|
||
|
||
static void WindowRideIncomeTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
|
||
{
|
||
if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr)
|
||
return;
|
||
|
||
money32 price = string_to_money(text);
|
||
if (price == MONEY32_UNDEFINED)
|
||
{
|
||
return;
|
||
}
|
||
|
||
price = std::clamp(price, 0.00_GBP, 20.00_GBP);
|
||
money16 price16 = static_cast<money16>(price);
|
||
|
||
if (widgetIndex == WIDX_PRIMARY_PRICE)
|
||
{
|
||
WindowRideIncomeSetPrimaryPrice(w, price16);
|
||
}
|
||
else
|
||
{
|
||
WindowRideIncomeSetSecondaryPrice(w, price16);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADAA3
|
||
*/
|
||
static void WindowRideIncomeInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
w->widgets[WIDX_TITLE].text = STR_ARG_18_STRINGID;
|
||
|
||
auto ft = Formatter::Common();
|
||
ft.Increment(18);
|
||
ride->FormatNameTo(ft);
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
// Primary item
|
||
w->pressed_widgets &= ~(1ULL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK);
|
||
w->disabled_widgets &= ~(1ULL << WIDX_PRIMARY_PRICE);
|
||
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE;
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_NONE;
|
||
|
||
// If ride prices are locked, do not allow setting the price, unless we're dealing with a shop or toilet.
|
||
if (!park_ride_prices_unlocked() && rideEntry->shop_item[0] == ShopItem::None && ride->type != RIDE_TYPE_TOILETS)
|
||
{
|
||
w->disabled_widgets |= (1ULL << WIDX_PRIMARY_PRICE);
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP;
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP;
|
||
}
|
||
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = STR_RIDE_INCOME_ADMISSION_PRICE;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO;
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty;
|
||
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_BOTTOM_TOOLBAR_CASH;
|
||
money16 ridePrimaryPrice = ride_get_price(ride);
|
||
ft.Rewind();
|
||
ft.Add<money64>(ridePrimaryPrice);
|
||
if (ridePrimaryPrice == 0)
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE;
|
||
|
||
ShopItem primaryItem = ShopItem::Admission;
|
||
if (ride->type == RIDE_TYPE_TOILETS || ((primaryItem = rideEntry->shop_item[0]) != ShopItem::None))
|
||
{
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox;
|
||
|
||
if (shop_item_has_common_price(primaryItem))
|
||
w->pressed_widgets |= (1ULL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK);
|
||
|
||
window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = GetShopItemDescriptor(primaryItem).Naming.PriceLabel;
|
||
}
|
||
|
||
// Get secondary item
|
||
auto secondaryItem = ride->GetRideTypeDescriptor().PhotoItem;
|
||
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO))
|
||
{
|
||
if ((secondaryItem = rideEntry->shop_item[1]) != ShopItem::None)
|
||
{
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem)
|
||
.Naming.PriceLabel;
|
||
}
|
||
}
|
||
|
||
if (secondaryItem == ShopItem::None)
|
||
{
|
||
// Hide secondary item widgets
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty;
|
||
}
|
||
else
|
||
{
|
||
// Set same price throughout park checkbox
|
||
w->pressed_widgets &= ~(1ULL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK);
|
||
if (shop_item_has_common_price(secondaryItem))
|
||
w->pressed_widgets |= (1ULL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK);
|
||
|
||
// Show widgets
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button;
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox;
|
||
|
||
// Set secondary item price
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_RIDE_SECONDARY_PRICE_VALUE;
|
||
ft.Rewind();
|
||
ft.Increment(10);
|
||
ft.Add<money64>(ride->price[1]);
|
||
if (ride->price[1] == 0)
|
||
window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_FREE;
|
||
}
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADCE5
|
||
*/
|
||
static void WindowRideIncomePaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
rct_string_id stringId;
|
||
money64 profit;
|
||
ShopItem primaryItem, secondaryItem;
|
||
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto rideEntry = ride->GetRideEntry();
|
||
if (rideEntry == nullptr)
|
||
return;
|
||
|
||
auto screenCoords = w->windowPos
|
||
+ ScreenCoordsXY{ window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4,
|
||
window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33 };
|
||
|
||
// Primary item profit / loss per item sold
|
||
primaryItem = rideEntry->shop_item[0];
|
||
if (primaryItem != ShopItem::None)
|
||
{
|
||
profit = ride->price[0];
|
||
|
||
stringId = STR_PROFIT_PER_ITEM_SOLD;
|
||
profit -= GetShopItemDescriptor(primaryItem).Cost;
|
||
if (profit < 0)
|
||
{
|
||
profit *= -1;
|
||
stringId = STR_LOSS_PER_ITEM_SOLD;
|
||
}
|
||
|
||
auto ft = Formatter();
|
||
ft.Add<money64>(profit);
|
||
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
}
|
||
screenCoords.y += 44;
|
||
|
||
// Secondary item profit / loss per item sold
|
||
secondaryItem = ride->GetRideTypeDescriptor().PhotoItem;
|
||
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO))
|
||
secondaryItem = rideEntry->shop_item[1];
|
||
|
||
if (secondaryItem != ShopItem::None)
|
||
{
|
||
profit = ride->price[1];
|
||
|
||
stringId = STR_PROFIT_PER_ITEM_SOLD;
|
||
profit -= GetShopItemDescriptor(secondaryItem).Cost;
|
||
if (profit < 0)
|
||
{
|
||
profit *= -1;
|
||
stringId = STR_LOSS_PER_ITEM_SOLD;
|
||
}
|
||
|
||
auto ft = Formatter();
|
||
ft.Add<money64>(profit);
|
||
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
}
|
||
screenCoords.y += 18;
|
||
|
||
// Income per hour
|
||
if (ride->income_per_hour != MONEY64_UNDEFINED)
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Add<money64>(ride->income_per_hour);
|
||
|
||
DrawTextBasic(dpi, screenCoords, STR_INCOME_PER_HOUR, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
// Running cost per hour
|
||
money64 costPerHour = ride->upkeep_cost * 16;
|
||
stringId = ride->upkeep_cost == MONEY16_UNDEFINED ? STR_RUNNING_COST_UNKNOWN : STR_RUNNING_COST_PER_HOUR;
|
||
auto ft = Formatter();
|
||
ft.Add<money64>(costPerHour);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Profit per hour
|
||
if (ride->profit != MONEY64_UNDEFINED)
|
||
{
|
||
ft = Formatter();
|
||
ft.Add<money64>(ride->profit);
|
||
DrawTextBasic(dpi, screenCoords, STR_PROFIT_PER_HOUR, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
screenCoords.y += 5;
|
||
|
||
// Total profit
|
||
ft = Formatter();
|
||
ft.Add<money64>(ride->total_profit);
|
||
DrawTextBasic(dpi, screenCoords, STR_TOTAL_PROFIT, ft);
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Customer
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD986
|
||
*/
|
||
static void WindowRideCustomerMouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||
{
|
||
switch (widgetIndex)
|
||
{
|
||
case WIDX_CLOSE:
|
||
window_close(w);
|
||
break;
|
||
case WIDX_TAB_1:
|
||
case WIDX_TAB_2:
|
||
case WIDX_TAB_3:
|
||
case WIDX_TAB_4:
|
||
case WIDX_TAB_5:
|
||
case WIDX_TAB_6:
|
||
case WIDX_TAB_7:
|
||
case WIDX_TAB_8:
|
||
case WIDX_TAB_9:
|
||
case WIDX_TAB_10:
|
||
WindowRideSetPage(w, widgetIndex - WIDX_TAB_1);
|
||
break;
|
||
case WIDX_SHOW_GUESTS_THOUGHTS:
|
||
{
|
||
auto intent = Intent(WC_GUEST_LIST);
|
||
intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast<int32_t>(GuestListFilterType::GuestsThinkingAboutRide));
|
||
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
|
||
context_open_intent(&intent);
|
||
break;
|
||
}
|
||
case WIDX_SHOW_GUESTS_ON_RIDE:
|
||
{
|
||
auto intent = Intent(WC_GUEST_LIST);
|
||
intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast<int32_t>(GuestListFilterType::GuestsOnRide));
|
||
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
|
||
context_open_intent(&intent);
|
||
break;
|
||
}
|
||
case WIDX_SHOW_GUESTS_QUEUING:
|
||
{
|
||
auto intent = Intent(WC_GUEST_LIST);
|
||
intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast<int32_t>(GuestListFilterType::GuestsInQueue));
|
||
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
|
||
context_open_intent(&intent);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006ADA29
|
||
*/
|
||
static void WindowRideCustomerResize(rct_window* w)
|
||
{
|
||
w->flags |= WF_RESIZABLE;
|
||
window_set_resize(w, 316, 163, 316, 163);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD9DD
|
||
*/
|
||
static void WindowRideCustomerUpdate(rct_window* w)
|
||
{
|
||
w->picked_peep_frame++;
|
||
if (w->picked_peep_frame >= 24)
|
||
w->picked_peep_frame = 0;
|
||
|
||
window_event_invalidate_call(w);
|
||
widget_invalidate(w, WIDX_TAB_10);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER)
|
||
{
|
||
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER;
|
||
w->Invalidate();
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD5F8
|
||
*/
|
||
static void WindowRideCustomerInvalidate(rct_window* w)
|
||
{
|
||
auto widgets = window_ride_page_widgets[w->page];
|
||
if (w->widgets != widgets)
|
||
{
|
||
w->widgets = widgets;
|
||
WindowInitScrollWidgets(*w);
|
||
}
|
||
|
||
WindowRideSetPressedTab(w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride != nullptr)
|
||
{
|
||
auto ft = Formatter::Common();
|
||
ride->FormatNameTo(ft);
|
||
|
||
window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn;
|
||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
|
||
{
|
||
window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty;
|
||
window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty;
|
||
}
|
||
else
|
||
{
|
||
window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn;
|
||
window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn;
|
||
}
|
||
|
||
WindowRideAnchorBorderWidgets(w);
|
||
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* rct2: 0x006AD6CD
|
||
*/
|
||
static void WindowRideCustomerPaint(rct_window* w, rct_drawpixelinfo* dpi)
|
||
{
|
||
ShopItem shopItem;
|
||
int16_t popularity, satisfaction, queueTime;
|
||
rct_string_id stringId;
|
||
|
||
WindowDrawWidgets(*w, dpi);
|
||
WindowRideDrawTabImages(dpi, w);
|
||
|
||
auto ride = get_ride(w->rideId);
|
||
if (ride == nullptr)
|
||
return;
|
||
|
||
auto screenCoords = w->windowPos
|
||
+ ScreenCoordsXY{ window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4,
|
||
window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4 };
|
||
|
||
// Customers currently on ride
|
||
if (ride->IsRide())
|
||
{
|
||
auto ft = Formatter();
|
||
ft.Add<int16_t>(ride->num_riders);
|
||
DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_ON_RIDE, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
// Customers per hour
|
||
auto ft = Formatter();
|
||
ft.Add<int32_t>(ride_customers_per_hour(ride));
|
||
DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_PER_HOUR, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Popularity
|
||
popularity = ride->popularity;
|
||
if (popularity == 255)
|
||
{
|
||
stringId = STR_POPULARITY_UNKNOWN;
|
||
}
|
||
else
|
||
{
|
||
stringId = STR_POPULARITY_PERCENT;
|
||
popularity *= 4;
|
||
}
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(popularity);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Satisfaction
|
||
satisfaction = ride->satisfaction;
|
||
if (satisfaction == 255)
|
||
{
|
||
stringId = STR_SATISFACTION_UNKNOWN;
|
||
}
|
||
else
|
||
{
|
||
stringId = STR_SATISFACTION_PERCENT;
|
||
satisfaction *= 5;
|
||
}
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(satisfaction);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Queue time
|
||
if (ride->IsRide())
|
||
{
|
||
queueTime = ride->GetMaxQueueTime();
|
||
stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES;
|
||
ft = Formatter();
|
||
ft.Add<int32_t>(queueTime);
|
||
screenCoords.y += DrawTextWrapped(dpi, screenCoords, 308, stringId, ft, { TextAlignment::LEFT });
|
||
screenCoords.y += 5;
|
||
}
|
||
|
||
// Primary shop items sold
|
||
shopItem = ride->GetRideEntry()->shop_item[0];
|
||
if (shopItem != ShopItem::None)
|
||
{
|
||
ft = Formatter();
|
||
ft.Add<rct_string_id>(GetShopItemDescriptor(shopItem).Naming.Plural);
|
||
ft.Add<uint32_t>(ride->no_primary_items_sold);
|
||
DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
// Secondary shop items sold / on-ride photos sold
|
||
shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? ride->GetRideTypeDescriptor().PhotoItem
|
||
: ride->GetRideEntry()->shop_item[1];
|
||
if (shopItem != ShopItem::None)
|
||
{
|
||
ft = Formatter();
|
||
ft.Add<rct_string_id>(GetShopItemDescriptor(shopItem).Naming.Plural);
|
||
ft.Add<uint32_t>(ride->no_secondary_items_sold);
|
||
DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
|
||
// Total customers
|
||
ft = Formatter();
|
||
ft.Add<uint32_t>(ride->total_customers);
|
||
DrawTextBasic(dpi, screenCoords, STR_TOTAL_CUSTOMERS, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
|
||
// Guests favourite
|
||
if (ride->IsRide())
|
||
{
|
||
ft = Formatter();
|
||
ft.Add<uint16_t>(ride->guests_favourite);
|
||
stringId = ride->guests_favourite == 1 ? STR_FAVOURITE_RIDE_OF_GUEST : STR_FAVOURITE_RIDE_OF_GUESTS;
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
screenCoords.y += LIST_ROW_HEIGHT;
|
||
}
|
||
screenCoords.y += 2;
|
||
|
||
// Age
|
||
// If the ride has a build date that is in the future, show it as built this year.
|
||
int16_t age = std::max(date_get_year(ride->GetAge()), 0);
|
||
stringId = age == 0 ? STR_BUILT_THIS_YEAR : age == 1 ? STR_BUILT_LAST_YEAR : STR_BUILT_YEARS_AGO;
|
||
ft = Formatter();
|
||
ft.Add<int16_t>(age);
|
||
DrawTextBasic(dpi, screenCoords, stringId, ft);
|
||
}
|
||
|
||
#pragma endregion
|