/***************************************************************************** * Copyright (c) 2014-2019 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace OpenRCT2; 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_MAIN_COLOUR, WIDX_VEHICLE_ADDITIONAL_COLOUR_1, WIDX_VEHICLE_ADDITIONAL_COLOUR_2, 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 }; #define RCT1_LIGHT_OFFSET 4 #define MAIN_RIDE_WIDGETS \ { WWT_FRAME, 0, 0, 315, 0, 206, 0xFFFFFFFF, STR_NONE }, \ { WWT_CAPTION, 0, 1, 314, 1, 14, STR_RIDE_WINDOW_TITLE, STR_WINDOW_TITLE_TIP }, \ { WWT_CLOSEBOX, 0, 303, 313, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, \ { WWT_RESIZE, 1, 0, 315, 43, 179, 0xFFFFFFFF, STR_NONE }, \ { WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_VIEW_OF_RIDE_ATTRACTION_TIP }, \ { WWT_TAB, 1, 34, 64, 17, 46, IMAGE_TYPE_REMAP | SPR_TAB, STR_VEHICLE_DETAILS_AND_OPTIONS_TIP }, \ { WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_OPERATING_OPTIONS_TIP }, \ { WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_MAINTENANCE_OPTIONS_TIP }, \ { WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_COLOUR_SCHEME_OPTIONS_TIP }, \ { WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SOUND_AND_MUSIC_OPTIONS_TIP }, \ { WWT_TAB, 1, 189, 219, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_MEASUREMENTS_AND_TEST_DATA_TIP }, \ { WWT_TAB, 1, 220, 250, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_GRAPHS_TIP }, \ { WWT_TAB, 1, 251, 281, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_INCOME_AND_COSTS_TIP }, \ { WWT_TAB, 1, 282, 312, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_CUSTOMER_INFORMATION_TIP } // 0x009ADC34 static rct_widget window_ride_main_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_VIEWPORT, 1, 3, 290, 60, 166, STR_VIEWPORT, STR_NONE }, { WWT_DROPDOWN, 1, 35, 256, 46, 57, 0xFFFFFFFF, STR_VIEW_SELECTION }, { WWT_BUTTON, 1, 245, 255, 47, 56, STR_DROPDOWN_GLYPH, STR_VIEW_SELECTION }, { WWT_LABEL_CENTRED, 1, 3, 290, 167, 177, 0xFFFFFFFF, STR_NONE }, { WWT_FLATBTN, 1, 291, 314, 46, 69, 0xFFFFFFFF, STR_OPEN_CLOSE_OR_TEST_RIDE }, { WWT_FLATBTN, 1, 291, 314, 70, 93, SPR_CONSTRUCTION, STR_CONSTRUCTION }, { WWT_FLATBTN, 1, 291, 314, 94, 117, SPR_RENAME, STR_NAME_RIDE_TIP }, { WWT_FLATBTN, 1, 291, 314, 118, 141, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP }, { WWT_FLATBTN, 1, 291, 314, 142, 165, SPR_DEMOLISH, STR_DEMOLISH_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 48, 61, SPR_G2_RCT1_CLOSE_BUTTON_0, STR_CLOSE_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_SIMULATE_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_TEST_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 76, 89, SPR_G2_RCT1_OPEN_BUTTON_0, STR_OPEN_RIDE_TIP }, { WWT_DROPDOWN, 1, 3, 307, 180, 191, STR_ARG_6_STRINGID, STR_NONE }, { WWT_BUTTON, 1, 297, 307, 180, 191, STR_DROPDOWN_GLYPH, STR_NONE }, { WIDGETS_END }, }; // 0x009ADDA8 static rct_widget window_ride_vehicle_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_DROPDOWN, 1, 7, 308, 50, 61, 0xFFFFFFFF, STR_NONE }, { WWT_BUTTON, 1, 297, 307, 51, 60, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_SCROLL, 1, 7, 308, 147, 189, 0, STR_NONE }, SPINNER_WIDGETS (1, 7, 151, 196, 207, STR_RIDE_VEHICLE_COUNT, STR_MAX_VEHICLES_TIP), SPINNER_WIDGETS (1, 164, 308, 196, 207, STR_1_CAR_PER_TRAIN, STR_MAX_CARS_PER_TRAIN_TIP), { WIDGETS_END }, }; // 0x009ADEFC static rct_widget window_ride_operating_widgets[] = { MAIN_RIDE_WIDGETS, SPINNER_WIDGETS (1, 157, 308, 61, 72, STR_ARG_18_STRINGID, STR_NONE), // NB: 3 widgets SPINNER_WIDGETS (1, 157, 308, 75, 86, STR_LIFT_HILL_CHAIN_SPEED_VALUE, STR_NONE), // NB: 3 widgets { WWT_CHECKBOX, 1, 7, 86, 109, 120, STR_WAIT_FOR, STR_WAIT_FOR_PASSENGERS_BEFORE_DEPARTING_TIP }, { WWT_CHECKBOX, 1, 7, 308, 124, 135, 0xFFFFFFFF, STR_NONE }, { WWT_CHECKBOX, 1, 7, 156, 139, 150, STR_MINIMUM_WAITING_TIME, STR_MINIMUM_LENGTH_BEFORE_DEPARTING_TIP }, SPINNER_WIDGETS (1, 157, 308, 139, 150, STR_ARG_10_STRINGID, STR_NONE), // NB: 3 widgets { WWT_CHECKBOX, 1, 7, 156, 154, 165, STR_MAXIMUM_WAITING_TIME, STR_MAXIMUM_LENGTH_BEFORE_DEPARTING_TIP }, SPINNER_WIDGETS (1, 157, 308, 154, 165, STR_ARG_14_STRINGID, STR_NONE), // NB: 3 widgets { WWT_CHECKBOX, 1, 7, 308, 169, 180, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP }, { WWT_LABEL, 1, 21, 149, 61, 72, 0xFFFFFFFF, STR_NONE }, { WWT_LABEL, 1, 21, 149, 75, 86, STR_LIFT_HILL_CHAIN_SPEED, STR_NONE }, { WWT_DROPDOWN, 1, 7, 308, 47, 58, 0xFFFFFFFF, STR_SELECT_OPERATING_MODE }, { WWT_BUTTON, 1, 297, 307, 48, 57, STR_DROPDOWN_GLYPH, STR_SELECT_OPERATING_MODE }, { WWT_DROPDOWN, 1, 87, 308, 109, 120, 0xFFFFFFFF, STR_NONE }, { WWT_BUTTON, 1, 297, 307, 110, 119, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_LABEL, 1, 21, 149, 89, 100, STR_NUMBER_OF_CIRCUITS, STR_NUMBER_OF_CIRCUITS_TIP }, SPINNER_WIDGETS (1, 157, 308, 89, 100, STR_NUMBER_OF_CIRCUITS_VALUE, STR_NONE), // NB: 3 widgets { WIDGETS_END }, }; // 0x009AE190 static rct_widget window_ride_maintenance_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_DROPDOWN, 1, 107, 308, 71, 82, 0, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE }, { WWT_BUTTON, 1, 297, 307, 72, 81, STR_DROPDOWN_GLYPH, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE }, { WWT_FLATBTN, 1, 289, 312, 108, 131, 0xFFFFFFFF, STR_LOCATE_NEAREST_AVAILABLE_MECHANIC_TIP }, { WWT_FLATBTN, 1, 265, 288, 108, 131, SPR_CONSTRUCTION, STR_REFURBISH_RIDE_TIP }, { WWT_FLATBTN, 1, 241, 264, 108, 131, SPR_NO_ENTRY, STR_DEBUG_FORCE_BREAKDOWN_TIP }, { WIDGETS_END }, }; // 0x009AE2A4 static rct_widget window_ride_colour_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_SPINNER, 1, 3, 70, 47, 93, 0xFFFFFFFF, STR_NONE }, { WWT_DROPDOWN, 1, 74, 312, 49, 60, STR_ARG_14_STRINGID, STR_NONE }, { WWT_BUTTON, 1, 301, 311, 50, 59, STR_DROPDOWN_GLYPH, STR_COLOUR_SCHEME_TO_CHANGE_TIP }, { WWT_COLOURBTN, 1, 79, 90, 74, 85, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP }, { WWT_COLOURBTN, 1, 99, 110, 74, 85, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP }, { WWT_COLOURBTN, 1, 119, 130, 74, 85, 0xFFFFFFFF, STR_SELECT_SUPPORT_STRUCTURE_COLOUR_TIP }, { WWT_DROPDOWN, 1, 74, 312, 49, 60, 0xFFFFFFFF, STR_NONE }, { WWT_BUTTON, 1, 301, 311, 50, 59, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_FLATBTN, 1, 289, 312, 68, 91, SPR_PAINTBRUSH, STR_PAINT_INDIVIDUAL_AREA_TIP }, { WWT_SPINNER, 1, 245, 312, 101, 147, 0xFFFFFFFF, STR_NONE }, { WWT_DROPDOWN, 1, 103, 241, 103, 114, 0, STR_NONE }, { WWT_BUTTON, 1, 230, 240, 104, 113, STR_DROPDOWN_GLYPH, STR_SELECT_STYLE_OF_ENTRANCE_EXIT_STATION_TIP }, { WWT_SCROLL, 1, 3, 70, 157, 203, 0, STR_NONE }, { WWT_DROPDOWN, 1, 74, 312, 157, 168, STR_ARG_6_STRINGID, STR_NONE }, { WWT_BUTTON, 1, 301, 311, 158, 167, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_COLOUR_SCHEME_TIP }, { WWT_DROPDOWN, 1, 74, 312, 173, 184, 0xFFFFFFFF, STR_NONE }, { WWT_BUTTON, 1, 301, 311, 174, 183, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_TO_MODIFY_TIP }, { WWT_COLOURBTN, 1, 79, 90, 190, 201, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP }, { WWT_COLOURBTN, 1, 99, 110, 190, 201, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP }, { WWT_COLOURBTN, 1, 119, 130, 190, 201, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_2_TIP }, { WIDGETS_END }, }; // 0x009AE4C8 static rct_widget window_ride_music_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_CHECKBOX, 1, 7, 308, 47, 58, STR_PLAY_MUSIC, STR_SELECT_MUSIC_TIP }, { WWT_DROPDOWN, 1, 7, 308, 62, 73, 0, STR_NONE }, { WWT_BUTTON, 1, 297, 307, 63, 72, STR_DROPDOWN_GLYPH, STR_SELECT_MUSIC_STYLE_TIP }, { WIDGETS_END }, }; // 0x009AE5DC static rct_widget window_ride_measurements_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_FLATBTN, 1, 288, 311, 194, 217, SPR_FLOPPY, STR_SAVE_TRACK_DESIGN }, { WWT_BUTTON, 1, 4, 157, 128, 139, STR_SELECT_NEARBY_SCENERY, STR_NONE }, { WWT_BUTTON, 1, 158, 311, 128, 139, STR_RESET_SELECTION, STR_NONE }, { WWT_BUTTON, 1, 4, 157, 178, 189, STR_DESIGN_SAVE, STR_NONE }, { WWT_BUTTON, 1, 158, 311, 178, 189, STR_DESIGN_CANCEL, STR_NONE }, { WIDGETS_END }, }; // 0x009AE710 static rct_widget window_ride_graphs_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_SCROLL, 1, 3, 308, 46, 157, SCROLL_HORIZONTAL, STR_LOGGING_DATA_FROM_TIP }, { WWT_BUTTON, 1, 3, 75, 163, 176, STR_RIDE_STATS_VELOCITY, STR_SHOW_GRAPH_OF_VELOCITY_AGAINST_TIME_TIP }, { WWT_BUTTON, 1, 76, 148, 163, 176, STR_RIDE_STATS_ALTITUDE, STR_SHOW_GRAPH_OF_ALTITUDE_AGAINST_TIME_TIP }, { WWT_BUTTON, 1, 149, 221, 163, 176, STR_RIDE_STATS_VERT_G, STR_SHOW_GRAPH_OF_VERTICAL_ACCELERATION_AGAINST_TIME_TIP }, { WWT_BUTTON, 1, 222, 294, 163, 176, 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, { WWT_LABEL, 1, 19, 144, 50, 61, 0xFFFFFFFF, STR_NONE }, SPINNER_WIDGETS (1, 147, 308, 50, 61, STR_ARG_6_CURRENCY2DP, STR_NONE), // NB: 3 widgets { WWT_CHECKBOX, 1, 5, 310, 62, 74, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP }, { WWT_LABEL, 1, 19, 144, 94, 105, 0xFFFFFFFF, STR_NONE }, SPINNER_WIDGETS (1, 147, 308, 94, 105, STR_RIDE_SECONDARY_PRICE_VALUE, STR_NONE), // NB: 3 widgets { WWT_CHECKBOX, 1, 5, 310, 106, 118, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP }, { WIDGETS_END }, }; // 0x009AE9C8 static rct_widget window_ride_customer_widgets[] = { MAIN_RIDE_WIDGETS, { WWT_FLATBTN, 1, 289, 312, 54, 77, SPR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION_TIP }, { WWT_FLATBTN, 1, 289, 312, 78, 101, SPR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION_TIP }, { WWT_FLATBTN, 1, 289, 312, 102, 125, 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 }; #define MAIN_RIDE_ENABLED_WIDGETS \ (1ULL << WIDX_CLOSE) | \ (1ULL << WIDX_TAB_1) | \ (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) static constexpr const uint64_t window_ride_page_enabled_widgets[] = { MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_VIEW) | (1ULL << WIDX_VIEW_DROPDOWN) | (1ULL << WIDX_OPEN) | (1ULL << WIDX_CONSTRUCTION) | (1ULL << WIDX_RENAME) | (1ULL << WIDX_LOCATE) | (1ULL << WIDX_DEMOLISH) | (1ULL << WIDX_CLOSE_LIGHT) | (1ULL << WIDX_SIMULATE_LIGHT) | (1ULL << WIDX_TEST_LIGHT) | (1ULL << WIDX_OPEN_LIGHT) | (1ULL << WIDX_RIDE_TYPE) | (1ULL << WIDX_RIDE_TYPE_DROPDOWN), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_VEHICLE_TYPE) | (1ULL << WIDX_VEHICLE_TYPE_DROPDOWN) | (1ULL << WIDX_VEHICLE_TRAINS) | (1ULL << WIDX_VEHICLE_TRAINS_INCREASE) | (1ULL << WIDX_VEHICLE_TRAINS_DECREASE) | (1ULL << WIDX_VEHICLE_CARS_PER_TRAIN) | (1ULL << WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE) | (1ULL << WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_MODE_TWEAK_INCREASE) | (1ULL << WIDX_MODE_TWEAK_DECREASE) | (1ULL << WIDX_LIFT_HILL_SPEED_INCREASE) | (1ULL << WIDX_LIFT_HILL_SPEED_DECREASE) | (1ULL << WIDX_LOAD_CHECKBOX) | (1ULL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX) | (1ULL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1ULL << WIDX_MINIMUM_LENGTH_INCREASE) | (1ULL << WIDX_MINIMUM_LENGTH_DECREASE) | (1ULL << WIDX_MAXIMUM_LENGTH_CHECKBOX) | (1ULL << WIDX_MAXIMUM_LENGTH_INCREASE) | (1ULL << WIDX_MAXIMUM_LENGTH_DECREASE) | (1ULL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX) | (1ULL << WIDX_MODE) | (1ULL << WIDX_MODE_DROPDOWN) | (1ULL << WIDX_LOAD) | (1ULL << WIDX_LOAD_DROPDOWN) | (1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE) | (1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_INSPECTION_INTERVAL) | (1ULL << WIDX_INSPECTION_INTERVAL_DROPDOWN) | (1ULL << WIDX_LOCATE_MECHANIC) | (1ULL << WIDX_REFURBISH_RIDE) | (1ULL << WIDX_FORCE_BREAKDOWN), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_TRACK_COLOUR_SCHEME_DROPDOWN) | (1ULL << WIDX_TRACK_MAIN_COLOUR) | (1ULL << WIDX_TRACK_ADDITIONAL_COLOUR) | (1ULL << WIDX_TRACK_SUPPORT_COLOUR) | (1ULL << WIDX_MAZE_STYLE) | (1ULL << WIDX_MAZE_STYLE_DROPDOWN) | (1ULL << WIDX_PAINT_INDIVIDUAL_AREA) | (1ULL << WIDX_ENTRANCE_STYLE) | (1ULL << WIDX_ENTRANCE_STYLE_DROPDOWN) | (1ULL << WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN) | (1ULL << WIDX_VEHICLE_COLOUR_INDEX) | (1ULL << WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN) | (1ULL << WIDX_VEHICLE_MAIN_COLOUR) | (1ULL << WIDX_VEHICLE_ADDITIONAL_COLOUR_1) | (1ULL << WIDX_VEHICLE_ADDITIONAL_COLOUR_2), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_PLAY_MUSIC) | (1ULL << WIDX_MUSIC) | (1ULL << WIDX_MUSIC_DROPDOWN), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_SAVE_TRACK_DESIGN) | (1ULL << WIDX_SELECT_NEARBY_SCENERY) | (1ULL << WIDX_RESET_SELECTION) | (1ULL << WIDX_SAVE_DESIGN) | (1ULL << WIDX_CANCEL_DESIGN), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_GRAPH_VELOCITY) | (1ULL << WIDX_GRAPH_ALTITUDE) | (1ULL << WIDX_GRAPH_VERTICAL) | (1ULL << WIDX_GRAPH_LATERAL), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_PRIMARY_PRICE) | (1ULL << WIDX_PRIMARY_PRICE_INCREASE) | (1ULL << WIDX_PRIMARY_PRICE_DECREASE) | (1ULL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK) | (1ULL << WIDX_SECONDARY_PRICE) | (1ULL << WIDX_SECONDARY_PRICE_INCREASE) | (1ULL << WIDX_SECONDARY_PRICE_DECREASE) | (1ULL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK), MAIN_RIDE_ENABLED_WIDGETS | (1ULL << WIDX_SHOW_GUESTS_THOUGHTS) | (1ULL << WIDX_SHOW_GUESTS_ON_RIDE) | (1ULL << WIDX_SHOW_GUESTS_QUEUING), }; 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), 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 }; #pragma endregion #pragma region Events static void window_ride_init_viewport(rct_window *w); static void window_ride_main_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_main_resize(rct_window *w); static void window_ride_main_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_main_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_main_update(rct_window *w); static void window_ride_main_textinput(rct_window *w, rct_widgetindex widgetIndex, char *text); static void window_ride_main_viewport_rotate(rct_window *w); static void window_ride_main_invalidate(rct_window *w); static void window_ride_main_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_vehicle_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_vehicle_resize(rct_window *w); static void window_ride_vehicle_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_vehicle_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_vehicle_update(rct_window *w); static void window_ride_vehicle_invalidate(rct_window *w); static void window_ride_vehicle_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_vehicle_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); static void window_ride_operating_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_operating_resize(rct_window *w); static void window_ride_operating_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_operating_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_operating_update(rct_window *w); static void window_ride_operating_invalidate(rct_window *w); static void window_ride_operating_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_maintenance_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_maintenance_resize(rct_window *w); static void window_ride_maintenance_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_maintenance_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_maintenance_update(rct_window *w); static void window_ride_maintenance_invalidate(rct_window *w); static void window_ride_maintenance_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_colour_close(rct_window *w); static void window_ride_colour_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_colour_resize(rct_window *w); static void window_ride_colour_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_colour_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_colour_update(rct_window *w); static void window_ride_colour_tooldown(rct_window *w, rct_widgetindex widgetIndex, int32_t x, int32_t y); static void window_ride_colour_tooldrag(rct_window *w, rct_widgetindex widgetIndex, int32_t x, int32_t y); static void window_ride_colour_invalidate(rct_window *w); static void window_ride_colour_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_colour_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); static void window_ride_music_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_music_resize(rct_window *w); static void window_ride_music_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_music_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_music_update(rct_window *w); static void window_ride_music_invalidate(rct_window *w); static void window_ride_music_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_measurements_close(rct_window *w); static void window_ride_measurements_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_measurements_resize(rct_window *w); static void window_ride_measurements_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_measurements_dropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex); static void window_ride_measurements_update(rct_window *w); static void window_ride_measurements_tooldown(rct_window *w, rct_widgetindex widgetIndex, int32_t x, int32_t y); static void window_ride_measurements_tooldrag(rct_window *w, rct_widgetindex widgetIndex, int32_t x, int32_t y); static void window_ride_measurements_toolabort(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_measurements_invalidate(rct_window *w); static void window_ride_measurements_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_graphs_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_graphs_resize(rct_window *w); static void window_ride_graphs_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_graphs_update(rct_window *w); static void window_ride_graphs_scrollgetheight(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height); static void window_ride_graphs_15(rct_window *w, int32_t scrollIndex, int32_t scrollAreaType); static void window_ride_graphs_tooltip(rct_window* w, rct_widgetindex widgetIndex, rct_string_id *stringId); static void window_ride_graphs_invalidate(rct_window *w); static void window_ride_graphs_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_graphs_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); static void window_ride_income_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_income_resize(rct_window *w); static void window_ride_income_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget); static void window_ride_income_update(rct_window *w); static void window_ride_income_textinput(rct_window *w, rct_widgetindex widgetIndex, char *text); static void window_ride_income_invalidate(rct_window *w); static void window_ride_income_paint(rct_window *w, rct_drawpixelinfo *dpi); static bool window_ride_income_can_modify_primary_price(rct_window* w); static void window_ride_customer_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_ride_customer_resize(rct_window *w); static void window_ride_customer_update(rct_window *w); static void window_ride_customer_invalidate(rct_window *w); static void window_ride_customer_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_ride_set_page(rct_window *w, int32_t page); // 0x0098DFD4 static rct_window_event_list window_ride_main_events = { nullptr, window_ride_main_mouseup, window_ride_main_resize, window_ride_main_mousedown, window_ride_main_dropdown, nullptr, window_ride_main_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_main_textinput, window_ride_main_viewport_rotate, nullptr, nullptr, nullptr, nullptr, window_ride_main_invalidate, window_ride_main_paint, nullptr }; // 0x0098E204 static rct_window_event_list window_ride_vehicle_events = { nullptr, window_ride_vehicle_mouseup, window_ride_vehicle_resize, window_ride_vehicle_mousedown, window_ride_vehicle_dropdown, nullptr, window_ride_vehicle_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_vehicle_invalidate, window_ride_vehicle_paint, window_ride_vehicle_scrollpaint }; // 0x0098E0B4 static rct_window_event_list window_ride_operating_events = { nullptr, window_ride_operating_mouseup, window_ride_operating_resize, window_ride_operating_mousedown, window_ride_operating_dropdown, nullptr, window_ride_operating_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_operating_invalidate, window_ride_operating_paint, nullptr }; // 0x0098E124 static rct_window_event_list window_ride_maintenance_events = { nullptr, window_ride_maintenance_mouseup, window_ride_maintenance_resize, window_ride_maintenance_mousedown, window_ride_maintenance_dropdown, nullptr, window_ride_maintenance_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_maintenance_invalidate, window_ride_maintenance_paint, nullptr }; // 0x0098E044 static rct_window_event_list window_ride_colour_events = { window_ride_colour_close, window_ride_colour_mouseup, window_ride_colour_resize, window_ride_colour_mousedown, window_ride_colour_dropdown, nullptr, window_ride_colour_update, nullptr, nullptr, nullptr, window_ride_colour_tooldown, window_ride_colour_tooldrag, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_colour_invalidate, window_ride_colour_paint, window_ride_colour_scrollpaint }; // 0x0098E194 static rct_window_event_list window_ride_music_events = { nullptr, window_ride_music_mouseup, window_ride_music_resize, window_ride_music_mousedown, window_ride_music_dropdown, nullptr, window_ride_music_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_music_invalidate, window_ride_music_paint, nullptr }; // 0x0098DE14 static rct_window_event_list window_ride_measurements_events = { window_ride_measurements_close, window_ride_measurements_mouseup, window_ride_measurements_resize, window_ride_measurements_mousedown, window_ride_measurements_dropdown, nullptr, window_ride_measurements_update, nullptr, nullptr, nullptr, window_ride_measurements_tooldown, window_ride_measurements_tooldrag, nullptr, window_ride_measurements_toolabort, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_measurements_invalidate, window_ride_measurements_paint, nullptr }; // 0x0098DF64 static rct_window_event_list window_ride_graphs_events = { nullptr, window_ride_graphs_mouseup, window_ride_graphs_resize, window_ride_graphs_mousedown, nullptr, nullptr, window_ride_graphs_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_graphs_scrollgetheight, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_graphs_15, window_ride_graphs_tooltip, nullptr, nullptr, window_ride_graphs_invalidate, window_ride_graphs_paint, window_ride_graphs_scrollpaint }; // 0x0098DEF4 static rct_window_event_list window_ride_income_events = { nullptr, window_ride_income_mouseup, window_ride_income_resize, window_ride_income_mousedown, nullptr, nullptr, window_ride_income_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_income_textinput, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_income_invalidate, window_ride_income_paint, nullptr }; // 0x0098DE84 static rct_window_event_list window_ride_customer_events = { nullptr, window_ride_customer_mouseup, window_ride_customer_resize, nullptr, nullptr, nullptr, window_ride_customer_update, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, window_ride_customer_invalidate, window_ride_customer_paint, nullptr }; 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 }; #pragma endregion static bool _collectTrackDesignScenery = false; static int32_t _lastSceneryX = 0; static int32_t _lastSceneryY = 0; static std::unique_ptr _trackDesign; // Cached overall view for each ride // (Re)calculated when the ride window is opened struct ride_overall_view { int16_t x, y, z; uint8_t zoom; }; static std::vector ride_overall_views = {}; static constexpr const int32_t window_ride_tab_animation_divisor[] = { 0, 0, 2, 2, 4, 2, 8, 8, 2, 0 }; static constexpr const int32_t window_ride_tab_animation_frames[] = { 0, 0, 4, 16, 8, 16, 8, 8, 8, 0 }; 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, }; static constexpr const rct_string_id RideBreakdownReasonNames[] = { STR_RIDE_BREAKDOWN_SAFETY_CUT_OUT , STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_CLOSED, STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_OPEN, STR_RIDE_BREAKDOWN_DOORS_STUCK_CLOSED, STR_RIDE_BREAKDOWN_DOORS_STUCK_OPEN, STR_RIDE_BREAKDOWN_VEHICLE_MALFUNCTION, STR_RIDE_BREAKDOWN_BRAKES_FAILURE, STR_RIDE_BREAKDOWN_CONTROL_FAILURE }; const rct_string_id ColourSchemeNames[4] = { STR_MAIN_COLOUR_SCHEME, STR_ALTERNATIVE_COLOUR_SCHEME_1, STR_ALTERNATIVE_COLOUR_SCHEME_2, STR_ALTERNATIVE_COLOUR_SCHEME_3, }; static constexpr const rct_string_id VehicleLoadNames[] = { STR_QUARTER_LOAD, STR_HALF_LOAD, STR_THREE_QUARTER_LOAD, STR_FULL_LOAD, STR_ANY_LOAD, }; static constexpr const rct_string_id VehicleColourSchemeNames[] = { STR_ALL_VEHICLES_IN_SAME_COLOURS , STR_DIFFERENT_COLOURS_PER , STR_DIFFERENT_COLOURS_PER_VEHICLE , }; static constexpr const rct_string_id VehicleStatusNames[] = { STR_MOVING_TO_END_OF, // VEHICLE_STATUS_MOVING_TO_END_OF_STATION STR_WAITING_FOR_PASSENGERS_AT, // VEHICLE_STATUS_WAITING_FOR_PASSENGERS STR_WAITING_TO_DEPART, // VEHICLE_STATUS_WAITING_TO_DEPART STR_DEPARTING, // VEHICLE_STATUS_DEPARTING STR_TRAVELLING_AT_0, // VEHICLE_STATUS_TRAVELLING STR_ARRIVING_AT, // VEHICLE_STATUS_ARRIVING STR_UNLOADING_PASSENGERS_AT, // VEHICLE_STATUS_UNLOADING_PASSENGERS STR_TRAVELLING_AT_1, // VEHICLE_STATUS_TRAVELLING_BOAT STR_CRASHING, // VEHICLE_STATUS_CRASHING STR_CRASHED_0, // VEHICLE_STATUS_CRASHED STR_TRAVELLING_AT_2, // VEHICLE_STATUS_TRAVELLING_DODGEMS STR_SWINGING, // VEHICLE_STATUS_SWINGING STR_ROTATING_0, // VEHICLE_STATUS_ROTATING STR_ROTATING_1, // VEHICLE_STATUS_FERRIS_WHEEL_ROTATING STR_OPERATING_0, // VEHICLE_STATUS_SIMULATOR_OPERATING STR_SHOWING_FILM, // VEHICLE_STATUS_SHOWING_FILM STR_ROTATING_2, // VEHICLE_STATUS_SPACE_RINGS_OPERATING STR_OPERATING_1, // VEHICLE_STATUS_TOP_SPIN_OPERATING STR_OPERATING_2, // VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING STR_DOING_CIRCUS_SHOW, // VEHICLE_STATUS_DOING_CIRCUS_SHOW STR_OPERATING_3, // VEHICLE_STATUS_CROOKED_HOUSE_OPERATING STR_WAITING_FOR_CABLE_LIFT, // VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT STR_TRAVELLING_AT_3, // VEHICLE_STATUS_TRAVELLING_CABLE_LIFT STR_STOPPING_0, // VEHICLE_STATUS_STOPPING STR_WAITING_FOR_PASSENGERS, // VEHICLE_STATUS_WAITING_FOR_PASSENGERS_17 STR_WAITING_TO_START, // VEHICLE_STATUS_WAITING_TO_START STR_STARTING, // VEHICLE_STATUS_STARTING STR_OPERATING, // VEHICLE_STATUS_OPERATING_1A STR_STOPPING_1, // VEHICLE_STATUS_STOPPING_1B STR_UNLOADING_PASSENGERS, // VEHICLE_STATUS_UNLOADING_PASSENGERS_1C STR_STOPPED_BY_BLOCK_BRAKES, // VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES }; static constexpr const rct_string_id SingleSessionVehicleStatusNames[] = { STR_STOPPING_0, // VEHICLE_STATUS_MOVING_TO_END_OF_STATION STR_WAITING_FOR_PASSENGERS, // VEHICLE_STATUS_WAITING_FOR_PASSENGERS STR_WAITING_TO_START, // VEHICLE_STATUS_WAITING_TO_DEPART STR_STARTING, // VEHICLE_STATUS_DEPARTING STR_OPERATING, // VEHICLE_STATUS_TRAVELLING STR_STOPPING_1, // VEHICLE_STATUS_ARRIVING STR_UNLOADING_PASSENGERS, // VEHICLE_STATUS_UNLOADING_PASSENGERS }; static constexpr const rct_string_id MusicStyleNames[] = { STR_MUSIC_STYLE_DODGEMS_BEAT, STR_MUSIC_STYLE_FAIRGROUND_ORGAN, STR_MUSIC_STYLE_ROMAN_FANFARE, STR_MUSIC_STYLE_ORIENTAL, STR_MUSIC_STYLE_MARTIAN, STR_MUSIC_STYLE_JUNGLE_DRUMS, STR_MUSIC_STYLE_EGYPTIAN, STR_MUSIC_STYLE_TOYLAND, STR_MUSIC_STYLE_CIRCUS_SHOW, STR_MUSIC_STYLE_SPACE, STR_MUSIC_STYLE_HORROR, STR_MUSIC_STYLE_TECHNO, STR_MUSIC_STYLE_GENTLE, STR_MUSIC_STYLE_SUMMER, STR_MUSIC_STYLE_WATER, STR_MUSIC_STYLE_WILD_WEST, STR_MUSIC_STYLE_JURASSIC, STR_MUSIC_STYLE_ROCK, STR_MUSIC_STYLE_RAGTIME, STR_MUSIC_STYLE_FANTASY, STR_MUSIC_STYLE_ROCK_STYLE_2, STR_MUSIC_STYLE_ICE, STR_MUSIC_STYLE_SNOW, STR_MUSIC_STYLE_CUSTOM_MUSIC_1, STR_MUSIC_STYLE_CUSTOM_MUSIC_2, STR_MUSIC_STYLE_MEDIEVAL, STR_MUSIC_STYLE_URBAN, STR_MUSIC_STYLE_ORGAN, STR_MUSIC_STYLE_MECHANICAL, STR_MUSIC_STYLE_MODERN, STR_MUSIC_STYLE_PIRATES, STR_MUSIC_STYLE_ROCK_STYLE_3, STR_MUSIC_STYLE_CANDY_STYLE, }; struct window_ride_maze_design_option { rct_string_id text; uint32_t sprite; }; static constexpr const window_ride_maze_design_option 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 window_ride_colour_preview { uint32_t track; uint32_t supports; }; static constexpr const window_ride_colour_preview TrackColourPreviews[] = { { SPR_RIDE_DESIGN_PREVIEW_SPIRAL_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_SPIRAL_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_STAND_UP_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_STAND_UP_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SUSPENDED_SWINGING_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_SUSPENDED_SWINGING_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_INVERTED_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_INVERTED_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_JUNIOR_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_JUNIOR_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINIATURE_RAILWAY_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINIATURE_RAILWAY_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MONORAIL_TRACK, SPR_RIDE_DESIGN_PREVIEW_MONORAIL_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINI_SUSPENDED_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINI_SUSPENDED_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_BOAT_HIRE_TRACK, SPR_RIDE_DESIGN_PREVIEW_BOAT_HIRE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_WOODEN_WILD_MOUSE_TRACK, SPR_RIDE_DESIGN_PREVIEW_WOODEN_WILD_MOUSE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_STEEPLECHASE_TRACK, SPR_RIDE_DESIGN_PREVIEW_STEEPLECHASE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_CAR_RIDE_TRACK, SPR_RIDE_DESIGN_PREVIEW_CAR_RIDE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_LAUNCHED_FREEFALL_TRACK, SPR_RIDE_DESIGN_PREVIEW_LAUNCHED_FREEFALL_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_BOBSLEIGH_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_BOBSLEIGH_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_OBSERVATION_TOWER_TRACK, SPR_RIDE_DESIGN_PREVIEW_OBSERVATION_TOWER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_LOOPING_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_LOOPING_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_DINGHY_SLIDE_TRACK, SPR_RIDE_DESIGN_PREVIEW_DINGHY_SLIDE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINE_TRAIN_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINE_TRAIN_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_CHAIRLIFT_TRACK, SPR_RIDE_DESIGN_PREVIEW_CHAIRLIFT_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_CORKSCREW_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_CORKSCREW_ROLLER_COASTER_SUPPORTS}, { 0, 0 }, // MAZE { SPR_RIDE_DESIGN_PREVIEW_SPIRAL_SLIDE_TRACK, 0}, { SPR_RIDE_DESIGN_PREVIEW_GO_KARTS_TRACK, SPR_RIDE_DESIGN_PREVIEW_GO_KARTS_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_LOG_FLUME_TRACK, SPR_RIDE_DESIGN_PREVIEW_LOG_FLUME_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_RIVER_RAPIDS_TRACK, SPR_RIDE_DESIGN_PREVIEW_RIVER_RAPIDS_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_DODGEMS_TRACK, SPR_RIDE_DESIGN_PREVIEW_DODGEMS_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_PIRATE_SHIP_TRACK, SPR_RIDE_DESIGN_PREVIEW_PIRATE_SHIP_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SWINGING_INVERTER_SHIP_TRACK, SPR_RIDE_DESIGN_PREVIEW_SWINGING_INVERTER_SHIP_SUPPORTS}, { 0, 0 }, // FOOD_STALL { 0, 0 }, // 1D { 0, 0 }, // DRINK_STALL { 0, 0 }, // 1F { 0, 0 }, // SHOP { 0, 0 }, // MERRY_GO_ROUND { 0, 0 }, // 22 { 0, 0 }, // INFORMATION_KIOSK { 0, 0 }, // TOILETS { SPR_RIDE_DESIGN_PREVIEW_FERRIS_WHEEL_TRACK, 0}, { 0, 0 }, // MOTION_SIMULATOR { 0, 0 }, // 3D_CINEMA { SPR_RIDE_DESIGN_PREVIEW_TOP_SPIN_TRACK, 0}, { 0, 0 }, // SPACE_RINGS { SPR_RIDE_DESIGN_PREVIEW_REVERSE_FREEFALL_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_REVERSE_FREEFALL_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_LIFT_TRACK, 0}, { SPR_RIDE_DESIGN_PREVIEW_VERTICAL_DROP_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_VERTICAL_DROP_ROLLER_COASTER_SUPPORTS}, { 0, 0 }, // CASH_MACHINE { 0, 0 }, // TWIST_TRACK { 0, 0 }, // HAUNTED_HOUSE { 0, 0 }, // FIRST_AID { 0, 0 }, // CIRCUS_SHOW { SPR_RIDE_DESIGN_PREVIEW_GHOST_TRAIN_TRACK, SPR_RIDE_DESIGN_PREVIEW_GHOST_TRAIN_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_TWISTER_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_TWISTER_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_WOODEN_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_WOODEN_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SIDE_FRICTION_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_SIDE_FRICTION_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_WILD_MOUSE_TRACK, SPR_RIDE_DESIGN_PREVIEW_WILD_MOUSE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MULTI_DIMENSION_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_MULTI_DIMENSION_ROLLER_COASTER_SUPPORTS}, { 0, 0 }, // 38 { SPR_RIDE_DESIGN_PREVIEW_FLYING_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_FLYING_ROLLER_COASTER_SUPPORTS}, { 0, 0 }, // 3A { SPR_RIDE_DESIGN_PREVIEW_VIRGINIA_REEL_TRACK, SPR_RIDE_DESIGN_PREVIEW_VIRGINIA_REEL_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SPLASH_BOATS_TRACK, SPR_RIDE_DESIGN_PREVIEW_SPLASH_BOATS_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINI_HELICOPTERS_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINI_HELICOPTERS_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_LAY_DOWN_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_LAY_DOWN_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SUSPENDED_MONORAIL_TRACK, SPR_RIDE_DESIGN_PREVIEW_SUSPENDED_MONORAIL_SUPPORTS}, { 0, 0 }, // 40 { SPR_RIDE_DESIGN_PREVIEW_REVERSER_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_REVERSER_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_HEARTLINE_TWISTER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_HEARTLINE_TWISTER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINI_GOLF_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINI_GOLF_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_GIGA_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_GIGA_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_ROTO_DROP_TRACK, SPR_RIDE_DESIGN_PREVIEW_ROTO_DROP_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_FLYING_SAUCERS_TRACK, 0}, { 0, 0 }, // CROOKED_HOUSE { SPR_RIDE_DESIGN_PREVIEW_MONORAIL_CYCLES_TRACK, SPR_RIDE_DESIGN_PREVIEW_MONORAIL_CYCLES_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_COMPACT_INVERTED_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_COMPACT_INVERTED_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_WATER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_WATER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_AIR_POWERED_VERTICAL_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_AIR_POWERED_VERTICAL_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_INVERTED_HAIRPIN_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_INVERTED_HAIRPIN_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MAGIC_CARPET_TRACK, SPR_RIDE_DESIGN_PREVIEW_MAGIC_CARPET_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_SUBMARINE_RIDE_TRACK, SPR_RIDE_DESIGN_PREVIEW_SUBMARINE_RIDE_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_RIVER_RAFTS_TRACK, SPR_RIDE_DESIGN_PREVIEW_RIVER_RAFTS_SUPPORTS}, { 0, 0 }, // 50 { 0, 0 }, // ENTERPRISE { 0, 0 }, // 52 { 0, 0 }, // 53 { 0, 0 }, // 54 { 0, 0 }, // 55 { SPR_RIDE_DESIGN_PREVIEW_INVERTED_IMPULSE_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_INVERTED_IMPULSE_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINI_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINI_ROLLER_COASTER_SUPPORTS}, { SPR_RIDE_DESIGN_PREVIEW_MINE_RIDE_TRACK, SPR_RIDE_DESIGN_PREVIEW_MINE_RIDE_SUPPORTS}, { 0, 0 }, // 59 { SPR_RIDE_DESIGN_PREVIEW_LIM_LAUNCHED_ROLLER_COASTER_TRACK, SPR_RIDE_DESIGN_PREVIEW_LIM_LAUNCHED_ROLLER_COASTER_SUPPORTS}, }; // clang-format on struct rct_window_graphs_y_axis { uint8_t interval; int8_t unit; int8_t unit_interval; rct_string_id label; }; /** rct2: 0x0098DD98 */ static constexpr const rct_window_graphs_y_axis 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_POS_VERTICAL = FIXED_2DP(5, 00); static constexpr auto RIDE_G_FORCES_RED_NEG_VERTICAL = -FIXED_2DP(2, 00); 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 RideDropdownData; // Used for sorting the vehicle type dropdown. struct VehicleTypeLabel { int32_t 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 VehicleDropdownData; static void window_ride_draw_tab_image(rct_drawpixelinfo* dpi, rct_window* w, int32_t page, int32_t spriteIndex) { rct_widgetindex widgetIndex = WIDX_TAB_1 + page; if (!(w->disabled_widgets & (1LL << 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]); } gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0); } } /** * * rct2: 0x006B2E88 */ static void window_ride_draw_tab_main(rct_drawpixelinfo* dpi, rct_window* w) { rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_MAIN; if (!(w->disabled_widgets & (1LL << widgetIndex))) { auto ride = get_ride(w->number); 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; } gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0); } } } /** * * rct2: 0x006B2B68 */ static void window_ride_draw_tab_vehicle(rct_drawpixelinfo* dpi, rct_window* w) { rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_VEHICLE; rct_widget* widget = &w->widgets[widgetIndex]; if (!(w->disabled_widgets & (1LL << widgetIndex))) { int32_t x = widget->left + 1; int32_t y = widget->top + 1; int32_t width = widget->right - x; int32_t height = widget->bottom - 3 - y; if (w->page == WINDOW_RIDE_PAGE_VEHICLE) height += 4; x += w->x; y += w->y; rct_drawpixelinfo clipDPI; if (!clip_drawpixelinfo(&clipDPI, dpi, x, y, width, height)) { return; } x = (widget->right - widget->left) / 2; y = (widget->bottom - widget->top) - 12; auto ride = get_ride(w->number); 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 = 1; clipDPI.width *= 2; clipDPI.height *= 2; x *= 2; 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->type == RIDE_TYPE_COMPACT_INVERTED_COASTER || ride->type == RIDE_TYPE_INVERTED_ROLLER_COASTER || ride->type == RIDE_TYPE_INVERTED_IMPULSE_COASTER || ride->type == RIDE_TYPE_INVERTED_HAIRPIN_COASTER || ride->type == RIDE_TYPE_SUSPENDED_SWINGING_COASTER || ride->type == RIDE_TYPE_CHAIRLIFT || ride->type == RIDE_TYPE_MINI_SUSPENDED_COASTER || ride->type == RIDE_TYPE_SUSPENDED_MONORAIL) { y /= 4; } const uint8_t vehicle = ride_entry_get_vehicle_at_position( ride->subtype, ride->num_cars_per_train, rideEntry->tab_vehicle); rct_ride_entry_vehicle* rideVehicleEntry = &rideEntry->vehicles[vehicle]; vehicle_colour vehicleColour = ride_get_vehicle_colour(ride, 0); int32_t spriteIndex = 32; if (w->page == WINDOW_RIDE_PAGE_VEHICLE) spriteIndex += w->frame_no; spriteIndex /= (rideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_11) ? 4 : 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->base_num_frames; spriteIndex += rideVehicleEntry->base_image_id; spriteIndex |= (vehicleColour.additional_1 << 24) | (vehicleColour.main << 19); spriteIndex |= IMAGE_TYPE_REMAP_2_PLUS; gfx_draw_sprite(&clipDPI, spriteIndex, x, y, vehicleColour.additional_2); } } /** * * rct2: 0x006B2F42 */ static void window_ride_draw_tab_customer(rct_drawpixelinfo* dpi, rct_window* w) { rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_CUSTOMER; if (!(w->disabled_widgets & (1LL << widgetIndex))) { rct_widget* widget = &w->widgets[widgetIndex]; int32_t spriteIndex = 0; if (w->page == WINDOW_RIDE_PAGE_CUSTOMER) spriteIndex = w->var_492 & ~3; spriteIndex += g_peep_animation_entries[PEEP_SPRITE_TYPE_NORMAL].sprite_animation->base_image; spriteIndex += 1; spriteIndex |= 0xA9E00000; gfx_draw_sprite(dpi, spriteIndex, w->x + (widget->left + widget->right) / 2, w->y + widget->bottom - 6, 0); } } /** * * rct2: 0x006B2B35 */ static void window_ride_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* w) { window_ride_draw_tab_vehicle(dpi, w); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_OPERATING, SPR_TAB_GEARS_0); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_MAINTENANCE, SPR_TAB_WRENCH_0); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_INCOME, SPR_TAB_ADMISSION_0); window_ride_draw_tab_main(dpi, w); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_MEASUREMENTS, SPR_TAB_TIMER_0); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_COLOUR, SPR_TAB_PAINT_0); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_GRAPHS, SPR_TAB_GRAPH_A_0); window_ride_draw_tab_customer(dpi, w); window_ride_draw_tab_image(dpi, w, WINDOW_RIDE_PAGE_MUSIC, SPR_TAB_MUSIC_0); } /** * * rct2: 0x006AEB9F */ static void window_ride_disable_tabs(rct_window* w) { uint32_t disabled_tabs = 0; auto ride = get_ride(w->number & 0xFF); if (ride == nullptr) return; uint8_t ride_type = ride->type; // ecx if (!ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) disabled_tabs |= (1 << WIDX_TAB_8); // 0x800 if (ride_type == RIDE_TYPE_MINI_GOLF) disabled_tabs |= (1 << WIDX_TAB_2 | 1 << WIDX_TAB_3 | 1 << WIDX_TAB_4); // 0xE0 if (ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_NO_VEHICLES)) disabled_tabs |= (1 << WIDX_TAB_2); // 0x20 if (!ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN) && !ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL) && !ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS) && !ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS) && !(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ENTRANCE_EXIT)) { disabled_tabs |= (1 << WIDX_TAB_5); // 0x100 } if (ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_IS_SHOP)) disabled_tabs |= (1 << WIDX_TAB_3 | 1 << WIDX_TAB_4 | 1 << WIDX_TAB_7); // 0x4C0 if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_MUSIC)) { disabled_tabs |= (1 << 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 |= (1 << WIDX_TAB_9); // 0x1000 if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0) disabled_tabs |= (1 << WIDX_TAB_4 | 1 << WIDX_TAB_6 | 1 << WIDX_TAB_9 | 1 << WIDX_TAB_10); // 0x3280 rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); if (rideEntry == nullptr) { disabled_tabs |= 1 << WIDX_TAB_2 | 1 << WIDX_TAB_3 | 1 << WIDX_TAB_4 | 1 << WIDX_TAB_5 | 1 << WIDX_TAB_6 | 1 << WIDX_TAB_7 | 1 << WIDX_TAB_8 | 1 << WIDX_TAB_9 | 1 << WIDX_TAB_10; } else if ((rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB) != 0) { disabled_tabs |= (1 << WIDX_TAB_5); } w->disabled_widgets = disabled_tabs; } static void window_ride_update_overall_view(Ride* ride) { // Calculate x, y, z bounds of the entire ride using its track elements tile_element_iterator it; tile_element_iterator_begin(&it); int32_t minx = std::numeric_limits::max(), miny = std::numeric_limits::max(), minz = std::numeric_limits::max(); int32_t maxx = std::numeric_limits::min(), maxy = std::numeric_limits::min(), maxz = std::numeric_limits::min(); while (tile_element_iterator_next(&it)) { if (it.element->GetType() != TILE_ELEMENT_TYPE_TRACK) continue; if (it.element->AsTrack()->GetRideIndex() != ride->id) continue; int32_t x = it.x * 32; int32_t y = it.y * 32; int32_t z1 = it.element->base_height * 8; int32_t z2 = it.element->clearance_height * 8; minx = std::min(minx, x); miny = std::min(miny, y); minz = std::min(minz, z1); maxx = std::max(maxx, x); maxy = std::max(maxy, y); maxz = std::max(maxz, z2); } if (ride->id >= ride_overall_views.size()) { ride_overall_views.resize(ride->id + 1); } auto& view = ride_overall_views[ride->id]; view.x = (minx + maxx) / 2 + 16; view.y = (miny + maxy) / 2 + 16; view.z = (minz + maxz) / 2 - 8; // Calculate size to determine from how far away to view the ride int32_t dx = maxx - minx; int32_t dy = maxy - miny; int32_t dz = maxz - minz; int32_t size = (int32_t)std::sqrt(dx * dx + dy * dy + dz * dz); 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 view.zoom = std::clamp(std::ceil(std::log(size / 80)) - 1, 0, 3); } else { // Small rides or stalls are zoomed in all the way. view.zoom = 0; } } /** * * rct2: 0x006AEAB4 */ static rct_window* window_ride_open(Ride* ride) { rct_window* w; w = window_create_auto_pos(316, 207, window_ride_page_events[0], WC_RIDE, WF_10 | WF_RESIZABLE); w->widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN]; w->enabled_widgets = window_ride_page_enabled_widgets[WINDOW_RIDE_PAGE_MAIN]; w->hold_down_widgets = window_ride_page_hold_down_widgets[WINDOW_RIDE_PAGE_MAIN]; w->number = ride->id; w->page = WINDOW_RIDE_PAGE_MAIN; w->vehicleIndex = 0; w->frame_no = 0; w->list_information_type = 0; w->var_492 = 0; w->ride_colour = 0; window_ride_disable_tabs(w); w->min_width = 316; w->min_height = 180; w->max_width = 500; w->max_height = 450; window_ride_update_overall_view(ride); return w; } /** * * rct2: 0x006ACC28 */ rct_window* window_ride_main_open(Ride* ride) { rct_window* w; w = window_bring_to_front_by_number(WC_RIDE, ride->id); if (w == nullptr) { w = window_ride_open(ride); w->ride.var_482 = -1; } 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) { window_ride_set_page(w, WINDOW_RIDE_PAGE_MAIN); } w->ride.view = 0; window_ride_init_viewport(w); return w; } /** * * rct2: 0x006ACCCE */ static rct_window* window_ride_open_station(Ride* ride, int32_t stationIndex) { if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES)) return window_ride_main_open(ride); auto w = window_bring_to_front_by_number(WC_RIDE, ride->id); if (w == nullptr) { w = window_ride_open(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->enabled_widgets = window_ride_page_enabled_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; window_ride_disable_tabs(w); window_init_scroll_widgets(w); // View for (int32_t i = stationIndex; i >= 0; i--) { if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) { stationIndex--; } } w->ride.view = 1 + ride->num_vehicles + stationIndex; window_ride_init_viewport(w); return w; } rct_window* window_ride_open_track(TileElement* tileElement) { assert(tileElement != nullptr); auto rideIndex = tile_element_get_ride_index(tileElement); if (rideIndex != RIDE_ID_NULL) { auto ride = get_ride(rideIndex); if (ride != nullptr) { switch (tileElement->GetType()) { case TILE_ELEMENT_TYPE_ENTRANCE: { // Open ride window in station view auto entranceElement = tileElement->AsEntrance(); auto stationIndex = entranceElement->GetStationIndex(); return window_ride_open_station(ride, stationIndex); } case TILE_ELEMENT_TYPE_TRACK: { // Open ride window in station view auto trackElement = tileElement->AsTrack(); auto trackType = trackElement->GetTrackType(); if (TrackSequenceProperties[trackType][0] & TRACK_SEQUENCE_FLAG_ORIGIN) { auto stationIndex = trackElement->GetStationIndex(); return window_ride_open_station(ride, stationIndex); } } } // Open ride window in overview mode return window_ride_main_open(ride); } } return nullptr; } /** * * rct2: 0x006ACAC2 */ rct_window* window_ride_open_vehicle(rct_vehicle* vehicle) { rct_vehicle* headVehicle = vehicle_get_head(vehicle); uint16_t headVehicleSpriteIndex = headVehicle->sprite_index; auto ride = get_ride(headVehicle->ride); if (ride == nullptr) return nullptr; // Get view index int32_t view = 1; for (int32_t i = 0; i < MAX_VEHICLES_PER_RIDE; i++) { if (ride->vehicles[i] == headVehicleSpriteIndex) break; view++; } rct_window* w = window_find_by_number(WC_RIDE, ride->id); 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++) { uint16_t peepSpriteIndex = vehicle->peep[i]; if (peepSpriteIndex == SPRITE_INDEX_NULL) continue; numPeepsLeft--; rct_window* w2 = window_find_by_number(WC_PEEP, peepSpriteIndex); if (w2 == nullptr) { Peep* peep = &(get_sprite(peepSpriteIndex)->peep); 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) : window_bring_to_front_by_number(WC_RIDE, ride->id); } if (w == nullptr) { w = window_ride_open(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->enabled_widgets = window_ride_page_enabled_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; window_ride_disable_tabs(w); window_init_scroll_widgets(w); w->ride.view = view; window_ride_init_viewport(w); w->Invalidate(); return w; } /** * * rct2: 0x006AF1D2 */ static void window_ride_set_page(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(); // 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->var_492 = 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); //} if (w->viewport != nullptr) { w->viewport->width = 0; w->viewport = nullptr; } w->enabled_widgets = window_ride_page_enabled_widgets[page]; 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]; window_ride_disable_tabs(w); w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); w->Invalidate(); if (listen != 0 && w->viewport != nullptr) w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON; } static void window_ride_set_pressed_tab(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 window_ride_anchor_border_widgets(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 /** * * rct2: 0x006AF994 */ static void window_ride_init_viewport(rct_window* w) { if (w->page != WINDOW_RIDE_PAGE_MAIN) return; auto ride = get_ride(w->number); if (ride == nullptr) return; int32_t eax = w->viewport_focus_coordinates.var_480 - 1; union { sprite_focus sprite; coordinate_focus coordinate; } focus; focus.coordinate.x = 0; focus.coordinate.y = 0; focus.coordinate.z = 0; focus.sprite.sprite_id = SPRITE_INDEX_NULL; focus.coordinate.zoom = 0; focus.coordinate.rotation = get_current_rotation(); focus.coordinate.width = 0; focus.coordinate.height = 0; if (eax >= 0 && eax < ride->num_vehicles && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) { focus.sprite.sprite_id = ride->vehicles[eax]; rct_ride_entry* ride_entry = ride->GetRideEntry(); if (ride_entry && ride_entry->tab_vehicle != 0) { rct_vehicle* vehicle = GET_VEHICLE(focus.sprite.sprite_id); if (vehicle == nullptr) { focus.sprite.sprite_id = SPRITE_INDEX_NULL; } else if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { focus.sprite.sprite_id = vehicle->next_vehicle_on_train; } } if (focus.sprite.sprite_id != SPRITE_INDEX_NULL) { focus.sprite.type |= VIEWPORT_FOCUS_TYPE_SPRITE; } } else if (eax >= ride->num_vehicles && eax < (ride->num_vehicles + ride->num_stations)) { int32_t stationIndex = -1; int32_t count = eax - ride->num_vehicles; do { stationIndex++; if (ride->stations[stationIndex].Start.xy != RCT_XY8_UNDEFINED) { count--; } } while (count >= 0); LocationXY8 location = ride->stations[stationIndex].Start; focus.coordinate.x = location.x * 32; focus.coordinate.y = location.y * 32; focus.coordinate.z = ride->stations[stationIndex].Height << 3; focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE; } else { if (eax > 0) { w->viewport_focus_coordinates.var_480 = 0; } if (w->number < ride_overall_views.size()) { const auto& view = ride_overall_views[w->number]; focus.coordinate.x = view.x; focus.coordinate.y = view.y; focus.coordinate.z = view.z; focus.coordinate.zoom = view.zoom; focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE; } } focus.coordinate.var_480 = w->viewport_focus_coordinates.var_480; uint16_t viewport_flags = 0; if (w->viewport != nullptr) { if (focus.coordinate.x == w->viewport_focus_coordinates.x && (focus.coordinate.y & VIEWPORT_FOCUS_Y_MASK) == w->viewport_focus_coordinates.y && focus.coordinate.z == w->viewport_focus_coordinates.z && focus.coordinate.rotation == w->viewport_focus_coordinates.rotation && focus.coordinate.zoom == w->viewport_focus_coordinates.zoom && focus.coordinate.width == w->width && focus.coordinate.height == w->height) { return; } viewport_flags = w->viewport->flags; w->viewport->width = 0; w->viewport = nullptr; } else if (gConfigGeneral.always_show_gridlines) { viewport_flags |= VIEWPORT_FLAG_GRIDLINES; } window_event_invalidate_call(w); w->viewport_focus_coordinates.x = focus.coordinate.x; w->viewport_focus_coordinates.y = focus.coordinate.y; w->viewport_focus_coordinates.z = focus.coordinate.z; w->viewport_focus_coordinates.rotation = focus.coordinate.rotation; w->viewport_focus_coordinates.zoom = focus.coordinate.zoom; w->viewport_focus_coordinates.width = w->width; w->viewport_focus_coordinates.height = w->height; // rct2: 0x006aec9c only used here so brought it into the function if (!w->viewport && ride->overall_view.xy != RCT_XY8_UNDEFINED) { rct_widget* view_widget = &w->widgets[WIDX_VIEWPORT]; int32_t x = view_widget->left + 1 + w->x; int32_t y = view_widget->top + 1 + w->y; int32_t width = view_widget->right - view_widget->left - 1; int32_t height = view_widget->bottom - view_widget->top - 1; viewport_create( w, x, y, width, height, focus.coordinate.zoom, focus.coordinate.x, focus.coordinate.y & VIEWPORT_FOCUS_Y_MASK, focus.coordinate.z, focus.sprite.type & VIEWPORT_FOCUS_TYPE_MASK, focus.sprite.sprite_id); w->flags |= WF_NO_SCROLLING; w->Invalidate(); } if (w->viewport) { w->viewport->flags = viewport_flags; w->Invalidate(); } } /** * * rct2: 0x006AF315 */ static void window_ride_rename(rct_window* w) { auto ride = get_ride(w->number); if (ride != nullptr) { auto rideName = ride->GetName(); window_text_input_raw_open( w, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, rideName.c_str(), 32); } } /** * * rct2: 0x006AF17E */ static void window_ride_main_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_CONSTRUCTION: { auto ride = get_ride(w->number); if (ride != nullptr) { ride_construct(ride); if (window_find_by_number(WC_RIDE_CONSTRUCTION, ride->id) != nullptr) { window_close(w); } } break; } case WIDX_RENAME: window_ride_rename(w); break; case WIDX_LOCATE: w->ScrollToViewport(); 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->number); if (ride != nullptr) { int32_t status; switch (widgetIndex) { default: case WIDX_CLOSE_LIGHT: status = RIDE_STATUS_CLOSED; break; case WIDX_SIMULATE_LIGHT: status = RIDE_STATUS_SIMULATING; break; case WIDX_TEST_LIGHT: status = RIDE_STATUS_TESTING; break; case WIDX_OPEN_LIGHT: status = RIDE_STATUS_OPEN; break; } ride_set_status(ride, status); } break; } } } /** * * rct2: 0x006AF4A2 */ static void window_ride_main_resize(rct_window* w) { int32_t minHeight = 180; if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE) { minHeight += 20 + RCT1_LIGHT_OFFSET; auto ride = get_ride(w->number); if (ride != nullptr) { #ifdef __SIMULATE_IN_RIDE_WINDOW__ if (ride->SupportsStatus(RIDE_STATUS_SIMULATING)) { minHeight += 14; } #endif if (ride->SupportsStatus(RIDE_STATUS_TESTING)) { minHeight += 14; } } } if (gCheatsAllowArbitraryRideTypeChanges) { minHeight += 15; } w->flags |= WF_RESIZABLE; window_set_resize(w, 316, minHeight, 500, 450); window_ride_init_viewport(w); } /** * * rct2: 0x006AF825 */ static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget) { rct_widget* dropdownWidget = widget - 1; auto ride = get_ride(w->number); if (ride == nullptr) return; int32_t numItems = 1; if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES)) { numItems += ride->num_stations; numItems += ride->num_vehicles; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, 0, numItems, widget->right - dropdownWidget->left); // First item gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[0] = STR_OVERALL_VIEW; int32_t currentItem = 1; // Vehicles int32_t name = RideComponentNames[RideNameConvention[ride->type].vehicle].number; for (int32_t i = 1; i <= ride->num_vehicles; i++) { gDropdownItemsFormat[currentItem] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[currentItem] = name | (currentItem << 16); currentItem++; } // Stations name = RideComponentNames[RideNameConvention[ride->type].station].number; for (int32_t i = 1; i <= ride->num_stations; i++) { gDropdownItemsFormat[currentItem] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[currentItem] = 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_set_disabled(i + 1, true); ; } } // Set checked item dropdown_set_checked(w->ride.view, true); } static uint8_t window_ride_get_next_default_status(const Ride* ride) { switch (ride->status) { default: case RIDE_STATUS_CLOSED: if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) || (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE)) { return RIDE_STATUS_CLOSED; } else if (ride->SupportsStatus(RIDE_STATUS_TESTING) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) { return RIDE_STATUS_TESTING; } else { return RIDE_STATUS_OPEN; } case RIDE_STATUS_SIMULATING: return RIDE_STATUS_TESTING; case RIDE_STATUS_TESTING: return (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RIDE_STATUS_OPEN : RIDE_STATUS_CLOSED; case RIDE_STATUS_OPEN: return RIDE_STATUS_CLOSED; } } struct RideStatusDropdownInfo { struct Ride* Ride{}; uint8_t CurrentStatus{}; uint8_t DefaultStatus{}; int32_t NumItems{}; int32_t CheckedIndex = -1; int32_t DefaultIndex = -1; }; static void window_ride_set_dropdown(RideStatusDropdownInfo& info, uint8_t status, rct_string_id text) { if (info.Ride->SupportsStatus(status)) { auto index = info.NumItems; gDropdownItemsFormat[index] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[index] = text; if (info.CurrentStatus == status) { info.CheckedIndex = index; } if (info.DefaultStatus == status) { info.DefaultIndex = index; } info.NumItems++; } } static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget) { RideStatusDropdownInfo info; info.Ride = get_ride(w->number); if (info.Ride == nullptr) return; info.CurrentStatus = info.Ride->status; info.DefaultStatus = window_ride_get_next_default_status(info.Ride); window_ride_set_dropdown(info, RIDE_STATUS_CLOSED, STR_CLOSE_RIDE); #ifdef __SIMULATE_IN_RIDE_WINDOW__ window_ride_set_dropdown(info, RIDE_STATUS_SIMULATING, STR_SIMULATE_RIDE); #endif window_ride_set_dropdown(info, RIDE_STATUS_TESTING, STR_TEST_RIDE); window_ride_set_dropdown(info, RIDE_STATUS_OPEN, STR_OPEN_RIDE); window_dropdown_show_text( w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], 0, info.NumItems); dropdown_set_checked(info.CheckedIndex, true); gDropdownDefaultIndex = info.DefaultIndex; } static void populate_ride_type_dropdown() { auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); if (RideDropdownDataLanguage == ls.GetCurrentLanguage()) return; RideDropdownData.clear(); for (uint8_t i = 0; i < RIDE_TYPE_COUNT; i++) { RideTypeLabel label = { i, RideNaming[i].name, ls.GetString(RideNaming[i].name) }; RideDropdownData.push_back(label); } 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 window_ride_show_ride_type_dropdown(rct_window* w, rct_widget* widget) { auto ride = get_ride(w->number); if (ride == nullptr) return; populate_ride_type_dropdown(); for (size_t i = 0; i < RideDropdownData.size(); i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = RideDropdownData[i].label_id; } rct_widget* dropdownWidget = widget - 1; window_dropdown_show_text( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], DROPDOWN_FLAG_STAY_OPEN, RIDE_TYPE_COUNT); // Find the current ride type in the ordered list. uint8_t pos = 0; for (uint8_t i = 0; i < RIDE_TYPE_COUNT; i++) { if (RideDropdownData[i].ride_type_id == ride->type) { pos = i; break; } } gDropdownHighlightedIndex = pos; gDropdownDefaultIndex = pos; dropdown_set_checked(pos, true); } static void populate_vehicle_type_dropdown(Ride* ride) { rct_ride_entry* rideEntry = ride->GetRideEntry(); bool selectionShouldBeExpanded; int32_t rideTypeIterator, rideTypeIteratorMax; if (gCheatsShowVehiclesFromOtherTrackTypes && !(ride_type_has_flag(ride->type, 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 (VehicleDropdownExpanded == selectionShouldBeExpanded && VehicleDropdownRideType == rideEntry && VehicleDropdownDataLanguage == ls.GetCurrentLanguage()) return; VehicleDropdownData.clear(); for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++) { if (selectionShouldBeExpanded && ride_type_has_flag(rideTypeIterator, RIDE_TYPE_FLAG_FLAT_RIDE)) continue; if (selectionShouldBeExpanded && (rideTypeIterator == RIDE_TYPE_MAZE || rideTypeIterator == RIDE_TYPE_MINI_GOLF)) continue; uint8_t* rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideTypeIterator); for (uint8_t* currentRideEntryIndex = rideEntryIndexPtr; *currentRideEntryIndex != RIDE_ENTRY_INDEX_NULL; currentRideEntryIndex++) { int32_t rideEntryIndex = *currentRideEntryIndex; rct_ride_entry* currentRideEntry = get_ride_entry(rideEntryIndex); // Skip if vehicle type has not been invented yet if (!ride_entry_is_invented(rideEntryIndex) && !gCheatsIgnoreResearchStatus) continue; // Skip if vehicle does not belong to the same ride group if (RideGroupManager::RideTypeHasRideGroups(ride->type) && !selectionShouldBeExpanded) { const RideGroup* rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry); const RideGroup* currentRideGroup = RideGroupManager::GetRideGroup(ride->type, currentRideEntry); if (!rideGroup->Equals(currentRideGroup)) continue; } VehicleTypeLabel label = { rideEntryIndex, currentRideEntry->naming.name, ls.GetString(currentRideEntry->naming.name) }; VehicleDropdownData.push_back(label); } } 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 window_ride_show_vehicle_type_dropdown(rct_window* w, rct_widget* widget) { auto ride = get_ride(w->number); if (ride == nullptr) return; populate_vehicle_type_dropdown(ride); size_t numItems = std::min(VehicleDropdownData.size(), DROPDOWN_ITEMS_MAX_SIZE); for (size_t i = 0; i < numItems; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = VehicleDropdownData[i].label_id; } rct_widget* dropdownWidget = widget - 1; window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left); // Find the current vehicle type in the ordered list. uint8_t pos = 0; for (uint8_t i = 0; i < VehicleDropdownData.size(); i++) { if (VehicleDropdownData[i].subtype_id == ride->subtype) { pos = i; break; } } gDropdownHighlightedIndex = pos; gDropdownDefaultIndex = pos; dropdown_set_checked(pos, true); } /** * * rct2: 0x006AF1BD */ static void window_ride_main_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { switch (widgetIndex) { case WIDX_VIEW_DROPDOWN: window_ride_show_view_dropdown(w, widget); break; case WIDX_OPEN: window_ride_show_open_dropdown(w, widget); break; case WIDX_RIDE_TYPE_DROPDOWN: window_ride_show_ride_type_dropdown(w, widget); break; } } /** * * rct2: 0x006AF300 */ static void window_ride_main_dropdown(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->number); 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; window_ride_init_viewport(w); w->Invalidate(); break; case WIDX_OPEN: { auto ride = get_ride(w->number); if (ride != nullptr) { auto status = RIDE_STATUS_CLOSED; if (dropdownIndex < 0) { dropdownIndex = gDropdownHighlightedIndex; } if (dropdownIndex < (int32_t)std::size(gDropdownItemsArgs)) { switch (gDropdownItemsArgs[dropdownIndex]) { case STR_CLOSE_RIDE: status = RIDE_STATUS_CLOSED; break; case STR_SIMULATE_RIDE: status = RIDE_STATUS_SIMULATING; break; case STR_TEST_RIDE: status = RIDE_STATUS_TESTING; break; case STR_OPEN_RIDE: status = RIDE_STATUS_OPEN; break; } } ride_set_status(ride, status); } break; } case WIDX_RIDE_TYPE_DROPDOWN: if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT) { uint8_t rideLabelId = std::clamp(dropdownIndex, 0, RIDE_TYPE_COUNT - 1); uint8_t rideType = RideDropdownData[rideLabelId].ride_type_id; if (rideType < RIDE_TYPE_COUNT) { set_operating_setting(w->number, RideSetSetting::RideType, rideType); } } } } /** * * rct2: 0x006AF40F */ static void window_ride_main_update(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->number); 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) { int32_t vehicleIndex = w->ride.view - 1; uint16_t vehicleSpriteIndex = ride->vehicles[vehicleIndex]; if (vehicleSpriteIndex == SPRITE_INDEX_NULL) return; rct_vehicle* vehicle = &(get_sprite(vehicleSpriteIndex)->vehicle); if (vehicle->status != VEHICLE_STATUS_TRAVELLING && vehicle->status != VEHICLE_STATUS_TRAVELLING_CABLE_LIFT && vehicle->status != VEHICLE_STATUS_TRAVELLING_DODGEMS && vehicle->status != VEHICLE_STATUS_TRAVELLING_BOAT) { return; } } } ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN; } widget_invalidate(w, WIDX_STATUS); } /** * * rct2: 0x006AF2F9 */ static void window_ride_main_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text) { if (widgetIndex != WIDX_RENAME || text == nullptr) return; auto ride = get_ride(w->number); if (ride != nullptr) { ride_set_name(ride, text, 0); } } /** * * rct2: 0x006AF55A */ static void window_ride_main_viewport_rotate(rct_window* w) { window_ride_init_viewport(w); } /** * * rct2: 0x006AECF6 */ static void window_ride_main_invalidate(rct_window* w) { rct_widget* widgets; int32_t i, height; widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; w->disabled_widgets &= ~((1 << WIDX_DEMOLISH) | (1 << WIDX_CONSTRUCTION)); if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)) w->disabled_widgets |= (1 << WIDX_DEMOLISH); ride->FormatNameTo(gCommonFormatArgs); uint32_t spriteIds[] = { SPR_CLOSED, SPR_OPEN, SPR_TESTING, SPR_G2_SIMULATE, }; window_ride_main_widgets[WIDX_OPEN].image = spriteIds[ride->status]; #ifdef __SIMULATE_IN_RIDE_WINDOW__ window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2 + widget_is_pressed(w, WIDX_CLOSE_LIGHT); window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 + (ride->status == RIDE_STATUS_SIMULATING) * 2 + widget_is_pressed(w, WIDX_SIMULATE_LIGHT); window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RIDE_STATUS_TESTING) * 2 + widget_is_pressed(w, WIDX_TEST_LIGHT); #else window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2 + widget_is_pressed(w, WIDX_CLOSE_LIGHT); auto baseSprite = ride->status == RIDE_STATUS_SIMULATING ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0; window_ride_main_widgets[WIDX_TEST_LIGHT].image = baseSprite + (ride->status == RIDE_STATUS_TESTING || ride->status == RIDE_STATUS_SIMULATING) * 2 + widget_is_pressed(w, WIDX_TEST_LIGHT); #endif window_ride_main_widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RIDE_STATUS_OPEN) * 2 + widget_is_pressed(w, WIDX_OPEN_LIGHT); window_ride_anchor_border_widgets(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 = WWT_EMPTY; window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WWT_EMPTY; } else { window_ride_main_widgets[WIDX_RIDE_TYPE].type = WWT_DROPDOWN; window_ride_main_widgets[WIDX_RIDE_TYPE].text = RideNaming[ride->type].name; window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WWT_BUTTON; } window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE) { window_ride_main_widgets[WIDX_OPEN].type = WWT_EMPTY; window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN; window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY; #ifdef __SIMULATE_IN_RIDE_WINDOW__ if (ride->SupportsStatus(RIDE_STATUS_SIMULATING)) window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_IMGBTN; #endif window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RIDE_STATUS_TESTING) ? WWT_IMGBTN : WWT_EMPTY; window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_IMGBTN; height = 62; if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WWT_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 != WWT_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 = WWT_FLATBTN; window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_EMPTY; window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY; window_ride_main_widgets[WIDX_TEST_LIGHT].type = WWT_EMPTY; window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_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 window_ride_get_status_overall_view(rct_window* w, void* arguments) { auto stringId = STR_NONE; auto ride = get_ride(w->number); if (ride != nullptr) { ride->FormatStatusTo(arguments); 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 window_ride_get_status_vehicle(rct_window* w, void* arguments) { rct_vehicle* vehicle; int32_t vehicleIndex; uint16_t vehicleSpriteIndex; rct_string_id stringId; auto ride = get_ride(w->number); if (ride == nullptr) return 0; vehicleIndex = w->ride.view - 1; vehicleSpriteIndex = ride->vehicles[vehicleIndex]; if (vehicleSpriteIndex == SPRITE_INDEX_NULL) return 0; vehicle = &(get_sprite(vehicleSpriteIndex)->vehicle); if (vehicle->status != VEHICLE_STATUS_CRASHING && vehicle->status != VEHICLE_STATUS_CRASHED) { int32_t trackType = vehicle->track_type >> 2; if (trackType == TRACK_ELEM_BLOCK_BRAKES || trackType == TRACK_ELEM_CABLE_LIFT_HILL || trackType == TRACK_ELEM_25_DEG_UP_TO_FLAT || trackType == TRACK_ELEM_60_DEG_UP_TO_FLAT || trackType == TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT || trackType == TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT) { if (track_piece_is_available_for_ride_type(ride->type, TRACK_BLOCK_BRAKES) && vehicle->velocity == 0) { *(rct_string_id*)(uintptr_t)arguments = STR_STOPPED_BY_BLOCK_BRAKES; return STR_BLACK_STRING; } } } stringId = VehicleStatusNames[vehicle->status]; // Get speed in mph *((uint16_t*)((uintptr_t)arguments + 2)) = (abs(vehicle->velocity) * 9) >> 18; if (ride->type == RIDE_TYPE_MINI_GOLF) return 0; if ((RideData4[ride->type].flags & RIDE_TYPE_FLAG4_SINGLE_SESSION) && vehicle->status <= VEHICLE_STATUS_UNLOADING_PASSENGERS) { stringId = SingleSessionVehicleStatusNames[vehicle->status]; } const ride_component_name stationName = RideComponentNames[RideNameConvention[ride->type].station]; *(rct_string_id*)((uintptr_t)arguments + 4) = (ride->num_stations > 1) ? stationName.number : stationName.singular; *((uint16_t*)((uintptr_t)arguments + 6)) = vehicle->current_station + 1; *(rct_string_id*)((uintptr_t)arguments + 0) = stringId; return stringId != STR_CRASHING && stringId != STR_CRASHED_0 ? STR_BLACK_STRING : STR_RED_OUTLINED_STRING; } /** * * rct2: 0x006AEF65 */ static rct_string_id window_ride_get_status_station(rct_window* w, void* arguments) { auto ride = get_ride(w->number); if (ride == nullptr) return STR_NONE; int32_t count = w->ride.view - ride->num_vehicles - 1; int32_t stationIndex = -1; rct_string_id stringId = 0; do { stationIndex++; if (ride->stations[stationIndex].Start.xy != RCT_XY8_UNDEFINED) count--; } while (count >= 0); // Entrance / exit if (ride->status == RIDE_STATUS_CLOSED) { if (ride_get_entrance_location(ride, (uint8_t)stationIndex).isNull()) stringId = STR_NO_ENTRANCE; else if (ride_get_exit_location(ride, (uint8_t)stationIndex).isNull()) stringId = STR_NO_EXIT; } else { if (ride_get_entrance_location(ride, (uint8_t)stationIndex).isNull()) stringId = STR_EXIT_ONLY; } // Queue length if (stringId == 0) { int32_t queueLength = ride->stations[stationIndex].QueueLength; set_format_arg_body(static_cast(arguments), 2, (uintptr_t)queueLength, sizeof(uint16_t)); stringId = STR_QUEUE_EMPTY; if (queueLength == 1) stringId = STR_QUEUE_ONE_PERSON; else if (queueLength > 1) stringId = STR_QUEUE_PEOPLE; } set_format_arg_body(static_cast(arguments), 0, (uintptr_t)stringId, sizeof(rct_string_id)); return STR_BLACK_STRING; } /** * * rct2: 0x006AEE73 */ static rct_string_id window_ride_get_status(rct_window* w, void* arguments) { auto ride = get_ride(w->number); if (w->ride.view == 0) return window_ride_get_status_overall_view(w, arguments); if (ride != nullptr && w->ride.view <= ride->num_vehicles) return window_ride_get_status_vehicle(w, arguments); if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) return window_ride_get_status_overall_view(w, arguments); return window_ride_get_status_station(w, arguments); } /** * * rct2: 0x006AEE73 */ static void window_ride_main_paint(rct_window* w, rct_drawpixelinfo* dpi) { rct_widget* widget; rct_string_id stringId; window_draw_widgets(w, dpi); window_ride_draw_tab_images(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, SPR_HEARING_VIEWPORT, w->x + 2, w->y + 2, 0); } // View dropdown auto ride = get_ride(w->number); if (ride == nullptr) return; stringId = STR_OVERALL_VIEW; if (w->ride.view != 0) { stringId = RideComponentNames[RideNameConvention[ride->type].vehicle].number; if (w->ride.view > ride->num_vehicles) { set_format_arg(2, uint16_t, w->ride.view - ride->num_vehicles); stringId = RideComponentNames[RideNameConvention[ride->type].station].number; } else { set_format_arg(2, uint16_t, w->ride.view); } } set_format_arg(0, uint16_t, stringId); widget = &window_ride_main_widgets[WIDX_VIEW]; gfx_draw_string_centred( dpi, STR_WINDOW_COLOUR_2_STRINGID, w->x + (widget->left + widget->right - 11) / 2, w->y + widget->top, COLOUR_BLACK, gCommonFormatArgs); // Status widget = &window_ride_main_widgets[WIDX_STATUS]; rct_string_id ride_status = window_ride_get_status(w, gCommonFormatArgs); gfx_draw_string_centred_clipped( dpi, ride_status, gCommonFormatArgs, COLOUR_BLACK, w->x + (widget->left + widget->right) / 2, w->y + widget->top, widget->right - widget->left); } #pragma endregion #pragma region Vehicle /** * * rct2: 0x006B272D */ static void window_ride_vehicle_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; } } /** * * rct2: 0x006B2ABB */ static void window_ride_vehicle_resize(rct_window* w) { window_set_resize(w, 316, 214, 316, 214); } /** * * rct2: 0x006B2748 */ static void window_ride_vehicle_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { auto ride = get_ride(w->number); if (ride == nullptr) return; switch (widgetIndex) { case WIDX_VEHICLE_TYPE_DROPDOWN: window_ride_show_vehicle_type_dropdown(w, &w->widgets[widgetIndex]); break; case WIDX_VEHICLE_TRAINS_INCREASE: if (ride->num_vehicles < 32) 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 < 255) 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 window_ride_vehicle_dropdown(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(dropdownIndex) < VehicleDropdownData.size()) { auto ride = get_ride(w->number); if (ride != nullptr) { auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id; ride->SetRideEntry(newRideType); } } break; } } /** * * rct2: 0x006B2AA1 */ static void window_ride_vehicle_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_2); } /** * * rct2: 0x006B222C */ static void window_ride_vehicle_invalidate(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; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; rideEntry = ride->GetRideEntry(); w->widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID; ride->FormatNameTo(gCommonFormatArgs + 20); // 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 = WWT_SPINNER; window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WWT_BUTTON; window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WWT_BUTTON; } else { window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WWT_EMPTY; window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WWT_EMPTY; window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WWT_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 = WWT_SPINNER; window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WWT_BUTTON; window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WWT_BUTTON; } else { window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WWT_EMPTY; window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WWT_EMPTY; window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WWT_EMPTY; } set_format_arg(6, uint16_t, carsPerTrain); RIDE_COMPONENT_TYPE vehicleType = RideNameConvention[ride->type].vehicle; stringId = RideComponentNames[vehicleType].count; if (ride->num_vehicles > 1) { stringId = RideComponentNames[vehicleType].count_plural; } set_format_arg(8, rct_string_id, stringId); set_format_arg(10, uint16_t, ride->num_vehicles); stringId = RideComponentNames[vehicleType].count; if (ride->max_trains > 1) { stringId = RideComponentNames[vehicleType].count_plural; } set_format_arg(12, rct_string_id, stringId); set_format_arg(14, uint16_t, ride->max_trains); set_format_arg(16, uint16_t, std::max(1, ride->min_max_cars_per_train & 0xF) - rideEntry->zero_cars); stringId = RideComponentNames[RIDE_COMPONENT_TYPE_CAR].singular; if ((ride->min_max_cars_per_train & 0xF) - rideEntry->zero_cars > 1) { stringId = RideComponentNames[RIDE_COMPONENT_TYPE_CAR].plural; } set_format_arg(18, rct_string_id, stringId); window_ride_anchor_border_widgets(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 window_ride_vehicle_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); auto ride = get_ride(w->number); if (ride == nullptr) return; auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; int32_t x = w->x + 8; int32_t y = w->y + 64; // Description y += gfx_draw_string_left_wrapped(dpi, &rideEntry->naming.description, x, y, 300, STR_BLACK_STRING, COLOUR_BLACK); y += 2; // Capacity gfx_draw_string_left(dpi, STR_CAPACITY, &rideEntry->capacity, COLOUR_BLACK, x, y); // Excitement Factor auto factor = (int16_t)rideEntry->excitement_multiplier; if (factor > 0) { y += LIST_ROW_HEIGHT; gfx_draw_string_left(dpi, STR_EXCITEMENT_FACTOR, &factor, COLOUR_BLACK, x, y); } // Intensity Factor factor = rideEntry->intensity_multiplier; if (factor > 0) { int32_t lineHeight = font_get_line_height(FONT_SPRITE_BASE_MEDIUM); if (lineHeight != 10) x += 150; else y += LIST_ROW_HEIGHT; gfx_draw_string_left(dpi, STR_INTENSITY_FACTOR, &factor, COLOUR_BLACK, x, y); if (lineHeight != 10) x -= 150; } // Nausea Factor factor = rideEntry->nausea_multiplier; if (factor > 0) { y += LIST_ROW_HEIGHT; gfx_draw_string_left(dpi, STR_NAUSEA_FACTOR, &factor, COLOUR_BLACK, x, y); } } struct rct_vehicle_paintinfo { int16_t x; int16_t y; int32_t sprite_index; int32_t tertiary_colour; }; static rct_vehicle_paintinfo _sprites_to_draw[144]; /** * * rct2: 0x006B2502 */ static void window_ride_vehicle_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { auto ride = get_ride(w->number); 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->right - widget->left) - ((ride->num_vehicles - 1) * 36)) / 2 - 25); int32_t startY = widget->bottom - widget->top - 4; rct_ride_entry_vehicle* rideVehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position( ride->subtype, ride->num_cars_per_train, 0)]; startY += rideVehicleEntry->tab_height; // For each train for (int32_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle_paintinfo* nextSpriteToDraw = _sprites_to_draw; int32_t x = startX; int32_t y = startY; // For each car in train for (int32_t j = 0; j < ride->num_cars_per_train; j++) { rideVehicleEntry = &rideEntry ->vehicles[ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, j)]; x += rideVehicleEntry->spacing / 17432; y -= (rideVehicleEntry->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; } vehicle_colour vehicleColour = ride_get_vehicle_colour(ride, vehicleColourIndex); int32_t spriteIndex = 16; if (rideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_11) spriteIndex /= 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->base_num_frames; spriteIndex += rideVehicleEntry->base_image_id; spriteIndex |= (vehicleColour.additional_1 << 24) | (vehicleColour.main << 19); spriteIndex |= IMAGE_TYPE_REMAP_2_PLUS; nextSpriteToDraw->x = x; nextSpriteToDraw->y = y; nextSpriteToDraw->sprite_index = spriteIndex; nextSpriteToDraw->tertiary_colour = vehicleColour.additional_2; nextSpriteToDraw++; x += rideVehicleEntry->spacing / 17432; y -= (rideVehicleEntry->spacing / 2) / 17432; } if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER) { rct_vehicle_paintinfo tmp = *(nextSpriteToDraw - 1); *(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2); *(nextSpriteToDraw - 2) = tmp; } rct_vehicle_paintinfo* current = nextSpriteToDraw; while (--current >= _sprites_to_draw) gfx_draw_sprite(dpi, current->sprite_index, current->x, current->y, current->tertiary_colour); startX += 36; } } #pragma endregion #pragma region Operating /** * * rct2: 0x006B11D5 */ static void window_ride_mode_tweak_increase(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return; uint8_t maxValue = RideProperties[ride->type].max_value; uint8_t minValue = gCheatsFastLiftHill ? 0 : RideProperties[ride->type].min_value; if (gCheatsFastLiftHill) { maxValue = 255; } uint8_t increment = ride->mode == RIDE_MODE_BUMPERCAR ? 10 : 1; set_operating_setting( w->number, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); } /** * * rct2: 0x006B120A */ static void window_ride_mode_tweak_decrease(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return; uint8_t maxValue = RideProperties[ride->type].max_value; uint8_t minValue = gCheatsFastLiftHill ? 0 : RideProperties[ride->type].min_value; if (gCheatsFastLiftHill) { maxValue = 255; } uint8_t decrement = ride->mode == RIDE_MODE_BUMPERCAR ? 10 : 1; set_operating_setting( w->number, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); } /** * * rct2: 0x006B1631 */ static void window_ride_mode_dropdown(rct_window* w, rct_widget* widget) { rct_widget* dropdownWidget; const uint8_t *availableModes, *mode; int32_t i, numAvailableModes; dropdownWidget = widget - 1; auto ride = get_ride(w->number); if (ride == nullptr) return; // Seek to available modes for this ride availableModes = ride_seek_available_modes(ride); // Count number of available modes mode = availableModes; numAvailableModes = -1; do { numAvailableModes++; } while (*(mode++) != 255); // Create dropdown list for (i = 0; i < numAvailableModes; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = RideModeNames[availableModes[i]]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numAvailableModes, widget->right - dropdownWidget->left); // Set checked item for (i = 0; i < numAvailableModes; i++) { if (ride->mode == availableModes[i]) { dropdown_set_checked(i, true); } } } /** * * rct2: 0x006B15C0 */ static void window_ride_load_dropdown(rct_window* w, rct_widget* widget) { auto ride = get_ride(w->number); if (ride == nullptr) return; auto dropdownWidget = widget - 1; for (auto i = 0; i < 5; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = VehicleLoadNames[i]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, 5, widget->right - dropdownWidget->left); dropdown_set_checked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true); } /** * * rct2: 0x006B10A7 */ static void window_ride_operating_mouseup(rct_window* w, rct_widgetindex widgetIndex) { auto ride = get_ride(w->number); 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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_LOAD_CHECKBOX: set_operating_setting(w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_LOAD); break; case WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX: set_operating_setting( w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES); break; case WIDX_MINIMUM_LENGTH_CHECKBOX: set_operating_setting( w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH); break; case WIDX_MAXIMUM_LENGTH_CHECKBOX: set_operating_setting( w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH); break; case WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX: set_operating_setting( w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS); break; } } /** * * rct2: 0x006B1715 */ static void window_ride_operating_resize(rct_window* w) { window_set_resize(w, 316, 186, 316, 186); } /** * * rct2: 0x006B10F4 */ static void window_ride_operating_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { auto ride = get_ride(w->number); if (ride == nullptr) return; uint8_t upper_bound, lower_bound; switch (widgetIndex) { case WIDX_MODE_TWEAK_INCREASE: window_ride_mode_tweak_increase(w); break; case WIDX_MODE_TWEAK_DECREASE: window_ride_mode_tweak_decrease(w); break; case WIDX_LIFT_HILL_SPEED_INCREASE: upper_bound = gCheatsFastLiftHill ? 255 : RideLiftData[ride->type].maximum_speed; lower_bound = gCheatsFastLiftHill ? 0 : RideLiftData[ride->type].minimum_speed; set_operating_setting( w->number, RideSetSetting::LiftHillSpeed, std::clamp(ride->lift_hill_speed + 1, lower_bound, upper_bound)); break; case WIDX_LIFT_HILL_SPEED_DECREASE: upper_bound = gCheatsFastLiftHill ? 255 : RideLiftData[ride->type].maximum_speed; lower_bound = gCheatsFastLiftHill ? 0 : RideLiftData[ride->type].minimum_speed; set_operating_setting( w->number, RideSetSetting::LiftHillSpeed, std::clamp(ride->lift_hill_speed - 1, lower_bound, upper_bound)); break; case WIDX_MINIMUM_LENGTH_INCREASE: upper_bound = 250; lower_bound = 0; set_operating_setting( w->number, RideSetSetting::MinWaitingTime, std::clamp(ride->min_waiting_time + 1, lower_bound, upper_bound)); break; case WIDX_MINIMUM_LENGTH_DECREASE: upper_bound = 250; lower_bound = 0; set_operating_setting( w->number, RideSetSetting::MinWaitingTime, std::clamp(ride->min_waiting_time - 1, lower_bound, upper_bound)); break; case WIDX_MAXIMUM_LENGTH_INCREASE: upper_bound = 250; lower_bound = 0; set_operating_setting( w->number, RideSetSetting::MaxWaitingTime, std::clamp(ride->max_waiting_time + 1, lower_bound, upper_bound)); break; case WIDX_MAXIMUM_LENGTH_DECREASE: upper_bound = 250; lower_bound = 0; set_operating_setting( w->number, RideSetSetting::MaxWaitingTime, std::clamp(ride->max_waiting_time - 1, lower_bound, upper_bound)); break; case WIDX_MODE_DROPDOWN: window_ride_mode_dropdown(w, widget); break; case WIDX_LOAD_DROPDOWN: window_ride_load_dropdown(w, widget); break; case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: upper_bound = gCheatsFastLiftHill ? 255 : 20; lower_bound = 1; set_operating_setting( w->number, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits + 1, lower_bound, upper_bound)); break; case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE: upper_bound = gCheatsFastLiftHill ? 255 : 20; lower_bound = 1; set_operating_setting( w->number, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits - 1, lower_bound, upper_bound)); break; } } /** * * rct2: 0x006B1165 */ static void window_ride_operating_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; auto ride = get_ride(w->number); if (ride == nullptr) return; switch (widgetIndex) { case WIDX_MODE_DROPDOWN: { // Seek to available modes for this ride auto availableModes = ride_seek_available_modes(ride); if (availableModes != nullptr) set_operating_setting(w->number, RideSetSetting::Mode, availableModes[dropdownIndex]); break; } case WIDX_LOAD_DROPDOWN: set_operating_setting( w->number, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex); break; } } /** * * rct2: 0x006B178E */ static void window_ride_operating_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_3); auto ride = get_ride(w->number); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; w->Invalidate(); } } /** * * rct2: 0x006B0B30 */ static void window_ride_operating_invalidate(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; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; ride->FormatNameTo(gCommonFormatArgs); // 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)); // Lift hill speed if (track_piece_is_available_for_ride_type(ride->type, TRACK_LIFT_HILL)) { window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WWT_LABEL; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WWT_SPINNER; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WWT_BUTTON; set_format_arg(20, uint16_t, ride->lift_hill_speed); } else { window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WWT_EMPTY; } // Number of circuits if (ride->CanHaveMultipleCircuits()) { window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WWT_LABEL; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WWT_SPINNER; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WWT_BUTTON; set_format_arg(22, uint16_t, ride->num_circuits); } else { window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WWT_EMPTY; } // Leave if another vehicle arrives at station if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_LEAVE_WHEN_ANOTHER_VEHICLE_ARRIVES_AT_STATION) && ride->num_vehicles > 1 && ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED && ride->mode != RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) { window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WWT_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 = RideNameConvention[ride->type].vehicle == RIDE_COMPONENT_TYPE_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 = WWT_EMPTY; } // Synchronise with adjacent stations if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS)) { window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WWT_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 = WWT_EMPTY; } // Mode window_ride_operating_widgets[WIDX_MODE].text = RideModeNames[ride->mode]; // Waiting window_ride_operating_widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)]; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)) { window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WWT_CHECKBOX; window_ride_operating_widgets[WIDX_LOAD].type = WWT_DROPDOWN; window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WWT_CHECKBOX; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WWT_SPINNER; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WWT_CHECKBOX; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WWT_SPINNER; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WWT_BUTTON; set_format_arg(10, rct_string_id, STR_FORMAT_SECONDS); set_format_arg(12, uint16_t, ride->min_waiting_time); set_format_arg(14, rct_string_id, STR_FORMAT_SECONDS); set_format_arg(16, uint16_t, ride->max_waiting_time); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD) w->pressed_widgets |= (1 << WIDX_LOAD_CHECKBOX); } else { window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_LOAD].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WWT_EMPTY; } if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) w->pressed_widgets |= (1 << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) w->pressed_widgets |= (1 << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) w->pressed_widgets |= (1 << WIDX_MINIMUM_LENGTH_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) w->pressed_widgets |= (1 << WIDX_MAXIMUM_LENGTH_CHECKBOX); // Mode specific functionality set_format_arg(18, uint16_t, ride->operation_option); switch (ride->mode) { case RIDE_MODE_POWERED_LAUNCH_PASSTROUGH: case RIDE_MODE_POWERED_LAUNCH: case RIDE_MODE_UPWARD_LAUNCH: case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: set_format_arg(18, uint16_t, (ride->launch_speed * 9) / 4); format = STR_RIDE_MODE_SPEED_VALUE; caption = STR_LAUNCH_SPEED; tooltip = STR_LAUNCH_SPEED_TIP; break; case RIDE_MODE_STATION_TO_STATION: set_format_arg(18, uint16_t, (ride->speed * 9) / 4); format = STR_RIDE_MODE_SPEED_VALUE; caption = STR_SPEED; tooltip = STR_SPEED_TIP; break; case RIDE_MODE_RACE: set_format_arg(18, uint16_t, ride->num_laps); format = STR_NUMBER_OF_LAPS_VALUE; caption = STR_NUMBER_OF_LAPS; tooltip = STR_NUMBER_OF_LAPS_TIP; break; case RIDE_MODE_BUMPERCAR: format = STR_RIDE_MODE_TIME_LIMIT_VALUE; caption = STR_TIME_LIMIT; tooltip = STR_TIME_LIMIT_TIP; break; case RIDE_MODE_SWING: format = STR_RIDE_MODE_NUMBER_OF_SWINGS_VALUE; caption = STR_NUMBER_OF_SWINGS; tooltip = STR_NUMBER_OF_SWINGS_TIP; break; case RIDE_MODE_ROTATION: case RIDE_MODE_FORWARD_ROTATION: case RIDE_MODE_BACKWARD_ROTATION: 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_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES)) format = 0; break; } if (format != 0) { if (ride->type == RIDE_TYPE_TWIST) { uint16_t arg; std::memcpy(&arg, gCommonFormatArgs + 18, sizeof(uint16_t)); set_format_arg(18, uint16_t, arg * 3); } window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WWT_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 = WWT_SPINNER; window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format; window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WWT_BUTTON; window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WWT_BUTTON; w->pressed_widgets &= ~(1 << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); } else { window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WWT_EMPTY; window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WWT_EMPTY; } window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006B1001 */ static void window_ride_operating_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); auto ride = get_ride(w->number); if (ride == nullptr) return; // Horizontal rule between mode settings and depart settings gfx_fill_rect_inset( dpi, w->x + window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, w->y + 103, w->x + window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, w->y + 104, w->colours[1], INSET_RECT_FLAG_BORDER_INSET); // Number of block sections if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) { auto blockSections = ride->num_block_brakes + ride->num_stations; gfx_draw_string_left( dpi, STR_BLOCK_SECTIONS, &blockSections, COLOUR_BLACK, w->x + 21, ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED ? w->y + 89 : w->y + 61); } } #pragma endregion #pragma region Maintenance /** * * rct2: 0x006B1AE4 */ static void window_ride_locate_mechanic(rct_window* w) { auto ride = get_ride(w->number); 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 window_ride_maintenance_draw_bar( rct_window* w, rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t value, int32_t colour) { gfx_fill_rect_inset(dpi, x, y, x + 149, y + 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, x + 2, y + 1, x + value + 1, y + 7, colour, 0); } } /** * * rct2: 0x006B1AAD */ static void window_ride_maintenance_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_LOCATE_MECHANIC: window_ride_locate_mechanic(w); break; case WIDX_REFURBISH_RIDE: context_open_detail_window(WD_REFURBISH_RIDE, w->number); break; } } /** * * rct2: 0x006B1D70 */ static void window_ride_maintenance_resize(rct_window* w) { window_set_resize(w, 316, 135, 316, 135); } /** * * rct2: 0x006B1ACE */ static void window_ride_maintenance_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { auto ride = get_ride(w->number); 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++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = RideInspectionIntervalNames[i]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, 7, widget->right - dropdownWidget->left); dropdown_set_checked(ride->inspection_interval, true); break; case WIDX_FORCE_BREAKDOWN: num_items = 1; for (j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { if (rideEntry->ride_type[j] != 0xFF) break; } gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[0] = STR_DEBUG_FIX_RIDE; for (int32_t i = 0; i < 8; i++) { assert(j < (int32_t)std::size(rideEntry->ride_type)); if (RideAvailableBreakdowns[rideEntry->ride_type[j]] & (uint8_t)(1 << i)) { if (i == BREAKDOWN_BRAKES_FAILURE && (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED)) { if (ride->num_vehicles != 1) continue; } gDropdownItemsFormat[num_items] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[num_items] = RideBreakdownReasonNames[i]; num_items++; } } if (num_items == 1) { context_show_error(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE); } else { window_dropdown_show_text( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], DROPDOWN_FLAG_STAY_OPEN, 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 (RideAvailableBreakdowns[rideEntry->ride_type[j]] & (uint8_t)(1 << i)) { if (i == BREAKDOWN_BRAKES_FAILURE && (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED)) { if (ride->num_vehicles != 1) continue; } if (i == breakdownReason) { dropdown_set_checked(num_items, true); break; } gDropdownItemsFormat[num_items] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[num_items] = RideBreakdownReasonNames[i]; num_items++; } } } if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0) { dropdown_set_disabled(0, true); } } break; } } /** * * rct2: 0x006B1AD9 */ static void window_ride_maintenance_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; auto ride = get_ride(w->number); if (ride == nullptr) return; auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; switch (widgetIndex) { case WIDX_INSPECTION_INTERVAL_DROPDOWN: set_operating_setting(w->number, RideSetSetting::InspectionInterval, dropdownIndex); break; case WIDX_FORCE_BREAKDOWN: if (dropdownIndex == 0) { rct_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) { uint16_t spriteId = ride->vehicles[i]; while (spriteId != SPRITE_INDEX_NULL) { vehicle = GET_VEHICLE(spriteId); vehicle->update_flags &= ~( VEHICLE_UPDATE_FLAG_BROKEN_CAR | VEHICLE_UPDATE_FLAG_ZERO_VELOCITY | VEHICLE_UPDATE_FLAG_BROKEN_TRAIN); spriteId = vehicle->next_vehicle_on_train; } } break; case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: case BREAKDOWN_RESTRAINTS_STUCK_OPEN: case BREAKDOWN_DOORS_STUCK_CLOSED: case BREAKDOWN_DOORS_STUCK_OPEN: vehicle = &(get_sprite(ride->vehicles[ride->broken_vehicle])->vehicle); vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_CAR; break; case BREAKDOWN_VEHICLE_MALFUNCTION: vehicle = &(get_sprite(ride->vehicles[ride->broken_vehicle])->vehicle); vehicle->update_flags &= ~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 == RIDE_STATUS_CLOSED) { context_show_error(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_IS_CLOSED); } else { int32_t j; for (j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; 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 < (int32_t)std::size(rideEntry->ride_type)); if (RideAvailableBreakdowns[rideEntry->ride_type[j]] & (uint8_t)(1 << i)) { if (i == BREAKDOWN_BRAKES_FAILURE && (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED)) { if (ride->num_vehicles != 1) continue; } if (num_items == dropdownIndex) break; num_items++; } } ride_prepare_breakdown(ride, i); } break; } } /** * * rct2: 0x006B1D37 */ static void window_ride_maintenance_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_4); auto ride = get_ride(w->number); 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 window_ride_maintenance_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; ride->FormatNameTo(gCommonFormatArgs); window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; window_ride_anchor_border_widgets(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 = WWT_FLATBTN; } else { window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WWT_EMPTY; } if (RideAvailableBreakdowns[ride->type] == 0 || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) { w->disabled_widgets |= (1 << WIDX_REFURBISH_RIDE); window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; } else { w->disabled_widgets &= ~(1 << WIDX_REFURBISH_RIDE); window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; } } /** * * rct2: 0x006B1877 */ static void window_ride_maintenance_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); auto ride = get_ride(w->number); if (ride == nullptr) return; // Locate mechanic button image rct_widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC]; int32_t x = w->x + widget->left; int32_t y = w->y + widget->top; gfx_draw_sprite(dpi, (gStaffMechanicColour << 24) | IMAGE_TYPE_REMAP | IMAGE_TYPE_REMAP_2_PLUS | SPR_MECHANIC, x, y, 0); // Inspection label widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL]; x = w->x + 4; y = w->y + widget->top + 1; gfx_draw_string_left(dpi, STR_INSPECTION, nullptr, COLOUR_BLACK, x, y); // Reliability widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND]; x = w->x + widget->left + 4; y = w->y + widget->top + 4; uint16_t reliability = ride->reliability_percentage; gfx_draw_string_left(dpi, STR_RELIABILITY_LABEL_1757, &reliability, COLOUR_BLACK, x, y); window_ride_maintenance_draw_bar(w, dpi, x + 103, y, std::max(10, reliability), COLOUR_BRIGHT_GREEN); y += 11; uint16_t downTime = ride->downtime; gfx_draw_string_left(dpi, STR_DOWN_TIME_LABEL_1889, &downTime, COLOUR_BLACK, x, y); window_ride_maintenance_draw_bar(w, dpi, x + 103, y, downTime, COLOUR_BRIGHT_RED); y += 26; // Last inspection uint16_t lastInspection = ride->last_inspection; // Use singular form for 1 minute of time or less rct_string_id stringId; if (lastInspection <= 1) stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTE; else if (lastInspection <= 240) stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTES; else stringId = STR_TIME_SINCE_LAST_INSPECTION_MORE_THAN_4_HOURS; gfx_draw_string_left(dpi, stringId, &lastInspection, COLOUR_BLACK, x, y); 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; rct_string_id breakdownMessage = RideBreakdownReasonNames[ride->breakdown_reason]; gfx_draw_string_left(dpi, stringId, &breakdownMessage, COLOUR_BLACK, x, y); y += 10; // 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; uint16_t spriteIndex; Peep* peep; FOR_ALL_STAFF (spriteIndex, peep) { if (peep->staff_type == STAFF_TYPE_MECHANIC) { 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) { gfx_draw_string_left_wrapped(dpi, nullptr, x + 4, y, 280, stringId, COLOUR_BLACK); } else { auto peep = (&(get_sprite(ride->mechanic)->peep))->AsStaff(); if (peep != nullptr && peep->IsMechanic()) { peep->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left_wrapped(dpi, gCommonFormatArgs, x + 4, y, 280, stringId, COLOUR_BLACK); } } } } } #pragma endregion #pragma region Colour static uint32_t window_ride_get_colour_button_image(int32_t colour) { return IMAGE_TYPE_TRANSPARENT | SPRITE_ID_PALETTE_COLOUR_1(colour) | SPR_PALETTE_BTN; } static int32_t window_ride_has_track_colour(Ride* ride, int32_t trackColour) { // Get station flags (shops don't have them) auto stationObjFlags = 0; if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) { auto stationObj = ride_get_station_object(ride); if (stationObj != nullptr) { stationObjFlags = stationObj->Flags; } } switch (trackColour) { case 0: return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR) || ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN); case 1: return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR) || ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL); case 2: return ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS); default: return 0; } } static void window_ride_set_track_colour_scheme(rct_window* w, int32_t x, int32_t y) { TileElement* tileElement; uint8_t newColourScheme; int32_t interactionType, z, direction; newColourScheme = (uint8_t)w->ride_colour; LocationXY16 mapCoord = {}; get_map_coordinates_from_pos( x, y, VIEWPORT_INTERACTION_MASK_RIDE, &mapCoord.x, &mapCoord.y, &interactionType, &tileElement, nullptr); x = mapCoord.x; y = mapCoord.y; if (interactionType != VIEWPORT_INTERACTION_ITEM_RIDE) return; if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) return; if (tileElement->AsTrack()->GetRideIndex() != w->number) return; if (tileElement->AsTrack()->GetColourScheme() == newColourScheme) return; z = tileElement->base_height * 8; direction = tileElement->GetDirection(); auto gameAction = RideSetColourSchemeAction( CoordsXYZD{ x, y, z, static_cast(direction) }, tileElement->AsTrack()->GetTrackType(), newColourScheme); GameActions::Execute(&gameAction); } /** * * rct2: 0x006B04FA */ static void window_ride_colour_close(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 window_ride_colour_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_PAINT_INDIVIDUAL_AREA: tool_set(w, WIDX_PAINT_INDIVIDUAL_AREA, TOOL_PAINT_DOWN); break; } } /** * * rct2: 0x006B0AB6 */ static void window_ride_colour_resize(rct_window* w) { window_set_resize(w, 316, 207, 316, 207); } /** * * rct2: 0x006B02C6 */ static void window_ride_colour_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { vehicle_colour vehicleColour; int32_t i, numItems; rct_string_id stringId; auto ride = get_ride(w->number); 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 < NUM_COLOUR_SCHEMES; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = ColourSchemeNames[i]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, 4, widget->right - dropdownWidget->left); dropdown_set_checked(colourSchemeIndex, true); break; case WIDX_TRACK_MAIN_COLOUR: window_dropdown_show_colour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main); break; case WIDX_TRACK_ADDITIONAL_COLOUR: window_dropdown_show_colour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional); break; case WIDX_TRACK_SUPPORT_COLOUR: window_dropdown_show_colour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports); break; case WIDX_MAZE_STYLE_DROPDOWN: for (i = 0; i < 4; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = MazeOptions[i].text; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, 4, widget->right - dropdownWidget->left); dropdown_set_checked(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(objManager.GetLoadedObject(OBJECT_TYPE_STATION, i)); if (stationObj != nullptr) { gDropdownItemsFormat[ddIndex] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[ddIndex] = stationObj->NameStringId; if (ride->entrance_style == i) { gDropdownItemsFormat[ddIndex] = STR_DROPDOWN_MENU_LABEL_SELECTED; } ddIndex++; } } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, ddIndex, widget->right - dropdownWidget->left); break; } case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: for (i = 0; i < 3; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = (RideComponentNames[RideNameConvention[ride->type].vehicle].singular << 16) | VehicleColourSchemeNames[i]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, rideEntry->max_cars_in_train > 1 ? 3 : 2, widget->right - dropdownWidget->left); dropdown_set_checked(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, (int32_t)DROPDOWN_ITEMS_MAX_SIZE); i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = ((int64_t)(i + 1) << 32) | ((RideComponentNames[RideNameConvention[ride->type].vehicle].capitalised) << 16) | stringId; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left); dropdown_set_checked(w->vehicleIndex, true); break; case WIDX_VEHICLE_MAIN_COLOUR: vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); window_dropdown_show_colour(w, widget, w->colours[1], vehicleColour.main); break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_1: vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); window_dropdown_show_colour(w, widget, w->colours[1], vehicleColour.additional_1); break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_2: vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); window_dropdown_show_colour(w, widget, w->colours[1], vehicleColour.additional_2); break; } } /** * * rct2: 0x006B0331 */ static void window_ride_colour_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; switch (widgetIndex) { case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: w->ride_colour = (uint16_t)dropdownIndex; w->Invalidate(); break; case WIDX_TRACK_MAIN_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::TrackColourMain, dropdownIndex, w->ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_TRACK_ADDITIONAL_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::TrackColourAdditional, dropdownIndex, w->ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_TRACK_SUPPORT_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::TrackColourSupports, dropdownIndex, w->ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_MAZE_STYLE_DROPDOWN: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, 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(objManager.GetLoadedObject(OBJECT_TYPE_STATION, i)); if (stationObj != nullptr) { if (ddIndex == dropdownIndex) { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::EntranceStyle, ddIndex, 0); GameActions::Execute(&rideSetAppearanceAction); break; } ddIndex++; } } break; } case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, 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_MAIN_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::VehicleColourBody, dropdownIndex, w->vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_1: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::VehicleColourTrim, dropdownIndex, w->vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_2: { auto rideSetAppearanceAction = RideSetAppearanceAction( w->number, RideSetAppearanceType::VehicleColourTernary, dropdownIndex, w->vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; } } /** * * rct2: 0x006B0A8F */ static void window_ride_colour_update(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 window_ride_colour_tooldown(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) window_ride_set_track_colour_scheme(w, x, y); } /** * * rct2: 0x006B04F3 */ static void window_ride_colour_tooldrag(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) window_ride_set_track_colour_scheme(w, x, y); } /** * * rct2: 0x006AFB36 */ static void window_ride_colour_invalidate(rct_window* w) { TrackColour trackColour; vehicle_colour vehicleColour; auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; ride->FormatNameTo(gCommonFormatArgs + 16); // 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 = WWT_DROPDOWN; window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WWT_BUTTON; window_ride_colour_widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text; } else { window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WWT_EMPTY; } // Track, multiple colour schemes if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR)) { window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WWT_DROPDOWN; window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WWT_BUTTON; window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WWT_FLATBTN; } else { window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WWT_EMPTY; } // Track main colour if (window_ride_has_track_colour(ride, 0)) { window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].image = window_ride_get_colour_button_image(trackColour.main); } else { window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WWT_EMPTY; } // Track additional colour if (window_ride_has_track_colour(ride, 1)) { window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = window_ride_get_colour_button_image( trackColour.additional); } else { window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WWT_EMPTY; } // Track supports colour if (window_ride_has_track_colour(ride, 2) && ride->type != RIDE_TYPE_MAZE) { window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].image = window_ride_get_colour_button_image(trackColour.supports); } else { window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WWT_EMPTY; } // Track preview if (ride_type_has_flag( ride->type, 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 = WWT_SPINNER; else window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WWT_EMPTY; // Entrance style if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ENTRANCE_EXIT) { window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WWT_SPINNER; window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WWT_DROPDOWN; window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WWT_BUTTON; auto stringId = STR_NONE; auto stationObj = ride_get_station_object(ride); if (stationObj != nullptr) { stringId = stationObj->NameStringId; } window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].text = stringId; } else { window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WWT_EMPTY; } // Vehicle colours if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES) && ride_type_has_flag(ride->type, 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 = WWT_SCROLL; window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].image = window_ride_get_colour_button_image(vehicleColour.main); bool allowChangingAdditionalColour1 = false; bool allowChangingAdditionalColour2 = 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->vehicles[vehicleTypeIndex].flags & VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_1) { allowChangingAdditionalColour1 = true; } if (rideEntry->vehicles[vehicleTypeIndex].flags & VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_2) { allowChangingAdditionalColour2 = true; } } // Additional colours if (allowChangingAdditionalColour1) { window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].image = window_ride_get_colour_button_image( vehicleColour.additional_1); if (allowChangingAdditionalColour2) { window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WWT_COLOURBTN; window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].image = window_ride_get_colour_button_image( vehicleColour.additional_2); } else { window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WWT_EMPTY; } } else { window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WWT_EMPTY; } // Vehicle colour scheme type if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_16) && (ride->num_cars_per_train | ride->num_vehicles) > 1) { window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WWT_DROPDOWN; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WWT_BUTTON; } else { window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WWT_EMPTY; } set_format_arg(6, rct_string_id, VehicleColourSchemeNames[vehicleColourSchemeType]); set_format_arg(8, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].singular); set_format_arg(10, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].capitalised); set_format_arg(12, uint16_t, w->vehicleIndex + 1); // Vehicle index if (vehicleColourSchemeType != 0) { window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WWT_DROPDOWN; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WWT_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 = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WWT_EMPTY; } } else { window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WWT_EMPTY; window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WWT_EMPTY; } set_format_arg(14, rct_string_id, ColourSchemeNames[colourScheme]); window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006AFF3E */ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) { // TODO: This should use lists and identified sprites rct_drawpixelinfo clippedDpi; rct_widget* widget; auto ride = get_ride(w->number); if (ride == nullptr) return; window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); // Track / shop item preview widget = &window_ride_colour_widgets[WIDX_TRACK_PREVIEW]; if (widget->type != WWT_EMPTY) gfx_fill_rect( dpi, w->x + widget->left + 1, w->y + widget->top + 1, w->x + widget->right - 1, w->y + widget->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 == SHOP_ITEM_NONE) { int32_t x = w->x + widget->left; int32_t y = w->y + widget->top; // Track if (ride->type == RIDE_TYPE_MAZE) { int32_t spriteIndex = MazeOptions[trackColour.supports].sprite; gfx_draw_sprite(dpi, spriteIndex, x, y, 0); } else { int32_t spriteIndex = TrackColourPreviews[ride->type].track; if (spriteIndex != 0) { spriteIndex |= SPRITE_ID_PALETTE_COLOUR_2(trackColour.main, trackColour.additional); gfx_draw_sprite(dpi, spriteIndex, x, y, 0); } // Supports spriteIndex = TrackColourPreviews[ride->type].supports; if (spriteIndex != 0) { spriteIndex |= SPRITE_ID_PALETTE_COLOUR_1(trackColour.supports); gfx_draw_sprite(dpi, spriteIndex, x, y, 0); } } } else { int32_t x = w->x + (widget->left + widget->right) / 2 - 8; int32_t y = w->y + (widget->bottom + widget->top) / 2 - 6; uint8_t shopItem = rideEntry->shop_item_secondary == SHOP_ITEM_NONE ? rideEntry->shop_item : rideEntry->shop_item_secondary; int32_t spriteIndex = ShopItems[shopItem].Image; spriteIndex |= SPRITE_ID_PALETTE_COLOUR_1(ride->track_colour[0].main); gfx_draw_sprite(dpi, spriteIndex, x, y, 0); } // Entrance preview trackColour = ride_get_track_colour(ride, 0); widget = &w->widgets[WIDX_ENTRANCE_PREVIEW]; if (widget->type != WWT_EMPTY) { if (clip_drawpixelinfo( &clippedDpi, dpi, w->x + widget->left + 1, w->y + widget->top + 1, widget->right - widget->left, widget->bottom - widget->top)) { gfx_clear(&clippedDpi, PALETTE_INDEX_12); auto stationObj = ride_get_station_object(ride); if (stationObj != nullptr && stationObj->BaseImageId != 0) { int32_t terniaryColour = 0; if (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT) { terniaryColour = IMAGE_TYPE_TRANSPARENT | (GlassPaletteIds[trackColour.main] << 19); } int32_t spriteIndex = SPRITE_ID_PALETTE_COLOUR_2(trackColour.main, trackColour.additional); spriteIndex += stationObj->BaseImageId; // Back gfx_draw_sprite(&clippedDpi, spriteIndex, 34, 20, terniaryColour); // Front gfx_draw_sprite(&clippedDpi, spriteIndex + 4, 34, 20, terniaryColour); // Glass if (terniaryColour != 0) gfx_draw_sprite(&clippedDpi, ((spriteIndex + 20) & 0x7FFFF) + terniaryColour, 34, 20, terniaryColour); } } gfx_draw_string_left_clipped(dpi, STR_STATION_STYLE, gCommonFormatArgs, COLOUR_BLACK, w->x + 3, w->y + 103, 97); } } /** * * rct2: 0x006B0192 */ static void window_ride_colour_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { auto ride = get_ride(w->number); 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); // ? int32_t x = (vehiclePreviewWidget->right - vehiclePreviewWidget->left) / 2; int32_t y = vehiclePreviewWidget->bottom - vehiclePreviewWidget->top - 15; // ? auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_DIFFERENT_PER_CAR ? w->vehicleIndex : rideEntry->tab_vehicle; rct_ride_entry_vehicle* rideVehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position( ride->subtype, ride->num_cars_per_train, trainCarIndex)]; y += rideVehicleEntry->tab_height; // Draw the coloured spinning vehicle uint32_t spriteIndex = (rideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_11) ? w->frame_no / 4 : w->frame_no / 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->base_num_frames; spriteIndex += rideVehicleEntry->base_image_id; spriteIndex |= (vehicleColour.additional_1 << 24) | (vehicleColour.main << 19); spriteIndex |= IMAGE_TYPE_REMAP_2_PLUS; gfx_draw_sprite(dpi, spriteIndex, x, y, vehicleColour.additional_2); } #pragma endregion #pragma region Music static constexpr const uint8_t MusicStyleOrder[] = { MUSIC_STYLE_GENTLE, MUSIC_STYLE_SUMMER, MUSIC_STYLE_WATER, MUSIC_STYLE_RAGTIME, MUSIC_STYLE_TECHNO, MUSIC_STYLE_MECHANICAL, MUSIC_STYLE_MODERN, MUSIC_STYLE_WILD_WEST, MUSIC_STYLE_PIRATES, MUSIC_STYLE_ROCK, MUSIC_STYLE_ROCK_STYLE_2, MUSIC_STYLE_ROCK_STYLE_3, MUSIC_STYLE_FANTASY, MUSIC_STYLE_HORROR, MUSIC_STYLE_TOYLAND, MUSIC_STYLE_CANDY_STYLE, MUSIC_STYLE_ROMAN_FANFARE, MUSIC_STYLE_ORIENTAL, MUSIC_STYLE_MARTIAN, MUSIC_STYLE_SPACE, MUSIC_STYLE_JUNGLE_DRUMS, MUSIC_STYLE_JURASSIC, MUSIC_STYLE_EGYPTIAN, MUSIC_STYLE_DODGEMS_BEAT, MUSIC_STYLE_SNOW, MUSIC_STYLE_ICE, MUSIC_STYLE_MEDIEVAL, MUSIC_STYLE_URBAN, MUSIC_STYLE_ORGAN }; static uint8_t window_ride_current_music_style_order[42]; /** * * rct2: 0x006B215D */ static void window_ride_toggle_music(rct_window* w) { auto ride = get_ride(w->number); if (ride != nullptr) { int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; set_operating_setting(w->number, RideSetSetting::Music, activateMusic); } } /** * * rct2: 0x006B1ED7 */ static void window_ride_music_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_PLAY_MUSIC: window_ride_toggle_music(w); break; } } /** * * rct2: 0x006AF4A2 */ static void window_ride_music_resize(rct_window* w) { w->flags |= WF_RESIZABLE; window_set_resize(w, 316, 81, 316, 81); } /** * * rct2: 0x006B1EFC */ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { if (widgetIndex != WIDX_MUSIC_DROPDOWN) return; auto dropdownWidget = widget - 1; auto ride = get_ride(w->number); if (ride == nullptr) return; int32_t numItems = 0; if (ride->type == RIDE_TYPE_MERRY_GO_ROUND) { window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_FAIRGROUND_ORGAN; } else { for (size_t n = 0; n < std::size(MusicStyleOrder); n++) window_ride_current_music_style_order[numItems++] = MusicStyleOrder[n]; if (gRideMusicInfoList[36].length != 0) window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_1; if (gRideMusicInfoList[37].length != 0) window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_2; } for (auto i = 0; i < numItems; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = MusicStyleNames[window_ride_current_music_style_order[i]]; } window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left); for (auto i = 0; i < numItems; i++) { if (window_ride_current_music_style_order[i] == ride->music) { dropdown_set_checked(i, true); } } } /** * * rct2: 0x006B1F03 */ static void window_ride_music_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { uint8_t musicStyle; if (widgetIndex != WIDX_MUSIC_DROPDOWN || dropdownIndex == -1) return; musicStyle = window_ride_current_music_style_order[dropdownIndex]; set_operating_setting(w->number, RideSetSetting::MusicType, musicStyle); } /** * * rct2: 0x006B2198 */ static void window_ride_music_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_6); } /** * * rct2: 0x006B1DEA */ static void window_ride_music_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; ride->FormatNameTo(gCommonFormatArgs); // Set selected music window_ride_music_widgets[WIDX_MUSIC].text = MusicStyleNames[ride->music]; // Set music activated auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; if (isMusicActivated) { w->pressed_widgets |= (1 << WIDX_PLAY_MUSIC); w->disabled_widgets &= ~(1 << WIDX_MUSIC); w->disabled_widgets &= ~(1 << WIDX_MUSIC_DROPDOWN); } else { w->pressed_widgets &= ~(1 << WIDX_PLAY_MUSIC); w->disabled_widgets |= (1 << WIDX_MUSIC); w->disabled_widgets |= (1 << WIDX_MUSIC_DROPDOWN); } window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006B1ECC */ static void window_ride_music_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); } #pragma endregion #pragma region Measurements static rct_string_id get_rating_name(ride_rating rating) { int32_t index = std::clamp(rating >> 8, 0, (int32_t)std::size(RatingNames) - 1); return RatingNames[index]; } /** * * rct2: 0x006D2804 when al == 0*/ static void cancel_scenery_selection() { gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; gTrackDesignSaveMode = false; audio_unpause_sounds(); rct_window* main_w = window_get_main(); if (main_w) { main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); } gfx_invalidate_screen(); tool_cancel(); } /** * * rct2: 0x006D27A3 */ static void setup_scenery_selection(rct_window* w) { if (gTrackDesignSaveMode) { cancel_scenery_selection(); } while (tool_set(w, WIDX_BACKGROUND, TOOL_CROSSHAIR)) ; gTrackDesignSaveRideIndex = (uint8_t)w->number; track_design_save_init(); gGamePaused |= GAME_PAUSED_SAVING_TRACK; gTrackDesignSaveMode = true; audio_stop_all_music_and_sounds(); rct_window* w_main = window_get_main(); if (w_main) { w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); } gfx_invalidate_screen(); } /** * * rct2: 0x006D3026 */ static void window_ride_measurements_design_reset() { track_design_save_reset_scenery(); } /** * * rct2: 0x006D303D */ static void window_ride_measurements_design_select_nearby_scenery() { track_design_save_select_nearby_scenery(gTrackDesignSaveRideIndex); } /** * * rct2: 0x006AD4DA */ void window_ride_measurements_design_cancel() { if (gTrackDesignSaveMode) { cancel_scenery_selection(); } } 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 window_ride_measurements_design_save(rct_window* w) { Ride* ride = get_ride(w->number); _trackDesign = ride->SaveToTrackDesign(); if (!_trackDesign) { return; } if (gTrackDesignSaveMode) { auto errMessage = _trackDesign->CreateTrackDesignScenery(); 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(&TrackDesignCallback)); context_open_intent(&intent); } /** * * rct2: 0x006AD4DA */ static void window_ride_measurements_close(rct_window* w) { window_ride_measurements_design_cancel(); } /** * * rct2: 0x006AD478 */ static void window_ride_measurements_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_SELECT_NEARBY_SCENERY: window_ride_measurements_design_select_nearby_scenery(); break; case WIDX_RESET_SELECTION: window_ride_measurements_design_reset(); break; case WIDX_SAVE_DESIGN: window_ride_measurements_design_save(w); break; case WIDX_CANCEL_DESIGN: window_ride_measurements_design_cancel(); break; } } /** * * rct2: 0x006AD564 */ static void window_ride_measurements_resize(rct_window* w) { window_set_resize(w, 316, 234, 316, 234); } /** * * rct2: 0x006AD4AB */ static void window_ride_measurements_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; auto ride = get_ride(w->number); if (ride == nullptr) return; gDropdownItemsFormat[0] = STR_SAVE_TRACK_DESIGN_ITEM; gDropdownItemsFormat[1] = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; window_dropdown_show_text( w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], DROPDOWN_FLAG_STAY_OPEN, 2); gDropdownDefaultIndex = 0; if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { dropdown_set_disabled(1, true); } else if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) { // Disable saving without scenery if we're a flat ride dropdown_set_disabled(0, true); gDropdownDefaultIndex = 1; } } /** * * rct2: 0x006AD4B2 */ static void window_ride_measurements_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; if (dropdownIndex == -1) dropdownIndex = gDropdownHighlightedIndex; if (dropdownIndex == 0) { window_ride_measurements_design_save(w); } else setup_scenery_selection(w); } /** * * rct2: 0x006AD5DD */ static void window_ride_measurements_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_7); } /** * * rct2: 0x006D2AE7 */ static void window_ride_measurements_tooldown(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) { TileElement* tileElement; int16_t mapX, mapY; int32_t interactionType; _lastSceneryX = x; _lastSceneryY = y; _collectTrackDesignScenery = true; // Default to true in case user does not select anything valid get_map_coordinates_from_pos(x, y, 0xFCCF, &mapX, &mapY, &interactionType, &tileElement, nullptr); switch (interactionType) { case VIEWPORT_INTERACTION_ITEM_SCENERY: case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: _collectTrackDesignScenery = !track_design_save_contains_tile_element(tileElement); track_design_save_select_tile_element(interactionType, { mapX, mapY }, tileElement, _collectTrackDesignScenery); break; } } static void window_ride_measurements_tooldrag(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) { if (x == _lastSceneryX && y == _lastSceneryY) return; _lastSceneryX = x; _lastSceneryY = y; TileElement* tileElement; int16_t mapX, mapY; int32_t interactionType; get_map_coordinates_from_pos(x, y, 0xFCCF, &mapX, &mapY, &interactionType, &tileElement, nullptr); switch (interactionType) { case VIEWPORT_INTERACTION_ITEM_SCENERY: case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: track_design_save_select_tile_element(interactionType, { mapX, mapY }, tileElement, _collectTrackDesignScenery); break; } } /** * * rct2: 0x006AD4DA */ static void window_ride_measurements_toolabort(rct_window* w, rct_widgetindex widgetIndex) { window_ride_measurements_design_cancel(); } /** * * rct2: 0x006ACDBC */ static void window_ride_measurements_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; ride->FormatNameTo(gCommonFormatArgs); window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WWT_EMPTY; if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == w->number) { window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WWT_BUTTON; window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WWT_BUTTON; window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WWT_BUTTON; window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WWT_BUTTON; } else { window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WWT_EMPTY; window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WWT_EMPTY; window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WWT_EMPTY; window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WWT_EMPTY; window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WWT_FLATBTN; w->disabled_widgets |= (1 << WIDX_SAVE_TRACK_DESIGN); if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) { if (ride->excitement != RIDE_RATING_UNDEFINED) { w->disabled_widgets &= ~(1 << WIDX_SAVE_TRACK_DESIGN); window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; } } } window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006ACF07 */ static void window_ride_measurements_paint(rct_window* w, rct_drawpixelinfo* dpi) { int16_t holes, maxSpeed, averageSpeed, drops, highestDropHeight, inversions, time; int32_t maxPositiveVerticalGs, maxNegativeVerticalGs, maxLateralGs, totalAirTime, length; window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WWT_BUTTON) { rct_widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND]; int32_t x = w->x + (widget->right - widget->left) / 2; int32_t y = w->y + widget->top + 40; gfx_draw_string_centred_wrapped(dpi, nullptr, x, y, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, COLOUR_BLACK); x = w->x + 4; y = w->y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; gfx_fill_rect_inset(dpi, x, y, w->x + 312, y + 1, w->colours[1], INSET_RECT_FLAG_BORDER_INSET); } else { auto ride = get_ride(w->number); if (ride == nullptr) return; int32_t x = w->x + window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4; int32_t y = w->y + window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4; if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) { // Excitement rct_string_id ratingName = get_rating_name(ride->excitement); set_format_arg(0, uint32_t, ride->excitement); set_format_arg(4, rct_string_id, ratingName); rct_string_id stringId = ride->excitement == RIDE_RATING_UNDEFINED ? STR_EXCITEMENT_RATING_NOT_YET_AVAILABLE : STR_EXCITEMENT_RATING; gfx_draw_string_left(dpi, stringId, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Intensity ratingName = get_rating_name(ride->intensity); set_format_arg(0, uint32_t, ride->intensity); set_format_arg(4, rct_string_id, ratingName); stringId = STR_INTENSITY_RATING; if (ride->excitement == RIDE_RATING_UNDEFINED) stringId = STR_INTENSITY_RATING_NOT_YET_AVAILABLE; else if (ride->intensity >= RIDE_RATING(10, 00)) stringId = STR_INTENSITY_RATING_RED; gfx_draw_string_left(dpi, stringId, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Nausea ratingName = get_rating_name(ride->nausea); set_format_arg(0, uint32_t, ride->nausea); set_format_arg(4, rct_string_id, ratingName); stringId = ride->excitement == RIDE_RATING_UNDEFINED ? STR_NAUSEA_RATING_NOT_YET_AVAILABLE : STR_NAUSEA_RATING; gfx_draw_string_left(dpi, stringId, gCommonFormatArgs, COLOUR_BLACK, x, y); y += 2 * LIST_ROW_HEIGHT; // Horizontal rule gfx_fill_rect_inset(dpi, x, y - 6, x + 303, y - 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 holes = ride->holes; gfx_draw_string_left(dpi, STR_HOLES, &holes, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } else { // Max speed maxSpeed = (ride->max_speed * 9) >> 18; gfx_draw_string_left(dpi, STR_MAX_SPEED, &maxSpeed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Average speed averageSpeed = (ride->average_speed * 9) >> 18; gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &averageSpeed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Ride time int32_t numTimes = 0; for (int32_t i = 0; i < ride->num_stations; i++) { time = ride->stations[numTimes].SegmentTime; if (time != 0) { set_format_arg(0 + (numTimes * 4), uint16_t, STR_RIDE_TIME_ENTRY_WITH_SEPARATOR); set_format_arg(2 + (numTimes * 4), uint16_t, time); numTimes++; } } if (numTimes == 0) { set_format_arg(0, rct_string_id, STR_RIDE_TIME_ENTRY); set_format_arg(2, 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 set_format_arg(0 + ((numTimes - 1) * 4), uint16_t, STR_RIDE_TIME_ENTRY); } set_format_arg(0 + (numTimes * 4), uint16_t, 0); set_format_arg(2 + (numTimes * 4), uint16_t, 0); set_format_arg(4 + (numTimes * 4), uint16_t, 0); set_format_arg(6 + (numTimes * 4), uint16_t, 0); gfx_draw_string_left_clipped(dpi, STR_RIDE_TIME, gCommonFormatArgs, COLOUR_BLACK, x, y, 308); y += LIST_ROW_HEIGHT; } // Ride length int32_t numLengths = 0; for (int32_t i = 0; i < ride->num_stations; i++) { length = ride->stations[numLengths].SegmentLength; if (length != 0) { length >>= 16; set_format_arg(0 + (numLengths * 4), uint16_t, STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR); set_format_arg(2 + (numLengths * 4), uint16_t, (length & 0xFFFF)); numLengths++; } } if (numLengths == 0) { set_format_arg(0, rct_string_id, STR_RIDE_LENGTH_ENTRY); set_format_arg(2, 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 set_format_arg(0 + ((numLengths - 1) * 4), rct_string_id, STR_RIDE_LENGTH_ENTRY); } set_format_arg(0 + (numLengths * 4), uint16_t, 0); set_format_arg(2 + (numLengths * 4), uint16_t, 0); set_format_arg(4 + (numLengths * 4), uint16_t, 0); set_format_arg(6 + (numLengths * 4), uint16_t, 0); gfx_draw_string_left_clipped(dpi, STR_RIDE_LENGTH, gCommonFormatArgs, COLOUR_BLACK, x, y, 308); y += LIST_ROW_HEIGHT; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) { // Max. positive vertical G's maxPositiveVerticalGs = ride->max_positive_vertical_g; stringId = maxPositiveVerticalGs >= RIDE_G_FORCES_RED_POS_VERTICAL ? STR_MAX_POSITIVE_VERTICAL_G_RED : STR_MAX_POSITIVE_VERTICAL_G; gfx_draw_string_left(dpi, stringId, &maxPositiveVerticalGs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Max. negative vertical G's maxNegativeVerticalGs = ride->max_negative_vertical_g; stringId = maxNegativeVerticalGs <= RIDE_G_FORCES_RED_NEG_VERTICAL ? STR_MAX_NEGATIVE_VERTICAL_G_RED : STR_MAX_NEGATIVE_VERTICAL_G; gfx_draw_string_left(dpi, stringId, &maxNegativeVerticalGs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Max lateral G's maxLateralGs = ride->max_lateral_g; stringId = maxLateralGs >= RIDE_G_FORCES_RED_LATERAL ? STR_MAX_LATERAL_G_RED : STR_MAX_LATERAL_G; gfx_draw_string_left(dpi, stringId, &maxLateralGs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Total 'air' time totalAirTime = ride->total_air_time * 3; gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &totalAirTime, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_DROPS)) { // Drops drops = ride->drops & 0x3F; gfx_draw_string_left(dpi, STR_DROPS, &drops, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Highest drop height highestDropHeight = (ride->highest_drop_height * 3) / 4; gfx_draw_string_left(dpi, STR_HIGHEST_DROP_HEIGHT, &highestDropHeight, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } if (ride->type != RIDE_TYPE_MINI_GOLF) { // Inversions inversions = ride->inversions; if (inversions != 0) { gfx_draw_string_left(dpi, STR_INVERSIONS, &inversions, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } } } } else { gfx_draw_string_left(dpi, STR_NO_TEST_RESULTS_YET, nullptr, COLOUR_BLACK, x, y); } } } #pragma endregion #pragma region Graphs enum { GRAPH_VELOCITY, GRAPH_ALTITUDE, GRAPH_VERTICAL, GRAPH_LATERAL }; /** * * rct2: 0x006AE8A6 */ static void window_ride_set_graph(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 window_ride_graphs_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; } } /** * * rct2: 0x006AE8DA */ static void window_ride_graphs_resize(rct_window* w) { window_set_resize(w, 316, 180, 500, 450); } /** * * rct2: 0x006AE878 */ static void window_ride_graphs_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { switch (widgetIndex) { case WIDX_GRAPH_VELOCITY: window_ride_set_graph(w, GRAPH_VELOCITY); break; case WIDX_GRAPH_ALTITUDE: window_ride_set_graph(w, GRAPH_ALTITUDE); break; case WIDX_GRAPH_VERTICAL: window_ride_set_graph(w, GRAPH_VERTICAL); break; case WIDX_GRAPH_LATERAL: window_ride_set_graph(w, GRAPH_LATERAL); break; } } /** * * rct2: 0x006AE95D */ static void window_ride_graphs_update(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->number); if (ride != nullptr) { RideMeasurement* measurement{}; std::tie(measurement, std::ignore) = ride_get_measurement(ride); x = measurement == nullptr ? 0 : measurement->current_item - (((widget->right - widget->left) / 4) * 3); } } w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - ((widget->right - widget->left) - 2)); widget_scroll_update_thumbs(w, WIDX_GRAPH); } /** * * rct2: 0x006AEA75 */ static void window_ride_graphs_scrollgetheight(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].right - window_ride_graphs_widgets[WIDX_GRAPH].left - 2; // Get measurement size auto ride = get_ride(w->number); if (ride != nullptr) { RideMeasurement* measurement{}; std::tie(measurement, std::ignore) = ride_get_measurement(ride); if (measurement != nullptr) { *width = std::max(*width, measurement->num_items); } } } /** * * rct2: 0x006AE953 */ static void window_ride_graphs_15(rct_window* w, int32_t scrollIndex, int32_t scrollAreaType) { w->list_information_type |= 0x8000; } /** * * rct2: 0x006AEA05 */ static void window_ride_graphs_tooltip(rct_window* w, rct_widgetindex widgetIndex, rct_string_id* stringId) { if (widgetIndex == WIDX_GRAPH) { auto ride = get_ride(w->number); if (ride != nullptr) { auto [measurement, message] = ride_get_measurement(ride); if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING)) { set_format_arg(4, uint16_t, measurement->vehicle_index + 1); set_format_arg(2, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].count); } else { *stringId = message; } } } else { *stringId = STR_NONE; } } /** * * rct2: 0x006AE372 */ static void window_ride_graphs_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; ride->FormatNameTo(gCommonFormatArgs); // Set pressed graph button type w->pressed_widgets &= ~(1 << WIDX_GRAPH_VELOCITY); w->pressed_widgets &= ~(1 << WIDX_GRAPH_ALTITUDE); w->pressed_widgets &= ~(1 << WIDX_GRAPH_VERTICAL); w->pressed_widgets &= ~(1 << WIDX_GRAPH_LATERAL); w->pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (w->list_information_type & 0xFF))); // Hide graph buttons that are not applicable if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) { window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WWT_BUTTON; window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WWT_BUTTON; } else { window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WWT_EMPTY; window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WWT_EMPTY; } // Anchor graph widget auto x = w->width - 4; auto y = w->height - 18; 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 += 11; 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; window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006AE4BC */ static void window_ride_graphs_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); } /** * * rct2: 0x006AE4C7 */ static void window_ride_graphs_scrollpaint(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 stringId = STR_NONE; RideMeasurement* measurement{}; auto ride = get_ride(w->number); if (ride != nullptr) { std::tie(measurement, stringId) = ride_get_measurement(ride); } if (measurement == nullptr) { // No measurement message int32_t x = (widget->right - widget->left) / 2; int32_t y = (widget->bottom - widget->top) / 2 - 5; int32_t width = widget->right - widget->left - 2; gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, width, stringId, COLOUR_BLACK); 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) { gfx_fill_rect(dpi, x + 0, dpi->y, x + 0, dpi->y + dpi->height - 1, lightColour); gfx_fill_rect(dpi, x + 16, dpi->y, x + 16, dpi->y + dpi->height - 1, darkColour); gfx_fill_rect(dpi, x + 32, dpi->y, x + 32, dpi->y + dpi->height - 1, darkColour); gfx_fill_rect(dpi, x + 48, dpi->y, x + 48, dpi->y + dpi->height - 1, darkColour); gfx_fill_rect(dpi, x + 64, dpi->y, x + 64, dpi->y + dpi->height - 1, 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->bottom - widget->top - 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; gfx_draw_string_left(dpi, stringID, &scaled_yUnit, COLOUR_BLACK, w->scrolls[0].h_left + 1, y - 4); } // Time marks time = 0; for (int32_t x = 0; x < dpi->x + dpi->width; x += 80) { if (x + 80 >= dpi->x) gfx_draw_string_left(dpi, STR_RIDE_STATS_TIME, &time, COLOUR_BLACK, x + 2, 1); time += 5; } // Plot int32_t x = dpi->x; int32_t top, bottom; // 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; switch (listType) { case GRAPH_VELOCITY: top = measurement->velocity[x] / 2; bottom = measurement->velocity[x + 1] / 2; break; case GRAPH_ALTITUDE: top = measurement->altitude[x]; bottom = measurement->altitude[x + 1]; break; case GRAPH_VERTICAL: top = measurement->vertical[x] + 39; bottom = measurement->vertical[x + 1] + 39; intensityThresholdPositive = (RIDE_G_FORCES_RED_POS_VERTICAL / 8) + 39; intensityThresholdNegative = (RIDE_G_FORCES_RED_NEG_VERTICAL / 8) + 39; break; case GRAPH_LATERAL: top = measurement->lateral[x] + 52; bottom = measurement->lateral[x + 1] + 52; intensityThresholdPositive = (RIDE_G_FORCES_RED_LATERAL / 8) + 52; intensityThresholdNegative = -(RIDE_G_FORCES_RED_LATERAL / 8) + 52; break; default: log_error("Wrong graph type %d", listType); top = bottom = 0; break; } // Adjust line to match graph widget position. top = widget->bottom - widget->top - top - 13; bottom = widget->bottom - widget->top - bottom - 13; if (top > bottom) { std::swap(top, bottom); } // Adjust threshold line position as well if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL) { intensityThresholdPositive = widget->bottom - widget->top - intensityThresholdPositive - 13; intensityThresholdNegative = widget->bottom - widget->top - intensityThresholdNegative - 13; } const bool previousMeasurement = x > measurement->current_item; // Draw the current line in gray. gfx_fill_rect(dpi, x, top, x, bottom, 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 (bottom >= intensityThresholdNegative) { const auto redLineTop = std::max(top, intensityThresholdNegative); const auto redLineBottom = std::max(bottom, intensityThresholdNegative); gfx_fill_rect(dpi, x, redLineTop, x, redLineBottom, redLineColour); } // Line exceeds positive threshold (at top of graph). if (top <= intensityThresholdPositive) { const auto redLineTop = std::min(top, intensityThresholdPositive); const auto redLineBottom = std::min(bottom, intensityThresholdPositive); gfx_fill_rect(dpi, x, redLineTop, x, redLineBottom, redLineColour); } } } } #pragma endregion #pragma region Income static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH]; static void update_same_price_throughout_flags(uint32_t shop_item) { uint64_t newFlags; if (shop_item_is_photo(shop_item)) { newFlags = gSamePriceThroughoutPark; newFlags ^= (1ULL << SHOP_ITEM_PHOTO) | (1ULL << SHOP_ITEM_PHOTO2) | (1ULL << SHOP_ITEM_PHOTO3) | (1ULL << SHOP_ITEM_PHOTO4); auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); GameActions::Execute(&parkSetParameter); } else { newFlags = gSamePriceThroughoutPark; newFlags ^= (1ULL << shop_item); auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); GameActions::Execute(&parkSetParameter); } } /** * * rct2: 0x006ADEFD */ static void window_ride_income_toggle_primary_price(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return; uint32_t shop_item; if (ride->type == RIDE_TYPE_TOILETS) { shop_item = SHOP_ITEM_ADMISSION; } else { auto rideEntry = get_ride_entry(ride->subtype); if (rideEntry != nullptr) { shop_item = rideEntry->shop_item; if (shop_item == 0xFFFF) return; } else { return; } } update_same_price_throughout_flags(shop_item); auto rideSetPriceAction = RideSetPriceAction(w->number, ride->price, true); GameActions::Execute(&rideSetPriceAction); } /** * * rct2: 0x006AE06E */ static void window_ride_income_toggle_secondary_price(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return; auto rideEntry = get_ride_entry(ride->subtype); if (rideEntry == nullptr) return; auto shop_item = rideEntry->shop_item_secondary; if (shop_item == SHOP_ITEM_NONE) shop_item = RidePhotoItems[ride->type]; update_same_price_throughout_flags(shop_item); auto rideSetPriceAction = RideSetPriceAction(w->number, ride->price_secondary, false); GameActions::Execute(&rideSetPriceAction); } static void window_ride_income_set_primary_price(rct_window* w, money16 price) { auto rideSetPriceAction = RideSetPriceAction(w->number, price, true); GameActions::Execute(&rideSetPriceAction); } /** * * rct2: 0x006AE1E4 */ static void window_ride_income_increase_primary_price(rct_window* w) { if (!window_ride_income_can_modify_primary_price(w)) return; auto ride = get_ride(w->number); if (ride == nullptr) return; money16 price = ride->price; if (price < MONEY(20, 00)) price++; window_ride_income_set_primary_price(w, price); } /** * * rct2: 0x006AE237 */ static void window_ride_income_decrease_primary_price(rct_window* w) { if (!window_ride_income_can_modify_primary_price(w)) return; auto ride = get_ride(w->number); if (ride == nullptr) return; money16 price = ride->price; if (price > MONEY(0, 00)) price--; window_ride_income_set_primary_price(w, price); } static money16 window_ride_income_get_secondary_price(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return 0; money16 price = ride->price_secondary; return price; } static void window_ride_income_set_secondary_price(rct_window* w, money16 price) { auto rideSetPriceAction = RideSetPriceAction((w->number & 0x00FF), price, false); GameActions::Execute(&rideSetPriceAction); } static bool window_ride_income_can_modify_primary_price(rct_window* w) { auto ride = get_ride(w->number); if (ride == nullptr) return false; auto rideEntry = ride->GetRideEntry(); return park_ride_prices_unlocked() || ride->type == RIDE_TYPE_TOILETS || (rideEntry != nullptr && rideEntry->shop_item != SHOP_ITEM_NONE); } /** * * rct2: 0x006AE269 */ static void window_ride_income_increase_secondary_price(rct_window* w) { money16 price = window_ride_income_get_secondary_price(w); if (price < MONEY(20, 00)) price++; window_ride_income_set_secondary_price(w, price); } /** * * rct2: 0x006AE28D */ static void window_ride_income_decrease_secondary_price(rct_window* w) { money16 price = window_ride_income_get_secondary_price(w); if (price > MONEY(0, 00)) price--; window_ride_income_set_secondary_price(w, price); } /** * * rct2: 0x006ADEA9 */ static void window_ride_income_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_PRIMARY_PRICE: { if (!window_ride_income_can_modify_primary_price(w)) return; auto ride = get_ride(w->number); if (ride != nullptr) { money_to_string((money32)ride->price, _moneyInputText, MONEY_STRING_MAXLENGTH, true); window_text_input_raw_open( w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, _moneyInputText, MONEY_STRING_MAXLENGTH); } break; } case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK: window_ride_income_toggle_primary_price(w); break; case WIDX_SECONDARY_PRICE: { money32 price32 = (money32)window_ride_income_get_secondary_price(w); money_to_string(price32, _moneyInputText, MONEY_STRING_MAXLENGTH, true); window_text_input_raw_open( w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, _moneyInputText, MONEY_STRING_MAXLENGTH); } break; case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK: window_ride_income_toggle_secondary_price(w); break; } } /** * * rct2: 0x006AE2F8 */ static void window_ride_income_resize(rct_window* w) { window_set_resize(w, 316, 194, 316, 194); } /** * * rct2: 0x006ADED4 */ static void window_ride_income_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { switch (widgetIndex) { case WIDX_PRIMARY_PRICE_INCREASE: window_ride_income_increase_primary_price(w); break; case WIDX_PRIMARY_PRICE_DECREASE: window_ride_income_decrease_primary_price(w); break; case WIDX_SECONDARY_PRICE_INCREASE: window_ride_income_increase_secondary_price(w); break; case WIDX_SECONDARY_PRICE_DECREASE: window_ride_income_decrease_secondary_price(w); break; } } /** * * rct2: 0x006AE2BF */ static void window_ride_income_update(rct_window* w) { w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_9); auto ride = get_ride(w->number); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; w->Invalidate(); } } static void window_ride_income_textinput(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, MONEY(0, 00), MONEY(20, 00)); money16 price16 = (money16)price; if (widgetIndex == WIDX_PRIMARY_PRICE) { window_ride_income_set_primary_price(w, price16); } else { window_ride_income_set_secondary_price(w, price16); } } /** * * rct2: 0x006ADAA3 */ static void window_ride_income_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride == nullptr) return; w->widgets[WIDX_TITLE].text = STR_ARG_14_STRINGID; ride->FormatNameTo(gCommonFormatArgs + 14); auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; // Primary item w->pressed_widgets &= ~(1 << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); w->disabled_widgets &= ~(1 << 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 == SHOP_ITEM_NONE && ride->type != RIDE_TYPE_TOILETS) { w->disabled_widgets |= (1 << 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 = WWT_EMPTY; window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_ARG_6_CURRENCY2DP; money16 ridePrimaryPrice = ride_get_price(ride); set_format_arg(6, money32, ridePrimaryPrice); if (ridePrimaryPrice == 0) window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE; uint8_t primaryItem = SHOP_ITEM_ADMISSION; if (ride->type == RIDE_TYPE_TOILETS || ((primaryItem = rideEntry->shop_item) != SHOP_ITEM_NONE)) { window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WWT_CHECKBOX; if (shop_item_has_common_price(primaryItem)) w->pressed_widgets |= (1 << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = ShopItems[primaryItem].Naming.PriceLabel; } // Get secondary item auto secondaryItem = RidePhotoItems[ride->type]; if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) { if ((secondaryItem = rideEntry->shop_item_secondary) != SHOP_ITEM_NONE) { window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = ShopItems[secondaryItem].Naming.PriceLabel; } } if (secondaryItem == SHOP_ITEM_NONE) { // Hide secondary item widgets window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WWT_EMPTY; window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WWT_EMPTY; window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WWT_EMPTY; window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WWT_EMPTY; window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WWT_EMPTY; } else { // Set same price throughout park checkbox w->pressed_widgets &= ~(1 << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); if (shop_item_has_common_price(secondaryItem)) w->pressed_widgets |= (1 << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); // Show widgets window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WWT_LABEL; window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WWT_SPINNER; window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WWT_BUTTON; window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WWT_BUTTON; window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WWT_CHECKBOX; // Set secondary item price window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_RIDE_SECONDARY_PRICE_VALUE; set_format_arg(10, money32, ride->price_secondary); if (ride->price_secondary == 0) window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; } window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } /** * * rct2: 0x006ADCE5 */ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) { rct_string_id stringId; money32 profit, costPerHour; int32_t x, y, primaryItem, secondaryItem; window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); auto ride = get_ride(w->number); if (ride == nullptr) return; auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; x = w->x + window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4; y = w->y + window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33; // Primary item profit / loss per item sold primaryItem = rideEntry->shop_item; if (primaryItem != SHOP_ITEM_NONE) { profit = ride->price; stringId = STR_PROFIT_PER_ITEM_SOLD; profit -= ShopItems[primaryItem].Cost; if (profit < 0) { profit *= -1; stringId = STR_LOSS_PER_ITEM_SOLD; } gfx_draw_string_left(dpi, stringId, &profit, COLOUR_BLACK, x, y); } y += 44; // Secondary item profit / loss per item sold secondaryItem = RidePhotoItems[ride->type]; if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) secondaryItem = rideEntry->shop_item_secondary; if (secondaryItem != SHOP_ITEM_NONE) { profit = ride->price_secondary; stringId = STR_PROFIT_PER_ITEM_SOLD; profit -= ShopItems[secondaryItem].Cost; if (profit < 0) { profit *= -1; stringId = STR_LOSS_PER_ITEM_SOLD; } gfx_draw_string_left(dpi, stringId, &profit, COLOUR_BLACK, x, y); } y += 18; // Income per hour if (ride->income_per_hour != MONEY32_UNDEFINED) { gfx_draw_string_left(dpi, STR_INCOME_PER_HOUR, &ride->income_per_hour, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Running cost per hour costPerHour = ride->upkeep_cost * 16; stringId = ride->upkeep_cost == MONEY16_UNDEFINED ? STR_RUNNING_COST_UNKNOWN : STR_RUNNING_COST_PER_HOUR; gfx_draw_string_left(dpi, stringId, &costPerHour, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Profit per hour if (ride->profit != MONEY32_UNDEFINED) { gfx_draw_string_left(dpi, STR_PROFIT_PER_HOUR, &ride->profit, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } y += 5; // Total profit gfx_draw_string_left(dpi, STR_TOTAL_PROFIT, &ride->total_profit, COLOUR_BLACK, x, y); } #pragma endregion #pragma region Customer /** * * rct2: 0x006AD986 */ static void window_ride_customer_mouseup(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: window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_SHOW_GUESTS_THOUGHTS: { auto intent = Intent(WC_GUEST_LIST); intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, GLFT_GUESTS_THINKING_ABOUT_RIDE); 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, GLFT_GUESTS_ON_RIDE); 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, GLFT_GUESTS_IN_QUEUE); intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number); context_open_intent(&intent); break; } } } /** * * rct2: 0x006ADA29 */ static void window_ride_customer_resize(rct_window* w) { w->flags |= WF_RESIZABLE; window_set_resize(w, 316, 163, 316, 163); } /** * * rct2: 0x006AD9DD */ static void window_ride_customer_update(rct_window* w) { w->var_492++; if (w->var_492 >= 24) w->var_492 = 0; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_10); auto ride = get_ride(w->number); 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 window_ride_customer_invalidate(rct_window* w) { auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; window_init_scroll_widgets(w); } window_ride_set_pressed_tab(w); auto ride = get_ride(w->number); if (ride != nullptr) { ride->FormatNameTo(gCommonFormatArgs); window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WWT_FLATBTN; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) { window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_EMPTY; window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_EMPTY; } else { window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_FLATBTN; window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_FLATBTN; } window_ride_anchor_border_widgets(w); window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); } } /** * * rct2: 0x006AD6CD */ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) { int32_t x, y; uint8_t shopItem; int16_t popularity, satisfaction, queueTime, age; int32_t customersPerHour; rct_string_id stringId; window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); auto ride = get_ride(w->number); if (ride == nullptr) return; x = w->x + window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4; y = w->y + window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4; // Customers currently on ride if (ride->IsRide()) { int16_t customersOnRide = ride->num_riders; gfx_draw_string_left(dpi, STR_CUSTOMERS_ON_RIDE, &customersOnRide, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Customers per hour customersPerHour = ride_customers_per_hour(ride); gfx_draw_string_left(dpi, STR_CUSTOMERS_PER_HOUR, &customersPerHour, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Popularity popularity = ride->popularity; if (popularity == 255) { stringId = STR_POPULARITY_UNKNOWN; } else { stringId = STR_POPULARITY_PERCENT; popularity *= 4; } gfx_draw_string_left(dpi, stringId, &popularity, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Satisfaction satisfaction = ride->satisfaction; if (satisfaction == 255) { stringId = STR_SATISFACTION_UNKNOWN; } else { stringId = STR_SATISFACTION_PERCENT; satisfaction *= 5; } gfx_draw_string_left(dpi, stringId, &satisfaction, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Queue time if (ride->IsRide()) { queueTime = ride->GetMaxQueueTime(); stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES; y += gfx_draw_string_left_wrapped(dpi, &queueTime, x, y, 308, stringId, COLOUR_BLACK); y += 5; } // Primary shop items sold shopItem = ride->GetRideEntry()->shop_item; if (shopItem != SHOP_ITEM_NONE) { set_format_arg(0, rct_string_id, ShopItems[shopItem].Naming.Plural); set_format_arg(2, uint32_t, ride->no_primary_items_sold); gfx_draw_string_left(dpi, STR_ITEMS_SOLD, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Secondary shop items sold / on-ride photos sold shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? RidePhotoItems[ride->type] : ride->GetRideEntry()->shop_item_secondary; if (shopItem != SHOP_ITEM_NONE) { set_format_arg(0, rct_string_id, ShopItems[shopItem].Naming.Plural); set_format_arg(2, uint32_t, ride->no_secondary_items_sold); gfx_draw_string_left(dpi, STR_ITEMS_SOLD, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Total customers gfx_draw_string_left(dpi, STR_TOTAL_CUSTOMERS, &ride->total_customers, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Guests favourite if (ride->IsRide()) { stringId = ride->guests_favourite == 1 ? STR_FAVOURITE_RIDE_OF_GUEST : STR_FAVOURITE_RIDE_OF_GUESTS; gfx_draw_string_left(dpi, stringId, &ride->guests_favourite, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } y += 2; // Age // If the ride has a build date that is in the future, show it as built this year. age = std::max((gDateMonthsElapsed - ride->build_date) / 8, 0); stringId = age == 0 ? STR_BUILT_THIS_YEAR : age == 1 ? STR_BUILT_LAST_YEAR : STR_BUILT_YEARS_AGO; gfx_draw_string_left(dpi, stringId, &age, COLOUR_BLACK, x, y); } #pragma endregion