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

6974 lines
247 KiB
C++
Raw Normal View History

2014-08-25 16:04:28 +02:00
/*****************************************************************************
* Copyright (c) 2014-2020 OpenRCT2 developers
2014-08-25 16:04:28 +02:00
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
2014-08-25 16:04:28 +02:00
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
2014-08-25 16:04:28 +02:00
*****************************************************************************/
2018-06-22 23:21:44 +02:00
#include "../interface/Theme.h"
#include <algorithm>
2018-03-14 15:25:34 +01:00
#include <cmath>
2018-11-21 23:16:04 +01:00
#include <iterator>
2017-11-26 21:24:57 +01:00
#include <limits>
2018-06-22 23:21:44 +02:00
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
2017-10-21 16:03:33 +02:00
#include <openrct2-ui/windows/Window.h>
2017-12-13 13:02:24 +01:00
#include <openrct2/Cheats.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/Context.h>
2017-11-30 18:17:06 +01:00
#include <openrct2/Game.h>
2017-12-12 14:52:57 +01:00
#include <openrct2/Input.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/GameAction.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/ParkSetParameterAction.h>
#include <openrct2/actions/RideSetAppearanceAction.h>
#include <openrct2/actions/RideSetColourSchemeAction.h>
#include <openrct2/actions/RideSetPriceAction.h>
#include <openrct2/actions/RideSetSettingAction.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/core/String.hpp>
2018-01-06 18:32:25 +01:00
#include <openrct2/localisation/Date.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/localisation/LocalisationService.h>
2018-01-06 18:32:25 +01:00
#include <openrct2/localisation/StringIds.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/network/network.h>
#include <openrct2/object/MusicObject.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/object/ObjectManager.h>
#include <openrct2/object/ObjectRepository.h>
#include <openrct2/object/StationObject.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/peep/Staff.h>
2018-01-09 10:40:42 +01:00
#include <openrct2/rct1/RCT1.h>
2019-04-16 20:36:00 +02:00
#include <openrct2/rct2/T6Exporter.h>
2018-01-10 00:00:09 +01:00
#include <openrct2/ride/RideData.h>
#include <openrct2/ride/ShopItem.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/ride/Station.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/ride/Track.h>
#include <openrct2/ride/TrackData.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/ride/TrackDesign.h>
2019-06-30 12:23:06 +02:00
#include <openrct2/ride/TrackDesignRepository.h>
2017-10-21 16:03:33 +02:00
#include <openrct2/sprites.h>
#include <openrct2/windows/Intent.h>
2021-02-23 10:49:41 +01:00
#include <openrct2/world/EntityList.h>
2018-03-19 23:28:40 +01:00
#include <openrct2/world/Park.h>
2021-02-23 09:30:16 +01:00
#include <openrct2/world/Sprite.h>
#include <vector>
using namespace OpenRCT2;
2020-05-05 22:26:14 +02:00
static constexpr const rct_string_id WINDOW_TITLE = STR_RIDE_WINDOW_TITLE;
static constexpr const int32_t WH = 207;
static constexpr const int32_t WW = 316;
2018-06-22 23:21:44 +02:00
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
};
2014-08-25 16:04:28 +02:00
#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
};
2019-10-24 05:38:13 +02:00
constexpr int32_t RCT1_LIGHT_OFFSET = 4;
#define MAIN_RIDE_WIDGETS \
2020-05-09 16:44:21 +02:00
WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
MakeWidget({ 0, 43}, {316, 137}, WindowWidgetType::Resize, WindowColour::Secondary), \
MakeTab ({ 3, 17}, STR_VIEW_OF_RIDE_ATTRACTION_TIP ), \
MakeTab ({ 34, 17}, STR_VEHICLE_DETAILS_AND_OPTIONS_TIP ), \
MakeTab ({ 65, 17}, STR_OPERATING_OPTIONS_TIP ), \
MakeTab ({ 96, 17}, STR_MAINTENANCE_OPTIONS_TIP ), \
MakeTab ({127, 17}, STR_COLOUR_SCHEME_OPTIONS_TIP ), \
MakeTab ({158, 17}, STR_SOUND_AND_MUSIC_OPTIONS_TIP ), \
MakeTab ({189, 17}, STR_MEASUREMENTS_AND_TEST_DATA_TIP ), \
MakeTab ({220, 17}, STR_GRAPHS_TIP ), \
MakeTab ({251, 17}, STR_INCOME_AND_COSTS_TIP ), \
MakeTab ({282, 17}, STR_CUSTOMER_INFORMATION_TIP )
// 0x009ADC34
static rct_widget window_ride_main_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({ 3, 60}, {288, 107}, WindowWidgetType::Viewport, WindowColour::Secondary, STR_VIEWPORT ),
MakeWidget({ 35, 46}, {222, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, 0xFFFFFFFF, STR_VIEW_SELECTION ),
MakeWidget({245, 47}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_VIEW_SELECTION ),
MakeWidget({ 3, 167}, {288, 11}, WindowWidgetType::LabelCentred, WindowColour::Secondary ),
MakeWidget({291, 46}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_OPEN_CLOSE_OR_TEST_RIDE),
MakeWidget({291, 70}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION, STR_CONSTRUCTION ),
MakeWidget({291, 94}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RENAME, STR_NAME_RIDE_TIP ),
MakeWidget({291, 118}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP ),
MakeWidget({291, 142}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH, STR_DEMOLISH_RIDE_TIP ),
MakeWidget({296, 48}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_CLOSE_BUTTON_0, STR_CLOSE_RIDE_TIP ),
MakeWidget({296, 62}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_TEST_BUTTON_0, STR_SIMULATE_RIDE_TIP ),
MakeWidget({296, 62}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_TEST_BUTTON_0, STR_TEST_RIDE_TIP ),
MakeWidget({296, 76}, { 14, 14}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_RCT1_OPEN_BUTTON_0, STR_OPEN_RIDE_TIP ),
MakeWidget({ 3, 180}, {305, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_6_STRINGID ),
MakeWidget({297, 180}, { 11, 12}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
{ WIDGETS_END },
};
// 0x009ADDA8
static rct_widget window_ride_vehicle_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget ({ 7, 50}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
MakeWidget ({297, 51}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
MakeWidget ({ 7, 147}, {302, 43}, WindowWidgetType::Scroll, WindowColour::Secondary, STR_EMPTY ),
MakeSpinnerWidgets({ 7, 196}, {145, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_RIDE_VEHICLE_COUNT, STR_MAX_VEHICLES_TIP ),
MakeSpinnerWidgets({164, 196}, {145, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_1_CAR_PER_TRAIN, STR_MAX_CARS_PER_TRAIN_TIP),
{ WIDGETS_END },
};
// 0x009ADEFC
static rct_widget window_ride_operating_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeSpinnerWidgets({157, 61}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_18_STRINGID ), // NB: 3 widgets
MakeSpinnerWidgets({157, 75}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_LIFT_HILL_CHAIN_SPEED_VALUE ), // NB: 3 widgets
MakeWidget ({ 7, 109}, { 80, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_WAIT_FOR, STR_WAIT_FOR_PASSENGERS_BEFORE_DEPARTING_TIP),
MakeWidget ({ 7, 124}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ),
MakeWidget ({ 7, 139}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MINIMUM_WAITING_TIME, STR_MINIMUM_LENGTH_BEFORE_DEPARTING_TIP ),
MakeSpinnerWidgets({157, 139}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_10_STRINGID ), // NB: 3 widgets
MakeWidget ({ 7, 154}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAXIMUM_WAITING_TIME, STR_MAXIMUM_LENGTH_BEFORE_DEPARTING_TIP ),
MakeSpinnerWidgets({157, 154}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_14_STRINGID ), // NB: 3 widgets
MakeWidget ({ 7, 169}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS, STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP ),
MakeWidget ({ 21, 61}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary ),
MakeWidget ({ 21, 75}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary, STR_LIFT_HILL_CHAIN_SPEED ),
MakeWidget ({ 7, 47}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_OPERATING_MODE ),
MakeWidget ({297, 48}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_OPERATING_MODE ),
MakeWidget ({ 87, 109}, {222, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
MakeWidget ({297, 110}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
MakeWidget ({ 21, 89}, {129, 12}, WindowWidgetType::Label, WindowColour::Secondary, STR_NUMBER_OF_CIRCUITS, STR_NUMBER_OF_CIRCUITS_TIP ),
MakeSpinnerWidgets({157, 89}, {152, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_NUMBER_OF_CIRCUITS_VALUE ), // NB: 3 widgets
{ WIDGETS_END },
};
// 0x009AE190
static rct_widget window_ride_maintenance_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({107, 71}, {202, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE),
MakeWidget({297, 72}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE),
MakeWidget({289, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_LOCATE_NEAREST_AVAILABLE_MECHANIC_TIP ),
MakeWidget({265, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION, STR_REFURBISH_RIDE_TIP ),
MakeWidget({241, 108}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_NO_ENTRY, STR_DEBUG_FORCE_BREAKDOWN_TIP ),
{ WIDGETS_END },
};
2014-09-10 19:13:07 +02:00
// 0x009AE2A4
static rct_widget window_ride_colour_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({ 3, 47}, { 68, 47}, WindowWidgetType::Spinner, WindowColour::Secondary ),
MakeWidget({ 74, 49}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_14_STRINGID ),
MakeWidget({301, 50}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_COLOUR_SCHEME_TO_CHANGE_TIP ),
MakeWidget({ 79, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP ),
MakeWidget({ 99, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP ),
MakeWidget({119, 74}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_SUPPORT_STRUCTURE_COLOUR_TIP ),
MakeWidget({ 74, 49}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
MakeWidget({301, 50}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH ),
MakeWidget({289, 68}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_PAINTBRUSH, STR_PAINT_INDIVIDUAL_AREA_TIP ),
MakeWidget({245, 101}, { 68, 47}, WindowWidgetType::Spinner, WindowColour::Secondary ),
MakeWidget({103, 103}, {139, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY ),
MakeWidget({230, 104}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_STYLE_OF_ENTRANCE_EXIT_STATION_TIP),
MakeWidget({ 3, 157}, { 68, 47}, WindowWidgetType::Scroll, WindowColour::Secondary, STR_EMPTY ),
MakeWidget({ 74, 157}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_ARG_6_STRINGID ),
MakeWidget({301, 158}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_COLOUR_SCHEME_TIP ),
MakeWidget({ 74, 173}, {239, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ),
MakeWidget({301, 174}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_VEHICLE_TO_MODIFY_TIP ),
MakeWidget({ 79, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_MAIN_COLOUR_TIP ),
MakeWidget({ 99, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_1_TIP ),
MakeWidget({119, 190}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_ADDITIONAL_COLOUR_2_TIP ),
{ WIDGETS_END },
2014-09-10 19:13:07 +02:00
};
// 0x009AE4C8
static rct_widget window_ride_music_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({ 7, 47}, {302, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_PLAY_MUSIC, STR_SELECT_MUSIC_TIP ),
MakeWidget({ 7, 62}, {302, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_EMPTY ),
MakeWidget({297, 63}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_MUSIC_STYLE_TIP),
{ WIDGETS_END },
};
// 0x009AE5DC
static rct_widget window_ride_measurements_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({288, 194}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_FLOPPY, STR_SAVE_TRACK_DESIGN),
MakeWidget({ 4, 127}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_SELECT_NEARBY_SCENERY ),
MakeWidget({158, 127}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RESET_SELECTION ),
MakeWidget({ 4, 177}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_DESIGN_SAVE ),
MakeWidget({158, 177}, {154, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_DESIGN_CANCEL ),
{ WIDGETS_END },
};
// 0x009AE710
static rct_widget window_ride_graphs_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({ 3, 46}, {306, 112}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_HORIZONTAL, STR_LOGGING_DATA_FROM_TIP ),
MakeWidget({ 3, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_VELOCITY, STR_SHOW_GRAPH_OF_VELOCITY_AGAINST_TIME_TIP ),
MakeWidget({ 76, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_ALTITUDE, STR_SHOW_GRAPH_OF_ALTITUDE_AGAINST_TIME_TIP ),
MakeWidget({149, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_VERT_G, STR_SHOW_GRAPH_OF_VERTICAL_ACCELERATION_AGAINST_TIME_TIP),
MakeWidget({222, 163}, { 73, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RIDE_STATS_LAT_G, STR_SHOW_GRAPH_OF_LATERAL_ACCELERATION_AGAINST_TIME_TIP ),
{ WIDGETS_END },
};
// 0x009AE844
static rct_widget window_ride_income_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget ({ 19, 50}, {126, 14}, WindowWidgetType::Label, WindowColour::Secondary ),
MakeSpinnerWidgets({147, 50}, {162, 14}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_ARG_6_CURRENCY2DP ), // NB: 3 widgets
MakeWidget ({ 5, 62}, {306, 13}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP),
MakeWidget ({ 19, 94}, {126, 14}, WindowWidgetType::Label, WindowColour::Secondary ),
MakeSpinnerWidgets({147, 94}, {162, 14}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_RIDE_SECONDARY_PRICE_VALUE ), // NB: 3 widgets
MakeWidget ({ 5, 106}, {306, 13}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_SAME_PRICE_THROUGHOUT_PARK, STR_SAME_PRICE_THROUGHOUT_PARK_TIP),
{ WIDGETS_END },
};
2014-09-04 20:59:42 +02:00
// 0x009AE9C8
static rct_widget window_ride_customer_widgets[] = {
MAIN_RIDE_WIDGETS,
MakeWidget({289, 54}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION_TIP),
MakeWidget({289, 78}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION_TIP ),
MakeWidget({289, 102}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_SHOW_GUESTS_QUEUING_FOR_THIS_RIDE_ATTRACTION, STR_SHOW_GUESTS_QUEUING_FOR_THIS_RIDE_ATTRACTION_TIP ),
{ WIDGETS_END },
2014-09-04 20:59:42 +02:00
};
2014-08-25 16:04:28 +02:00
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
2014-08-25 16:04:28 +02:00
};
#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) |
(1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE) |
(1ULL << WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE),
0,
0,
0,
0,
0,
(1ULL << WIDX_PRIMARY_PRICE_INCREASE) |
(1ULL << WIDX_PRIMARY_PRICE_DECREASE) |
(1ULL << WIDX_SECONDARY_PRICE_INCREASE) |
(1ULL << WIDX_SECONDARY_PRICE_DECREASE),
0
};
2014-08-25 16:04:28 +02:00
#pragma endregion
#pragma region Events
2014-09-21 18:43:53 +02:00
static void window_ride_init_viewport(rct_window *w);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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 OpenRCT2String window_ride_vehicle_tooltip(rct_window* const w, const rct_widgetindex widgetIndex, rct_string_id fallback);
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);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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);
2014-09-10 19:13:07 +02:00
static void window_ride_colour_update(rct_window *w);
static void window_ride_colour_tooldown(rct_window *w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
static void window_ride_colour_tooldrag(rct_window *w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
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);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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, const ScreenCoordsXY& screenCoords);
static void window_ride_measurements_tooldrag(rct_window *w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
2017-05-01 15:41:45 +02:00
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);
2017-05-01 15:41:45 +02:00
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 OpenRCT2String window_ride_graphs_tooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback);
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);
2017-05-01 15:41:45 +02:00
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);
2018-06-01 14:23:46 +02:00
static bool window_ride_income_can_modify_primary_price(rct_window* w);
2017-05-01 15:41:45 +02:00
static void window_ride_customer_mouseup(rct_window *w, rct_widgetindex widgetIndex);
static void window_ride_customer_resize(rct_window *w);
2014-09-04 20:59:42 +02:00
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);
2014-09-04 20:59:42 +02:00
static void window_ride_set_page(rct_window *w, int32_t page);
// 0x0098DFD4
static rct_window_event_list window_ride_main_events([](auto& events)
{
events.mouse_up = &window_ride_main_mouseup;
events.resize = &window_ride_main_resize;
events.mouse_down = &window_ride_main_mousedown;
events.dropdown = &window_ride_main_dropdown;
events.update = &window_ride_main_update;
events.text_input = &window_ride_main_textinput;
events.viewport_rotate = &window_ride_main_viewport_rotate;
events.invalidate = &window_ride_main_invalidate;
events.paint = &window_ride_main_paint;
});
// 0x0098E204
static rct_window_event_list window_ride_vehicle_events([](auto& events)
{
events.mouse_up = &window_ride_vehicle_mouseup;
events.resize = &window_ride_vehicle_resize;
events.mouse_down = &window_ride_vehicle_mousedown;
events.dropdown = &window_ride_vehicle_dropdown;
events.update = &window_ride_vehicle_update;
events.tooltip = &window_ride_vehicle_tooltip;
events.invalidate = &window_ride_vehicle_invalidate;
events.paint = &window_ride_vehicle_paint;
events.scroll_paint = &window_ride_vehicle_scrollpaint;
});
// 0x0098E0B4
static rct_window_event_list window_ride_operating_events([](auto& events)
{
events.mouse_up = &window_ride_operating_mouseup;
events.resize = &window_ride_operating_resize;
events.mouse_down = &window_ride_operating_mousedown;
events.dropdown = &window_ride_operating_dropdown;
events.update = &window_ride_operating_update;
events.invalidate = &window_ride_operating_invalidate;
events.paint = &window_ride_operating_paint;
});
// 0x0098E124
static rct_window_event_list window_ride_maintenance_events([](auto& events)
{
events.mouse_up = &window_ride_maintenance_mouseup;
events.resize = &window_ride_maintenance_resize;
events.mouse_down = &window_ride_maintenance_mousedown;
events.dropdown = &window_ride_maintenance_dropdown;
events.update = &window_ride_maintenance_update;
events.invalidate = &window_ride_maintenance_invalidate;
events.paint = &window_ride_maintenance_paint;
});
2014-09-10 19:13:07 +02:00
// 0x0098E044
static rct_window_event_list window_ride_colour_events([](auto& events)
{
events.close = &window_ride_colour_close;
events.mouse_up = &window_ride_colour_mouseup;
events.resize = &window_ride_colour_resize;
events.mouse_down = &window_ride_colour_mousedown;
events.dropdown = &window_ride_colour_dropdown;
events.update = &window_ride_colour_update;
events.tool_down = &window_ride_colour_tooldown;
events.tool_drag = &window_ride_colour_tooldrag;
events.invalidate = &window_ride_colour_invalidate;
events.paint = &window_ride_colour_paint;
events.scroll_paint = &window_ride_colour_scrollpaint;
});
2014-09-10 19:13:07 +02:00
// 0x0098E194
static rct_window_event_list window_ride_music_events([](auto& events)
{
events.mouse_up = &window_ride_music_mouseup;
events.resize = &window_ride_music_resize;
events.mouse_down = &window_ride_music_mousedown;
events.dropdown = &window_ride_music_dropdown;
events.update = &window_ride_music_update;
events.invalidate = &window_ride_music_invalidate;
events.paint = &window_ride_music_paint;
});
// 0x0098DE14
static rct_window_event_list window_ride_measurements_events([](auto& events)
{
events.close = &window_ride_measurements_close;
events.mouse_up = &window_ride_measurements_mouseup;
events.resize = &window_ride_measurements_resize;
events.mouse_down = &window_ride_measurements_mousedown;
events.dropdown = &window_ride_measurements_dropdown;
events.update = &window_ride_measurements_update;
events.tool_down = &window_ride_measurements_tooldown;
events.tool_drag = &window_ride_measurements_tooldrag;
events.tool_abort = &window_ride_measurements_toolabort;
events.invalidate = &window_ride_measurements_invalidate;
events.paint = &window_ride_measurements_paint;
});
// 0x0098DF64
static rct_window_event_list window_ride_graphs_events([](auto& events)
{
events.mouse_up = &window_ride_graphs_mouseup;
events.resize = &window_ride_graphs_resize;
events.mouse_down = &window_ride_graphs_mousedown;
events.update = &window_ride_graphs_update;
events.get_scroll_size = &window_ride_graphs_scrollgetheight;
events.unknown_15 = &window_ride_graphs_15;
events.tooltip = &window_ride_graphs_tooltip;
events.invalidate = &window_ride_graphs_invalidate;
events.paint = &window_ride_graphs_paint;
events.scroll_paint = &window_ride_graphs_scrollpaint;
});
// 0x0098DEF4
static rct_window_event_list window_ride_income_events([](auto& events)
{
events.mouse_up = &window_ride_income_mouseup;
events.resize = &window_ride_income_resize;
events.mouse_down = &window_ride_income_mousedown;
events.update = &window_ride_income_update;
events.text_input = &window_ride_income_textinput;
events.invalidate = &window_ride_income_invalidate;
events.paint = &window_ride_income_paint;
});
2014-09-04 20:59:42 +02:00
// 0x0098DE84
static rct_window_event_list window_ride_customer_events([](auto& events)
{
events.mouse_up = &window_ride_customer_mouseup;
events.resize = &window_ride_customer_resize;
events.update = &window_ride_customer_update;
events.invalidate = &window_ride_customer_invalidate;
events.paint = &window_ride_customer_paint;
});
2014-09-04 20:59:42 +02:00
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
2014-08-25 16:04:28 +02:00
};
#pragma endregion
static bool _collectTrackDesignScenery = false;
static int32_t _lastSceneryX = 0;
static int32_t _lastSceneryY = 0;
2019-06-30 12:23:06 +02:00
static std::unique_ptr<TrackDesign> _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_view> 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 };
2014-09-04 00:19:13 +02:00
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::MovingToEndOfStation
STR_WAITING_FOR_PASSENGERS_AT, // Vehicle::Status::WaitingForPassengers
STR_WAITING_TO_DEPART, // Vehicle::Status::WaitingToDepart
STR_DEPARTING, // Vehicle::Status::Departing
STR_TRAVELLING_AT_0, // Vehicle::Status::Travelling
STR_ARRIVING_AT, // Vehicle::Status::Arriving
STR_UNLOADING_PASSENGERS_AT, // Vehicle::Status::UnloadingPassengers
STR_TRAVELLING_AT_1, // Vehicle::Status::TravellingBoat
STR_CRASHING, // Vehicle::Status::Crashing
STR_CRASHED_0, // Vehicle::Status::Crashed
STR_TRAVELLING_AT_2, // Vehicle::Status::TravellingDodgems
STR_SWINGING, // Vehicle::Status::Swinging
STR_ROTATING_0, // Vehicle::Status::Rotating
STR_ROTATING_1, // Vehicle::Status::FerrisWheelRotating
STR_OPERATING_0, // Vehicle::Status::SimulatorOperating
STR_SHOWING_FILM, // Vehicle::Status::ShowingFilm
STR_ROTATING_2, // Vehicle::Status::SpaceRingsOperating
STR_OPERATING_1, // Vehicle::Status::TopSpinOperating
STR_OPERATING_2, // Vehicle::Status::HauntedHouseOperating
STR_DOING_CIRCUS_SHOW, // Vehicle::Status::DoingCircusShow
STR_OPERATING_3, // Vehicle::Status::CrookedHouseOperating
STR_WAITING_FOR_CABLE_LIFT, // Vehicle::Status::WaitingForCableLift
STR_TRAVELLING_AT_3, // Vehicle::Status::TravellingCableLift
STR_STOPPING_0, // Vehicle::Status::Stopping
STR_WAITING_FOR_PASSENGERS, // Vehicle::Status::WaitingForPassengers17
STR_WAITING_TO_START, // Vehicle::Status::WaitingToStart
STR_STARTING, // Vehicle::Status::Starting
STR_OPERATING, // Vehicle::Status::Operating1A
STR_STOPPING_1, // Vehicle::Status::Stopping1B
STR_UNLOADING_PASSENGERS, // Vehicle::Status::UnloadingPassengers1C
STR_STOPPED_BY_BLOCK_BRAKES, // Vehicle::Status::StoppedByBlockBrakes
};
static constexpr const rct_string_id SingleSessionVehicleStatusNames[] = {
STR_STOPPING_0, // Vehicle::Status::MovingToEndOfStation
STR_WAITING_FOR_PASSENGERS, // Vehicle::Status::WaitingForPassengers
STR_WAITING_TO_START, // Vehicle::Status::WaitingToDepart
STR_STARTING, // Vehicle::Status::Departing
STR_OPERATING, // Vehicle::Status::Travelling
STR_STOPPING_1, // Vehicle::Status::Arriving
STR_UNLOADING_PASSENGERS, // Vehicle::Status::UnloadingPassengers
};
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 },
};
// clang-format on
2018-06-22 23:21:44 +02:00
struct rct_window_graphs_y_axis
{
uint8_t interval;
int8_t unit;
int8_t unit_interval;
rct_string_id label;
};
2016-08-02 18:49:04 +02:00
/** rct2: 0x0098DD98 */
static constexpr const rct_window_graphs_y_axis window_graphs_y_axi[] = {
2018-06-22 23:21:44 +02:00
{ 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
2016-08-02 18:49:04 +02:00
};
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<RideTypeLabel> 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;
2018-06-22 23:21:44 +02:00
static rct_ride_entry* VehicleDropdownRideType = nullptr;
static bool VehicleDropdownExpanded = false;
static std::vector<VehicleTypeLabel> VehicleDropdownData;
2018-06-22 23:21:44 +02:00
static void window_ride_draw_tab_image(rct_drawpixelinfo* dpi, rct_window* w, int32_t page, int32_t spriteIndex)
2014-09-04 00:19:13 +02:00
{
rct_widgetindex widgetIndex = WIDX_TAB_1 + page;
2014-09-04 00:19:13 +02:00
2018-06-22 23:21:44 +02:00
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]);
}
2014-09-04 00:19:13 +02:00
2020-03-01 20:32:35 +01:00
gfx_draw_sprite(
dpi, spriteIndex, w->windowPos + ScreenCoordsXY{ w->widgets[widgetIndex].left, w->widgets[widgetIndex].top }, 0);
}
2014-09-04 00:19:13 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2E88
2014-09-04 00:19:13 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_draw_tab_main(rct_drawpixelinfo* dpi, rct_window* w)
2014-09-04 00:19:13 +02:00
{
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_MAIN;
2018-06-22 23:21:44 +02:00
if (!(w->disabled_widgets & (1LL << widgetIndex)))
{
2019-08-03 19:11:30 +02:00
auto ride = get_ride(w->number);
if (ride != nullptr)
2018-06-22 23:21:44 +02:00
{
2019-08-03 19:11:30 +02:00
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;
}
2020-03-01 20:32:35 +01:00
gfx_draw_sprite(
dpi, spriteIndex, w->windowPos + ScreenCoordsXY{ w->widgets[widgetIndex].left, w->widgets[widgetIndex].top },
2020-03-01 20:32:35 +01:00
0);
}
}
2014-09-04 00:19:13 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2B68
2014-09-04 00:19:13 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_draw_tab_vehicle(rct_drawpixelinfo* dpi, rct_window* w)
2014-09-04 00:19:13 +02:00
{
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_VEHICLE;
2018-06-22 23:21:44 +02:00
rct_widget* widget = &w->widgets[widgetIndex];
2018-06-22 23:21:44 +02:00
if (!(w->disabled_widgets & (1LL << widgetIndex)))
{
auto screenCoords = ScreenCoordsXY{ widget->left + 1, widget->top + 1 };
int32_t width = widget->right - screenCoords.x;
int32_t height = widget->bottom - 3 - screenCoords.y;
if (w->page == WINDOW_RIDE_PAGE_VEHICLE)
height += 4;
screenCoords += w->windowPos;
rct_drawpixelinfo clipDPI;
if (!clip_drawpixelinfo(&clipDPI, dpi, screenCoords, width, height))
2018-06-22 23:21:44 +02:00
{
return;
}
screenCoords = ScreenCoordsXY{ widget->width() / 2, widget->height() - 12 };
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto rideEntry = ride->GetRideEntry();
if (rideEntry == nullptr)
return;
2018-06-22 23:21:44 +02:00
if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF)
{
clipDPI.zoom_level = 1;
clipDPI.width *= 2;
clipDPI.height *= 2;
screenCoords.x *= 2;
screenCoords.y *= 2;
clipDPI.x *= 2;
clipDPI.y *= 2;
}
// For any suspended rides, move image higher in the vehicle tab on the rides window
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SUSPENDED))
{
screenCoords.y /= 4;
}
const 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];
auto vehicleId = ((ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_VEHICLE) ? rideEntry->tab_vehicle : 0;
vehicle_colour vehicleColour = ride_get_vehicle_colour(ride, vehicleId);
int32_t spriteIndex = 32;
if (w->page == WINDOW_RIDE_PAGE_VEHICLE)
spriteIndex += w->frame_no;
2017-07-31 12:27:04 +02:00
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, screenCoords, vehicleColour.additional_2);
}
2014-09-04 00:19:13 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2F42
2014-09-04 00:19:13 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_draw_tab_customer(rct_drawpixelinfo* dpi, rct_window* w)
2014-09-04 00:19:13 +02:00
{
rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_CUSTOMER;
2014-09-04 00:19:13 +02:00
2018-06-22 23:21:44 +02:00
if (!(w->disabled_widgets & (1LL << widgetIndex)))
{
rct_widget* widget = &w->widgets[widgetIndex];
int32_t spriteIndex = 0;
if (w->page == WINDOW_RIDE_PAGE_CUSTOMER)
2020-03-01 21:51:29 +01:00
spriteIndex = w->picked_peep_frame & ~3;
2014-09-04 00:19:13 +02:00
spriteIndex += GetPeepAnimation(PeepSpriteType::Normal).base_image;
spriteIndex += 1;
spriteIndex |= 0xA9E00000;
2015-10-20 20:16:30 +02:00
gfx_draw_sprite(dpi, spriteIndex, w->windowPos + ScreenCoordsXY{ widget->midX(), widget->bottom - 6 }, 0);
}
2014-09-04 00:19:13 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2B35
2014-09-04 00:19:13 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* w)
2014-09-04 00:19:13 +02:00
{
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);
2014-09-04 00:19:13 +02:00
}
2015-03-06 17:05:47 +01:00
/**
2018-06-22 23:21:44 +02:00
*
* rct2: 0x006AEB9F
*/
static void window_ride_disable_tabs(rct_window* w)
2015-03-06 17:05:47 +01:00
{
uint32_t disabled_tabs = 0;
2020-10-27 22:24:39 +01:00
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2015-03-06 17:05:47 +01:00
const auto& rtd = ride->GetRideTypeDescriptor();
2015-03-06 17:05:47 +01:00
if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING))
disabled_tabs |= (1 << WIDX_TAB_8); // 0x800
2015-03-06 17:05:47 +01:00
if (ride->type == RIDE_TYPE_MINI_GOLF)
disabled_tabs |= (1 << WIDX_TAB_2 | 1 << WIDX_TAB_3 | 1 << WIDX_TAB_4); // 0xE0
2015-03-06 17:05:47 +01:00
if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
disabled_tabs |= (1 << WIDX_TAB_2); // 0x20
2015-03-06 17:05:47 +01:00
if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL)
&& !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS)
&& !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
2018-06-22 23:21:44 +02:00
{
disabled_tabs |= (1 << WIDX_TAB_5); // 0x100
}
2015-03-06 17:05:47 +01:00
if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
disabled_tabs |= (1 << WIDX_TAB_3 | 1 << WIDX_TAB_4 | 1 << WIDX_TAB_7); // 0x4C0
2015-03-06 17:05:47 +01:00
if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC))
2018-06-22 23:21:44 +02:00
{
disabled_tabs |= (1 << WIDX_TAB_6); // 0x200
}
2015-03-06 17:05:47 +01:00
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
2015-03-06 17:05:47 +01:00
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
2015-03-06 17:05:47 +01:00
2018-06-22 23:21:44 +02:00
rct_ride_entry* rideEntry = get_ride_entry(ride->subtype);
2015-03-06 17:05:47 +01:00
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
2018-06-22 23:21:44 +02:00
| 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);
}
2015-03-06 17:05:47 +01:00
w->disabled_widgets = disabled_tabs;
2015-03-06 17:05:47 +01:00
}
2019-01-18 19:46:22 +01:00
static void window_ride_update_overall_view(Ride* ride)
2018-06-22 23:21:44 +02:00
{
// Calculate x, y, z bounds of the entire ride using its track elements
2017-10-31 12:57:40 +01:00
tile_element_iterator it;
2017-10-31 12:57:40 +01:00
tile_element_iterator_begin(&it);
2018-06-22 23:21:44 +02:00
int32_t minx = std::numeric_limits<int32_t>::max(), miny = std::numeric_limits<int32_t>::max(),
minz = std::numeric_limits<int32_t>::max();
int32_t maxx = std::numeric_limits<int32_t>::min(), maxy = std::numeric_limits<int32_t>::min(),
maxz = std::numeric_limits<int32_t>::min();
2018-06-22 23:21:44 +02:00
while (tile_element_iterator_next(&it))
{
if (it.element->GetType() != TILE_ELEMENT_TYPE_TRACK)
continue;
2019-01-18 19:46:22 +01:00
if (it.element->AsTrack()->GetRideIndex() != ride->id)
continue;
2020-03-13 12:03:43 +01:00
auto location = TileCoordsXY(it.x, it.y).ToCoordsXY();
int32_t baseZ = it.element->GetBaseZ();
int32_t clearZ = it.element->GetClearanceZ();
2020-03-13 12:03:43 +01:00
minx = std::min(minx, location.x);
miny = std::min(miny, location.y);
minz = std::min(minz, baseZ);
2020-03-13 12:03:43 +01:00
maxx = std::max(maxx, location.x);
maxy = std::max(maxy, location.y);
maxz = std::max(maxz, clearZ);
}
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 = static_cast<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<int32_t>(std::ceil(std::log(size / 80)) - 1, 0, 3);
}
else
{
// Small rides or stalls are zoomed in all the way.
view.zoom = 0;
}
}
2014-08-25 16:04:28 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEAB4
2014-08-25 16:04:28 +02:00
*/
2019-01-18 19:46:22 +01:00
static rct_window* window_ride_open(Ride* ride)
2014-08-25 16:04:28 +02:00
{
2018-06-22 23:21:44 +02:00
rct_window* w;
w = WindowCreateAutoPos(316, 207, window_ride_page_events[0], WC_RIDE, WF_10 | WF_RESIZABLE);
w->widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN];
w->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];
2019-01-18 19:46:22 +01:00
w->number = ride->id;
w->page = WINDOW_RIDE_PAGE_MAIN;
2017-07-28 12:31:49 +02:00
w->vehicleIndex = 0;
w->frame_no = 0;
w->list_information_type = 0;
2020-03-01 21:51:29 +01:00
w->picked_peep_frame = 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;
2019-01-18 19:46:22 +01:00
window_ride_update_overall_view(ride);
return w;
2014-08-25 16:04:28 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ACC28
2014-08-25 16:04:28 +02:00
*/
2019-01-18 19:46:22 +01:00
rct_window* window_ride_main_open(Ride* ride)
2014-08-25 16:04:28 +02:00
{
if (ride->type >= RIDE_TYPE_COUNT)
{
return nullptr;
}
2014-08-25 16:04:28 +02:00
rct_window* w = window_bring_to_front_by_number(WC_RIDE, ride->id);
2018-06-22 23:21:44 +02:00
if (w == nullptr)
{
2019-01-18 19:46:22 +01:00
w = window_ride_open(ride);
w->ride.var_482 = -1;
w->ride.view = 0;
}
else if (w->ride.view >= (1 + ride->num_vehicles + ride->num_stations))
{
w->ride.view = 0;
}
2014-08-25 16:04:28 +02:00
2018-06-22 23:21:44 +02:00
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
{
if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number)
{
tool_cancel();
}
}
2014-08-25 16:04:28 +02:00
2018-06-22 23:21:44 +02:00
if (w->page != WINDOW_RIDE_PAGE_MAIN)
{
window_ride_set_page(w, WINDOW_RIDE_PAGE_MAIN);
}
window_ride_init_viewport(w);
return w;
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ACCCE
*/
static rct_window* window_ride_open_station(Ride* ride, StationIndex stationIndex)
{
if (ride->type >= RIDE_TYPE_COUNT)
return nullptr;
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
2019-01-18 19:46:22 +01:00
return window_ride_main_open(ride);
2019-01-18 19:46:22 +01:00
auto w = window_bring_to_front_by_number(WC_RIDE, ride->id);
2018-06-22 23:21:44 +02:00
if (w == nullptr)
{
2019-01-18 19:46:22 +01:00
w = window_ride_open(ride);
w->ride.var_482 = -1;
}
2018-06-22 23:21:44 +02:00
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);
WindowInitScrollWidgets(w);
// View
2019-01-18 19:46:22 +01:00
for (int32_t i = stationIndex; i >= 0; i--)
2017-07-18 12:23:24 +02:00
{
if (ride->stations[i].Start.isNull())
2017-07-18 12:23:24 +02:00
{
stationIndex--;
2017-07-18 12:23:24 +02:00
}
}
w->ride.view = 1 + ride->num_vehicles + stationIndex;
window_ride_init_viewport(w);
return w;
}
2018-11-01 13:53:50 +01:00
rct_window* window_ride_open_track(TileElement* tileElement)
{
2019-01-18 19:46:22 +01:00
assert(tileElement != nullptr);
auto rideIndex = tileElement->GetRideIndex();
2019-01-18 19:46:22 +01:00
if (rideIndex != RIDE_ID_NULL)
2018-06-22 23:21:44 +02:00
{
2019-01-18 19:46:22 +01:00
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);
}
}
2019-01-18 19:46:22 +01:00
return nullptr;
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ACAC2
*/
2020-01-19 16:53:17 +01:00
rct_window* window_ride_open_vehicle(Vehicle* vehicle)
{
Vehicle* headVehicle = vehicle->TrainHead();
uint16_t headVehicleSpriteIndex = headVehicle->sprite_index;
auto ride = headVehicle->GetRide();
if (ride == nullptr)
return nullptr;
// Get view index
int32_t view = 1;
for (int32_t i = 0; i <= MAX_VEHICLES_PER_RIDE; i++)
2018-06-22 23:21:44 +02:00
{
if (ride->vehicles[i] == headVehicleSpriteIndex)
break;
view++;
}
2019-01-18 19:46:22 +01:00
rct_window* w = window_find_by_number(WC_RIDE, ride->id);
2018-06-22 23:21:44 +02:00
if (w != nullptr)
{
w->Invalidate();
2018-06-22 23:21:44 +02:00
if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification
&& gCurrentToolWidget.window_number == w->number)
{
tool_cancel();
}
int32_t openedPeepWindow = 0;
2018-06-22 23:21:44 +02:00
if (w->ride.view == view)
{
int32_t numPeepsLeft = vehicle->num_peeps;
2018-06-22 23:21:44 +02:00
for (int32_t i = 0; i < 32 && numPeepsLeft > 0; i++)
{
Peep* peep = GetEntity<Peep>(vehicle->peep[i]);
if (peep == nullptr)
continue;
numPeepsLeft--;
rct_window* w2 = window_find_by_number(WC_PEEP, vehicle->peep[i]);
2018-06-22 23:21:44 +02:00
if (w2 == nullptr)
{
2017-10-06 23:21:00 +02:00
auto intent = Intent(WC_PEEP);
intent.putExtra(INTENT_EXTRA_PEEP, peep);
context_open_intent(&intent);
openedPeepWindow = 1;
break;
}
}
}
2019-01-18 19:46:22 +01:00
w = openedPeepWindow ? window_find_by_number(WC_RIDE, ride->id) : window_bring_to_front_by_number(WC_RIDE, ride->id);
}
2018-06-22 23:21:44 +02:00
if (w == nullptr)
{
2019-01-18 19:46:22 +01:00
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);
WindowInitScrollWidgets(w);
w->ride.view = view;
window_ride_init_viewport(w);
w->Invalidate();
return w;
}
/**
*
* rct2: 0x006AF1D2
*/
2018-06-22 23:21:44 +02:00
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();
if (page == WINDOW_RIDE_PAGE_VEHICLE)
{
auto constructionWindow = window_find_by_class(WC_RIDE_CONSTRUCTION);
if (constructionWindow && constructionWindow->number == w->number)
{
window_close_by_class(WC_RIDE_CONSTRUCTION);
// Closing the construction window sets the tab to the first page, which we don't want here,
// as user just clicked the Vehicle page
window_ride_set_page(w, WINDOW_RIDE_PAGE_VEHICLE);
}
}
// Set listen only to viewport
listen = 0;
2018-06-22 23:21:44 +02:00
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;
2020-03-01 21:51:29 +01:00
w->picked_peep_frame = 0;
2018-06-22 23:21:44 +02:00
// There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, so
2019-01-18 11:46:18 +01:00
// I've removed it if (page == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride);
//}
w->RemoveViewport();
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);
WindowInitScrollWidgets(w);
w->Invalidate();
2017-08-15 10:07:44 +02:00
if (listen != 0 && w->viewport != nullptr)
w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON;
}
2018-06-22 23:21:44 +02:00
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);
}
2018-06-22 23:21:44 +02:00
static void window_ride_anchor_border_widgets(rct_window* w)
2014-08-25 23:06:51 +02:00
{
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;
2014-08-25 23:06:51 +02:00
}
#pragma region Main
2014-09-03 20:13:37 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF994
2014-09-03 20:13:37 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_init_viewport(rct_window* w)
2014-09-03 20:13:37 +02:00
{
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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();
2018-06-22 23:21:44 +02:00
if (ride_entry && ride_entry->tab_vehicle != 0)
{
Vehicle* vehicle = GetEntity<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)
2018-06-22 23:21:44 +02:00
{
focus.sprite.sprite_id = vehicle->next_vehicle_on_train;
}
}
2018-06-22 23:21:44 +02:00
if (focus.sprite.sprite_id != SPRITE_INDEX_NULL)
{
focus.sprite.type |= VIEWPORT_FOCUS_TYPE_SPRITE;
}
}
2018-06-22 23:21:44 +02:00
else if (eax >= ride->num_vehicles && eax < (ride->num_vehicles + ride->num_stations))
{
StationIndex stationIndex = STATION_INDEX_NULL;
int32_t count = eax - ride->num_vehicles;
2018-06-22 23:21:44 +02:00
do
{
stationIndex++;
if (!ride->stations[stationIndex].Start.isNull())
2017-07-18 12:23:24 +02:00
{
count--;
2017-07-18 12:23:24 +02:00
}
} while (count >= 0);
auto location = ride->stations[stationIndex].GetStart();
focus.coordinate.x = location.x;
focus.coordinate.y = location.y;
focus.coordinate.z = location.z;
focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE;
}
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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
2018-06-22 23:21:44 +02:00
&& 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->RemoveViewport();
2018-06-22 23:21:44 +02:00
}
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;
2018-06-22 23:21:44 +02:00
// rct2: 0x006aec9c only used here so brought it into the function
if (!w->viewport && !ride->overall_view.isNull())
2018-06-22 23:21:44 +02:00
{
rct_widget* view_widget = &w->widgets[WIDX_VIEWPORT];
2020-03-01 20:32:35 +01:00
auto screenPos = w->windowPos + ScreenCoordsXY{ view_widget->left + 1, view_widget->top + 1 };
int32_t width = view_widget->width() - 1;
int32_t height = view_widget->height() - 1;
viewport_create(
w, screenPos, width, height, 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();
}
2018-06-22 23:21:44 +02:00
if (w->viewport)
{
w->viewport->flags = viewport_flags;
w->Invalidate();
}
2014-09-03 20:13:37 +02:00
}
2014-09-02 15:05:44 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF315
2014-09-02 15:05:44 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_rename(rct_window* w)
2014-09-02 15:05:44 +02:00
{
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);
}
2014-09-02 15:05:44 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF17E
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_CLOSE:
window_close(w);
break;
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
case WIDX_CONSTRUCTION:
2019-08-09 19:29:34 +02:00
{
auto ride = get_ride(w->number);
if (ride != nullptr)
2018-06-22 23:21:44 +02:00
{
2019-08-09 19:29:34 +02:00
ride_construct(ride);
if (window_find_by_number(WC_RIDE_CONSTRUCTION, ride->id) != nullptr)
{
window_close(w);
}
2018-06-22 23:21:44 +02:00
}
break;
2019-08-09 19:29:34 +02:00
}
2018-06-22 23:21:44 +02:00
case WIDX_RENAME:
window_ride_rename(w);
break;
case WIDX_LOCATE:
w->ScrollToViewport();
2018-06-22 23:21:44 +02:00
break;
case WIDX_DEMOLISH:
context_open_detail_window(WD_DEMOLISH_RIDE, w->number);
break;
case WIDX_CLOSE_LIGHT:
case WIDX_SIMULATE_LIGHT:
2018-06-22 23:21:44 +02:00
case WIDX_TEST_LIGHT:
case WIDX_OPEN_LIGHT:
2019-08-09 19:29:34 +02:00
{
2019-02-13 00:58:45 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
{
2019-08-09 19:29:34 +02:00
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;
}
2019-02-13 00:58:45 +01:00
ride_set_status(ride, status);
}
2018-06-22 23:21:44 +02:00
break;
2019-08-09 19:29:34 +02:00
}
}
}
2014-09-03 20:13:37 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF4A2
2014-09-03 20:13:37 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_resize(rct_window* w)
2014-09-03 20:13:37 +02:00
{
2019-05-08 18:41:29 +02:00
int32_t minHeight = 180;
if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
2019-05-08 18:41:29 +02:00
{
minHeight += 20 + RCT1_LIGHT_OFFSET;
auto ride = get_ride(w->number);
if (ride != nullptr)
{
2019-05-23 19:41:17 +02:00
#ifdef __SIMULATE_IN_RIDE_WINDOW__
2019-05-08 18:41:29 +02:00
if (ride->SupportsStatus(RIDE_STATUS_SIMULATING))
{
minHeight += 14;
}
2019-05-23 19:41:17 +02:00
#endif
2019-05-08 18:41:29 +02:00
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);
2014-09-03 20:13:37 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF825
*/
2018-06-22 23:21:44 +02:00
static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget)
{
2018-06-22 23:21:44 +02:00
rct_widget* dropdownWidget = widget - 1;
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
int32_t numItems = 1;
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
2018-06-22 23:21:44 +02:00
{
numItems += ride->num_stations;
numItems += ride->num_vehicles;
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, 0, numItems, widget->right - dropdownWidget->left);
// First item
gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[0] = STR_OVERALL_VIEW;
int32_t currentItem = 1;
const auto& rtd = ride->GetRideTypeDescriptor();
// Vehicles
int32_t name = GetRideComponentName(rtd.NameConvention.vehicle).number;
2018-06-22 23:21:44 +02:00
for (int32_t i = 1; i <= ride->num_vehicles; i++)
{
gDropdownItemsFormat[currentItem] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[currentItem] = name | (currentItem << 16);
currentItem++;
}
// Stations
name = GetRideComponentName(rtd.NameConvention.station).number;
2018-06-22 23:21:44 +02:00
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::SetDisabled(i + 1, true);
2018-06-22 23:21:44 +02:00
;
}
}
// Set checked item
Dropdown::SetChecked(w->ride.view, true);
}
2019-05-23 19:41:17 +02:00
static uint8_t window_ride_get_next_default_status(const Ride* ride)
{
2018-06-22 23:21:44 +02:00
switch (ride->status)
{
2019-05-23 19:41:17 +02:00
default:
2018-06-22 23:21:44 +02:00
case RIDE_STATUS_CLOSED:
2019-05-23 19:41:17 +02:00
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)
|| (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE))
2019-05-08 18:41:29 +02:00
{
2019-05-23 19:41:17 +02:00
return RIDE_STATUS_CLOSED;
2019-05-08 18:41:29 +02:00
}
2019-05-23 19:41:17 +02:00
else if (ride->SupportsStatus(RIDE_STATUS_TESTING) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
2019-05-08 18:41:29 +02:00
{
2019-05-23 19:41:17 +02:00
return RIDE_STATUS_TESTING;
2019-05-08 18:41:29 +02:00
}
2019-05-23 19:41:17 +02:00
else
2019-05-08 18:41:29 +02:00
{
2019-05-23 19:41:17 +02:00
return RIDE_STATUS_OPEN;
2019-05-08 18:41:29 +02:00
}
2019-05-23 19:41:17 +02:00
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;
2018-06-22 23:21:44 +02:00
case RIDE_STATUS_OPEN:
2019-05-23 19:41:17 +02:00
return RIDE_STATUS_CLOSED;
}
2019-05-23 19:41:17 +02:00
}
2019-05-23 19:41:17 +02:00
struct RideStatusDropdownInfo
{
struct Ride* Ride{};
uint8_t CurrentStatus{};
uint8_t DefaultStatus{};
int32_t NumItems{};
int32_t CheckedIndex = -1;
int32_t DefaultIndex = -1;
};
2019-05-23 19:41:17 +02:00
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;
2019-05-23 19:41:17 +02:00
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);
WindowDropdownShowText(
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, info.NumItems);
Dropdown::SetChecked(info.CheckedIndex, true);
2019-05-23 19:41:17 +02:00
gDropdownDefaultIndex = info.DefaultIndex;
}
static rct_string_id get_ride_type_name_for_dropdown(uint8_t rideType)
{
switch (rideType)
{
case RIDE_TYPE_1D:
return STR_RIDE_NAME_1D;
case RIDE_TYPE_1F:
return STR_RIDE_NAME_1F;
case RIDE_TYPE_22:
return STR_RIDE_NAME_22;
case RIDE_TYPE_50:
return STR_RIDE_NAME_50;
case RIDE_TYPE_52:
return STR_RIDE_NAME_52;
case RIDE_TYPE_53:
return STR_RIDE_NAME_53;
case RIDE_TYPE_54:
return STR_RIDE_NAME_54;
case RIDE_TYPE_55:
return STR_RIDE_NAME_55;
case RIDE_TYPE_59:
return STR_RIDE_NAME_59;
default:
return GetRideTypeDescriptor(rideType).Naming.Name;
}
}
static void 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++)
{
auto name = get_ride_type_name_for_dropdown(i);
RideDropdownData.push_back({ i, name, ls.GetString(name) });
}
2018-06-22 23:21:44 +02:00
std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) {
return String::Compare(a.label_string, b.label_string, true) < 0;
2018-06-22 23:21:44 +02:00
});
RideDropdownDataLanguage = ls.GetCurrentLanguage();
}
2018-06-22 23:21:44 +02:00
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;
}
2018-06-22 23:21:44 +02:00
rct_widget* dropdownWidget = widget - 1;
WindowDropdownShowText(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], Dropdown::Flag::StayOpen, RIDE_TYPE_COUNT);
// Find the current ride type in the ordered list.
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::SetChecked(pos, true);
}
2018-06-22 23:21:44 +02:00
static void populate_vehicle_type_dropdown(Ride* ride)
{
auto& objManager = GetContext()->GetObjectManager();
rct_ride_entry* rideEntry = ride->GetRideEntry();
bool selectionShouldBeExpanded;
int32_t rideTypeIterator, rideTypeIteratorMax;
2018-06-22 23:21:44 +02:00
if (gCheatsShowVehiclesFromOtherTrackTypes
2019-11-04 14:02:30 +01:00
&& !(
ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) || ride->type == RIDE_TYPE_MAZE
2019-11-04 14:02:30 +01:00
|| 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();
2018-06-22 23:21:44 +02:00
if (VehicleDropdownExpanded == selectionShouldBeExpanded && VehicleDropdownRideType == rideEntry
&& VehicleDropdownDataLanguage == ls.GetCurrentLanguage())
return;
VehicleDropdownData.clear();
for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++)
{
if (selectionShouldBeExpanded && GetRideTypeDescriptor(rideTypeIterator).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
continue;
if (selectionShouldBeExpanded && (rideTypeIterator == RIDE_TYPE_MAZE || rideTypeIterator == RIDE_TYPE_MINI_GOLF))
continue;
auto& rideEntries = objManager.GetAllRideEntries(rideTypeIterator);
for (auto rideEntryIndex : rideEntries)
{
auto currentRideEntry = get_ride_entry(rideEntryIndex);
// Skip if vehicle type has not been invented yet
if (!ride_entry_is_invented(rideEntryIndex) && !gCheatsIgnoreResearchStatus)
continue;
VehicleDropdownData.push_back(
{ rideEntryIndex, currentRideEntry->naming.Name, ls.GetString(currentRideEntry->naming.Name) });
}
}
2018-06-22 23:21:44 +02:00
std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) {
return String::Compare(a.label_string, b.label_string, true) < 0;
2018-06-22 23:21:44 +02:00
});
VehicleDropdownExpanded = selectionShouldBeExpanded;
VehicleDropdownRideType = rideEntry;
VehicleDropdownDataLanguage = ls.GetCurrentLanguage();
}
2018-06-22 23:21:44 +02:00
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<size_t>(VehicleDropdownData.size(), Dropdown::ItemsMaxSize);
for (size_t i = 0; i < numItems; i++)
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = VehicleDropdownData[i].label_id;
}
2018-06-22 23:21:44 +02:00
rct_widget* dropdownWidget = widget - 1;
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
// Find the current vehicle type in the ordered list.
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::SetChecked(pos, true);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF1BD
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF300
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_VIEW_DROPDOWN:
if (dropdownIndex == -1)
{
2019-05-08 18:41:29 +02:00
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;
}
}
2018-06-22 23:21:44 +02:00
}
2018-06-22 23:21:44 +02:00
w->ride.view = dropdownIndex;
window_ride_init_viewport(w);
w->Invalidate();
2018-06-22 23:21:44 +02:00
break;
case WIDX_OPEN:
2019-05-08 18:41:29 +02:00
{
auto ride = get_ride(w->number);
if (ride != nullptr)
2018-06-22 23:21:44 +02:00
{
2019-05-23 19:41:17 +02:00
auto status = RIDE_STATUS_CLOSED;
if (dropdownIndex < 0)
2019-05-08 18:41:29 +02:00
{
dropdownIndex = gDropdownHighlightedIndex;
}
2020-04-19 14:08:22 +02:00
if (dropdownIndex < static_cast<int32_t>(std::size(gDropdownItemsArgs)))
2019-05-08 18:41:29 +02:00
{
2019-05-23 19:41:17 +02:00
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;
}
2019-05-08 18:41:29 +02:00
}
ride_set_status(ride, status);
2018-06-22 23:21:44 +02:00
}
break;
2019-05-08 18:41:29 +02:00
}
2018-06-22 23:21:44 +02:00
case WIDX_RIDE_TYPE_DROPDOWN:
if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT)
{
uint8_t rideLabelId = std::clamp(dropdownIndex, 0, RIDE_TYPE_COUNT - 1);
2018-06-22 23:21:44 +02:00
uint8_t rideType = RideDropdownData[rideLabelId].ride_type_id;
if (rideType < RIDE_TYPE_COUNT)
{
auto rideSetSetting = RideSetSettingAction(w->number, RideSetSetting::RideType, rideType);
rideSetSetting.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
// Reset ghost track if ride construction window is open, prevents a crash
// Will get set to the correct Alternative variable during set_default_next_piece.
// TODO: Rework construction window to prevent the need for this.
_currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES;
ride_construction_set_default_next_piece();
});
GameActions::Execute(&rideSetSetting);
2018-06-22 23:21:44 +02:00
}
}
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF40F
*/
2018-06-22 23:21:44 +02:00
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)
2018-06-22 23:21:44 +02:00
{
if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN))
2018-06-22 23:21:44 +02:00
{
if (w->ride.view == 0)
return;
if (w->ride.view <= ride->num_vehicles)
2018-06-22 23:21:44 +02:00
{
Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[w->ride.view - 1]);
if (vehicle == nullptr
|| (vehicle->status != Vehicle::Status::Travelling
&& vehicle->status != Vehicle::Status::TravellingCableLift
&& vehicle->status != Vehicle::Status::TravellingDodgems
&& vehicle->status != Vehicle::Status::TravellingBoat))
{
return;
}
}
}
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN;
}
widget_invalidate(w, WIDX_STATUS);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF2F9
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
{
2017-08-15 10:07:44 +02:00
if (widgetIndex != WIDX_RENAME || text == nullptr)
return;
2019-02-13 00:58:45 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
{
ride_set_name(ride, text, 0);
}
}
2014-09-04 00:19:13 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF55A
2014-09-04 00:19:13 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_viewport_rotate(rct_window* w)
2014-09-04 00:19:13 +02:00
{
window_ride_init_viewport(w);
2014-09-04 00:19:13 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AECF6
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_invalidate(rct_window* w)
{
2018-06-22 23:21:44 +02:00
rct_widget* widgets;
int32_t i, height;
widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(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);
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
2019-07-21 02:28:33 +02:00
uint32_t spriteIds[] = {
SPR_CLOSED,
SPR_OPEN,
SPR_TESTING,
2019-05-25 11:25:50 +02:00
SPR_G2_SIMULATE,
};
window_ride_main_widgets[WIDX_OPEN].image = spriteIds[ride->status];
2019-05-23 19:41:17 +02:00
#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
2020-11-03 22:29:22 +01:00
+ WidgetIsPressed(w, WIDX_CLOSE_LIGHT);
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0
2020-11-03 22:29:22 +01:00
+ (ride->status == RIDE_STATUS_SIMULATING) * 2 + WidgetIsPressed(w, WIDX_SIMULATE_LIGHT);
window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RIDE_STATUS_TESTING) * 2
2020-11-03 22:29:22 +01:00
+ WidgetIsPressed(w, WIDX_TEST_LIGHT);
2019-05-23 19:41:17 +02:00
#else
window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2
2020-11-03 22:29:22 +01:00
+ WidgetIsPressed(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
2020-11-03 22:29:22 +01:00
+ WidgetIsPressed(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
2020-11-03 22:29:22 +01:00
+ WidgetIsPressed(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;
2018-06-22 23:21:44 +02:00
if (!gCheatsAllowArbitraryRideTypeChanges)
{
window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu;
window_ride_main_widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name;
window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button;
}
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
2018-06-22 23:21:44 +02:00
{
window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty;
2019-05-23 19:41:17 +02:00
#ifdef __SIMULATE_IN_RIDE_WINDOW__
if (ride->SupportsStatus(RIDE_STATUS_SIMULATING))
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn;
2019-05-23 19:41:17 +02:00
#endif
window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RIDE_STATUS_TESTING) ? WindowWidgetType::ImgBtn
: WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn;
height = 62;
if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty)
{
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = height;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = height + 13;
height += 14;
}
if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty)
2018-06-22 23:21:44 +02:00
{
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;
}
2018-06-22 23:21:44 +02:00
else
{
window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn;
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty;
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty;
height = 46;
}
2018-06-22 23:21:44 +02:00
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;
}
2018-06-22 23:21:44 +02:00
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;
}
}
2014-09-03 20:13:37 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF10A
2014-09-03 20:13:37 +02:00
*/
2020-08-26 17:13:19 +02:00
static rct_string_id window_ride_get_status_overall_view(rct_window* w, Formatter& ft)
2014-09-03 20:13:37 +02:00
{
2019-02-13 00:58:45 +01:00
auto stringId = STR_NONE;
auto ride = get_ride(w->number);
if (ride != nullptr)
{
2020-08-26 17:13:19 +02:00
ride->FormatStatusTo(ft);
2019-07-21 21:44:19 +02:00
stringId = STR_BLACK_STRING;
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
2019-02-13 00:58:45 +01:00
{
2019-07-21 21:44:19 +02:00
stringId = STR_RED_OUTLINED_STRING;
2019-02-13 00:58:45 +01:00
}
}
return stringId;
2014-09-03 20:13:37 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEFEF
2014-09-03 20:13:37 +02:00
*/
2020-08-26 17:13:19 +02:00
static rct_string_id window_ride_get_status_vehicle(rct_window* w, Formatter& ft)
2014-09-03 20:13:37 +02:00
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return STR_EMPTY;
auto vehicle = GetEntity<Vehicle>(ride->vehicles[w->ride.view - 1]);
if (vehicle == nullptr)
return STR_EMPTY;
if (vehicle->status != Vehicle::Status::Crashing && vehicle->status != Vehicle::Status::Crashed)
2018-06-22 23:21:44 +02:00
{
2021-01-22 11:33:55 +01:00
auto trackType = vehicle->GetTrackType();
if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::CableLiftHill
|| trackType == TrackElemType::Up25ToFlat || trackType == TrackElemType::Up60ToFlat
|| trackType == TrackElemType::DiagUp25ToFlat || trackType == TrackElemType::DiagUp60ToFlat)
2018-06-22 23:21:44 +02:00
{
if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_BLOCK_BRAKES) && vehicle->velocity == 0)
2018-06-22 23:21:44 +02:00
{
2020-08-26 17:13:19 +02:00
ft.Add<rct_string_id>(STR_STOPPED_BY_BLOCK_BRAKES);
return STR_BLACK_STRING;
}
}
}
if (ride->type == RIDE_TYPE_MINI_GOLF)
2020-08-26 13:59:18 +02:00
return STR_EMPTY;
2020-08-26 13:59:18 +02:00
auto stringId = VehicleStatusNames[static_cast<size_t>(vehicle->status)];
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION)
&& vehicle->status <= Vehicle::Status::UnloadingPassengers)
2018-06-22 23:21:44 +02:00
{
stringId = SingleSessionVehicleStatusNames[static_cast<size_t>(vehicle->status)];
}
2020-08-26 17:13:19 +02:00
ft.Add<rct_string_id>(stringId);
2020-08-26 13:59:18 +02:00
uint16_t speedInMph = (abs(vehicle->velocity) * 9) >> 18;
2020-08-26 17:13:19 +02:00
ft.Add<uint16_t>(speedInMph);
const RideComponentName stationName = GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station);
2020-08-26 17:13:19 +02:00
ft.Add<rct_string_id>(ride->num_stations > 1 ? stationName.number : stationName.singular);
ft.Add<uint16_t>(vehicle->current_station + 1);
return stringId != STR_CRASHING && stringId != STR_CRASHED_0 ? STR_BLACK_STRING : STR_RED_OUTLINED_STRING;
2014-09-03 20:13:37 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEF65
2014-09-03 20:13:37 +02:00
*/
2020-08-26 17:13:19 +02:00
static rct_string_id window_ride_get_status_station(rct_window* w, Formatter& ft)
2014-09-03 20:13:37 +02:00
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return STR_NONE;
int32_t count = w->ride.view - ride->num_vehicles - 1;
StationIndex stationIndex = STATION_INDEX_NULL;
2020-08-26 13:59:18 +02:00
rct_string_id stringId = STR_EMPTY;
2018-06-22 23:21:44 +02:00
do
{
stationIndex++;
if (!ride->stations[stationIndex].Start.isNull())
count--;
} while (count >= 0);
// Entrance / exit
2018-06-22 23:21:44 +02:00
if (ride->status == RIDE_STATUS_CLOSED)
{
if (ride_get_entrance_location(ride, static_cast<uint8_t>(stationIndex)).isNull())
stringId = STR_NO_ENTRANCE;
else if (ride_get_exit_location(ride, static_cast<uint8_t>(stationIndex)).isNull())
stringId = STR_NO_EXIT;
2018-06-22 23:21:44 +02:00
}
else
{
if (ride_get_entrance_location(ride, static_cast<uint8_t>(stationIndex)).isNull())
stringId = STR_EXIT_ONLY;
}
// Queue length
2020-08-26 13:59:18 +02:00
if (stringId == STR_EMPTY)
2018-06-22 23:21:44 +02:00
{
stringId = STR_QUEUE_EMPTY;
2020-08-26 13:59:18 +02:00
uint16_t queueLength = ride->stations[stationIndex].QueueLength;
if (queueLength == 1)
stringId = STR_QUEUE_ONE_PERSON;
else if (queueLength > 1)
stringId = STR_QUEUE_PEOPLE;
2020-08-26 13:59:18 +02:00
2020-08-26 17:13:19 +02:00
ft.Add<rct_string_id>(stringId);
ft.Add<uint16_t>(queueLength);
2020-08-26 13:59:18 +02:00
}
else
{
2020-08-26 17:13:19 +02:00
ft.Add<rct_string_id>(stringId);
}
return STR_BLACK_STRING;
2014-09-03 20:13:37 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEE73
2014-09-03 20:13:37 +02:00
*/
2020-08-26 17:13:19 +02:00
static rct_string_id window_ride_get_status(rct_window* w, Formatter& ft)
2014-09-03 20:13:37 +02:00
{
auto ride = get_ride(w->number);
if (w->ride.view == 0)
2020-08-26 13:59:18 +02:00
return window_ride_get_status_overall_view(w, ft);
if (ride != nullptr && w->ride.view <= ride->num_vehicles)
2020-08-26 13:59:18 +02:00
return window_ride_get_status_vehicle(w, ft);
if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
2020-08-26 13:59:18 +02:00
return window_ride_get_status_overall_view(w, ft);
return window_ride_get_status_station(w, ft);
2014-09-03 20:13:37 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEE73
*/
2018-06-22 23:21:44 +02:00
static void window_ride_main_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
2018-06-22 23:21:44 +02:00
rct_widget* widget;
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
// Viewport and ear icon
2018-06-22 23:21:44 +02:00
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->windowPos + ScreenCoordsXY{ 2, 2 }, 0);
}
// View dropdown
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto ft = Formatter();
2018-06-22 23:21:44 +02:00
if (w->ride.view != 0)
{
if (w->ride.view > ride->num_vehicles)
{
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number);
ft.Add<uint16_t>(w->ride.view - ride->num_vehicles);
2018-06-22 23:21:44 +02:00
}
else
{
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number);
ft.Add<uint16_t>(w->ride.view);
}
}
else
{
ft.Add<rct_string_id>(STR_OVERALL_VIEW);
}
widget = &window_ride_main_widgets[WIDX_VIEW];
gfx_draw_string_centred(
dpi, STR_WINDOW_COLOUR_2_STRINGID,
{ w->windowPos.x + (widget->left + widget->right - 11) / 2, w->windowPos.y + widget->top }, COLOUR_BLACK, ft.Data());
// Status
ft = Formatter();
widget = &window_ride_main_widgets[WIDX_STATUS];
2020-08-26 17:13:19 +02:00
rct_string_id rideStatus = window_ride_get_status(w, ft);
DrawTextEllipsised(
dpi, w->windowPos + ScreenCoordsXY{ (widget->left + widget->right) / 2, widget->top }, widget->width(), rideStatus, ft,
COLOUR_BLACK, TextAlignment::CENTRE);
}
#pragma endregion
#pragma region Vehicle
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B272D
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2ABB
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_resize(rct_window* w)
{
window_set_resize(w, 316, 214, 316, 214);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2748
*/
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_TRAINS_DECREASE:
if (ride->num_vehicles > 1)
ride->SetNumVehicles(ride->num_vehicles - 1);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE:
if (ride->num_cars_per_train < 255)
ride->SetNumCarsPerVehicle(ride->num_cars_per_train + 1);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE:
rct_ride_entry* rideEntry = ride->GetRideEntry();
2018-06-22 23:21:44 +02:00
if (ride->num_cars_per_train > rideEntry->zero_cars + 1)
ride->SetNumCarsPerVehicle(ride->num_cars_per_train - 1);
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2767
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
if (dropdownIndex == -1)
return;
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_VEHICLE_TYPE_DROPDOWN:
if (dropdownIndex >= 0 && static_cast<std::size_t>(dropdownIndex) < VehicleDropdownData.size())
{
2019-02-13 21:16:42 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
{
auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id;
ride->SetRideEntry(newRideType);
2019-02-13 21:16:42 +01:00
}
}
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2AA1
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_update(rct_window* w)
{
w->frame_no++;
window_event_invalidate_call(w);
widget_invalidate(w, WIDX_TAB_2);
}
static OpenRCT2String window_ride_vehicle_tooltip(
rct_window* const w, const rct_widgetindex widgetIndex, rct_string_id fallback)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return { STR_NONE, {} };
switch (widgetIndex)
{
case WIDX_VEHICLE_TRAINS:
case WIDX_VEHICLE_TRAINS_DECREASE:
case WIDX_VEHICLE_TRAINS_INCREASE:
{
auto ft = Formatter();
ft.Increment(12);
RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle;
rct_string_id stringId = GetRideComponentName(vehicleType).count;
if (ride->max_trains > 1)
{
stringId = GetRideComponentName(vehicleType).count_plural;
}
ft.Add<rct_string_id>(stringId);
ft.Add<uint16_t>(ride->max_trains);
return { fallback, ft };
}
case WIDX_VEHICLE_CARS_PER_TRAIN:
case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE:
case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE:
{
auto rideEntry = ride->GetRideEntry();
if (rideEntry == nullptr)
return { STR_NONE, {} };
auto ft = Formatter();
ft.Increment(16);
ft.Add<uint16_t>(std::max(uint8_t(1), ride->GetMaxCarsPerTrain()) - rideEntry->zero_cars);
rct_string_id stringId = GetRideComponentName(RideComponentType::Car).singular;
if (ride->GetMaxCarsPerTrain() - rideEntry->zero_cars > 1)
{
stringId = GetRideComponentName(RideComponentType::Car).plural;
}
ft.Add<rct_string_id>(stringId);
return { fallback, ft };
}
}
return { fallback, {} };
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B222C
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_invalidate(rct_window* w)
{
2018-06-22 23:21:44 +02:00
rct_widget* widgets;
rct_ride_entry* rideEntry;
rct_string_id stringId;
int32_t carsPerTrain;
widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
rideEntry = ride->GetRideEntry();
2019-07-21 13:09:23 +02:00
w->widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID;
// Widget setup
carsPerTrain = ride->num_cars_per_train - rideEntry->zero_cars;
// Vehicle type
window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name;
// Trains
2018-06-22 23:21:44 +02:00
if (rideEntry->cars_per_flat_ride > 1 || gCheatsDisableTrainLengthLimit)
{
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner;
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button;
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty;
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty;
window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty;
}
// Cars per train
2018-06-22 23:21:44 +02:00
if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || gCheatsDisableTrainLengthLimit)
{
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner;
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button;
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty;
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty;
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty;
}
auto ft = Formatter::Common();
ft.Increment(6);
ft.Add<uint16_t>(carsPerTrain);
RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle;
stringId = GetRideComponentName(vehicleType).count;
2018-06-22 23:21:44 +02:00
if (ride->num_vehicles > 1)
{
stringId = GetRideComponentName(vehicleType).count_plural;
}
ft.Add<rct_string_id>(stringId);
ft.Add<uint16_t>(ride->num_vehicles);
ft.Increment(8);
ride->FormatNameTo(ft);
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B23DC
*/
2018-06-22 23:21:44 +02:00
static void window_ride_vehicle_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(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;
auto screenCoords = w->windowPos + ScreenCoordsXY{ 8, 64 };
// Description
screenCoords.y += gfx_draw_string_left_wrapped(
dpi, &rideEntry->naming.Description, screenCoords, 300, STR_BLACK_STRING, COLOUR_BLACK);
screenCoords.y += 2;
// Capacity
gfx_draw_string_left(dpi, STR_CAPACITY, &rideEntry->capacity, COLOUR_BLACK, screenCoords);
// Excitement Factor
auto factor = static_cast<int16_t>(rideEntry->excitement_multiplier);
if (factor > 0)
{
screenCoords.y += LIST_ROW_HEIGHT;
gfx_draw_string_left(dpi, STR_EXCITEMENT_FACTOR, &factor, COLOUR_BLACK, screenCoords);
}
// Intensity Factor
factor = rideEntry->intensity_multiplier;
if (factor > 0)
{
int32_t lineHeight = font_get_line_height(FONT_SPRITE_BASE_MEDIUM);
if (lineHeight != 10)
screenCoords.x += 150;
else
screenCoords.y += LIST_ROW_HEIGHT;
gfx_draw_string_left(dpi, STR_INTENSITY_FACTOR, &factor, COLOUR_BLACK, screenCoords);
if (lineHeight != 10)
screenCoords.x -= 150;
}
// Nausea Factor
factor = rideEntry->nausea_multiplier;
if (factor > 0)
{
screenCoords.y += LIST_ROW_HEIGHT;
gfx_draw_string_left(dpi, STR_NAUSEA_FACTOR, &factor, COLOUR_BLACK, screenCoords);
}
}
2018-06-22 23:21:44 +02:00
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];
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2502
*/
2018-06-22 23:21:44 +02:00
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);
2018-06-22 23:21:44 +02:00
rct_widget* widget = &window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_PREVIEW];
int32_t startX = std::max(2, (widget->width() - ((ride->num_vehicles - 1) * 36)) / 2 - 25);
int32_t startY = widget->height() - 4;
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
2018-06-22 23:21:44 +02:00
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
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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;
2017-07-31 12:27:04 +02:00
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;
}
2018-06-22 23:21:44 +02:00
if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER)
{
rct_vehicle_paintinfo tmp = *(nextSpriteToDraw - 1);
*(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2);
*(nextSpriteToDraw - 2) = tmp;
}
2018-06-22 23:21:44 +02:00
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
2014-09-09 18:17:08 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B11D5
2014-09-09 18:17:08 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_mode_tweak_increase(rct_window* w)
2014-09-09 18:17:08 +02:00
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings;
uint8_t maxValue = operatingSettings.MaxValue;
uint8_t minValue = gCheatsFastLiftHill ? 0 : operatingSettings.MinValue;
2018-06-22 23:21:44 +02:00
if (gCheatsFastLiftHill)
{
maxValue = 255;
}
2014-09-09 18:17:08 +02:00
uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1;
set_operating_setting(
w->number, RideSetSetting::Operation, std::clamp<int16_t>(ride->operation_option + increment, minValue, maxValue));
2014-09-09 18:17:08 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B120A
2014-09-09 18:17:08 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_mode_tweak_decrease(rct_window* w)
2014-09-09 18:17:08 +02:00
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings;
uint8_t maxValue = operatingSettings.MaxValue;
uint8_t minValue = gCheatsFastLiftHill ? 0 : operatingSettings.MinValue;
2018-06-22 23:21:44 +02:00
if (gCheatsFastLiftHill)
{
maxValue = 255;
}
2014-09-09 18:17:08 +02:00
uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1;
set_operating_setting(
w->number, RideSetSetting::Operation, std::clamp<int16_t>(ride->operation_option - decrement, minValue, maxValue));
2014-09-09 18:17:08 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1631
2014-09-09 18:17:08 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_mode_dropdown(rct_window* w, rct_widget* widget)
2014-09-09 18:17:08 +02:00
{
2018-06-22 23:21:44 +02:00
rct_widget* dropdownWidget;
dropdownWidget = widget - 1;
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto availableModes = ride->GetAvailableModes();
// Create dropdown list
auto numAvailableModes = 0;
auto checkedIndex = -1;
for (auto i = 0; i < static_cast<uint8_t>(RideMode::Count); i++)
2018-06-22 23:21:44 +02:00
{
if (availableModes & (1ULL << i))
{
gDropdownItemsFormat[numAvailableModes] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numAvailableModes] = RideModeNames[i];
if (ride->mode == static_cast<RideMode>(i))
checkedIndex = numAvailableModes;
numAvailableModes++;
}
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left);
if (checkedIndex != -1)
2018-06-22 23:21:44 +02:00
{
Dropdown::SetChecked(checkedIndex, true);
}
2014-09-09 18:17:08 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B15C0
2014-09-09 18:17:08 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_load_dropdown(rct_window* w, rct_widget* widget)
2014-09-09 18:17:08 +02:00
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2014-09-09 18:17:08 +02:00
auto dropdownWidget = widget - 1;
for (auto i = 0; i < 5; i++)
2018-06-22 23:21:44 +02:00
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = VehicleLoadNames[i];
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left);
2014-09-09 18:17:08 +02:00
Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true);
2014-09-09 18:17:08 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B10A7
*/
2018-06-22 23:21:44 +02:00
static void window_ride_operating_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2018-06-22 23:21:44 +02:00
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);
2018-06-22 23:21:44 +02:00
break;
case WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX:
set_operating_setting(
w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES);
2018-06-22 23:21:44 +02:00
break;
case WIDX_MINIMUM_LENGTH_CHECKBOX:
set_operating_setting(
w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH);
2018-06-22 23:21:44 +02:00
break;
case WIDX_MAXIMUM_LENGTH_CHECKBOX:
set_operating_setting(
w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH);
2018-06-22 23:21:44 +02:00
break;
case WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX:
set_operating_setting(
w->number, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS);
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1715
*/
2018-06-22 23:21:44 +02:00
static void window_ride_operating_resize(rct_window* w)
{
window_set_resize(w, 316, 186, 316, 186);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B10F4
*/
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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 : ride->GetRideTypeDescriptor().LiftData.maximum_speed;
lower_bound = gCheatsFastLiftHill ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed;
2018-06-22 23:21:44 +02:00
set_operating_setting(
w->number, RideSetSetting::LiftHillSpeed,
std::clamp<int16_t>(ride->lift_hill_speed + 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
case WIDX_LIFT_HILL_SPEED_DECREASE:
upper_bound = gCheatsFastLiftHill ? 255 : ride->GetRideTypeDescriptor().LiftData.maximum_speed;
lower_bound = gCheatsFastLiftHill ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed;
2018-06-22 23:21:44 +02:00
set_operating_setting(
w->number, RideSetSetting::LiftHillSpeed,
std::clamp<int16_t>(ride->lift_hill_speed - 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
case WIDX_MINIMUM_LENGTH_INCREASE:
upper_bound = 250;
lower_bound = 0;
set_operating_setting(
w->number, RideSetSetting::MinWaitingTime,
std::clamp<int16_t>(ride->min_waiting_time + 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
case WIDX_MINIMUM_LENGTH_DECREASE:
upper_bound = 250;
lower_bound = 0;
set_operating_setting(
w->number, RideSetSetting::MinWaitingTime,
std::clamp<int16_t>(ride->min_waiting_time - 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
case WIDX_MAXIMUM_LENGTH_INCREASE:
upper_bound = 250;
lower_bound = 0;
set_operating_setting(
w->number, RideSetSetting::MaxWaitingTime,
std::clamp<int16_t>(ride->max_waiting_time + 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
case WIDX_MAXIMUM_LENGTH_DECREASE:
upper_bound = 250;
lower_bound = 0;
set_operating_setting(
w->number, RideSetSetting::MaxWaitingTime,
std::clamp<int16_t>(ride->max_waiting_time - 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
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<int16_t>(ride->num_circuits + 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
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<int16_t>(ride->num_circuits - 1, lower_bound, upper_bound));
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1165
*/
2018-06-22 23:21:44 +02:00
static void window_ride_operating_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
if (dropdownIndex == -1)
return;
2014-09-09 18:17:08 +02:00
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2014-09-09 18:17:08 +02:00
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_MODE_DROPDOWN:
{
RideMode rideMode = RideMode::NullMode;
auto availableModes = ride->GetAvailableModes();
auto modeInDropdownIndex = -1;
for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++)
{
if (availableModes & EnumToFlag(rideModeIndex))
{
modeInDropdownIndex++;
if (modeInDropdownIndex == dropdownIndex)
{
rideMode = rideModeIndex;
break;
}
}
}
if (rideMode != RideMode::NullMode)
set_operating_setting(w->number, RideSetSetting::Mode, static_cast<uint8_t>(rideMode));
2018-06-22 23:21:44 +02:00
break;
}
2018-06-22 23:21:44 +02:00
case WIDX_LOAD_DROPDOWN:
set_operating_setting(
w->number, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex);
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B178E
*/
2018-06-22 23:21:44 +02:00
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)
2018-06-22 23:21:44 +02:00
{
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING;
w->Invalidate();
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B0B30
*/
2018-06-22 23:21:44 +02:00
static void window_ride_operating_invalidate(rct_window* w)
{
2018-06-22 23:21:44 +02:00
rct_widget* widgets;
rct_string_id format, caption, tooltip;
widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
// Widget setup
w->pressed_widgets &= ~(
2018-06-22 23:21:44 +02:00
(1ULL << WIDX_LOAD_CHECKBOX) | (1ULL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX)
| (1ULL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1ULL << WIDX_MAXIMUM_LENGTH_CHECKBOX)
| (1ULL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX));
// Sometimes, only one of the alternatives support lift hill pieces. Make sure to check both.
bool hasAlternativeType = ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE);
if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_LIFT_HILL)
|| (hasAlternativeType
&& GetRideTypeDescriptor(ride->GetRideTypeDescriptor().AlternateType).SupportsTrackPiece(TRACK_LIFT_HILL)))
{
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button;
ft.Rewind();
ft.Increment(20);
ft.Add<uint16_t>(ride->lift_hill_speed);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty;
}
// Number of circuits
if (ride->CanHaveMultipleCircuits())
2018-06-22 23:21:44 +02:00
{
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button;
ft.Rewind();
ft.Increment(22);
ft.Add<uint16_t>(ride->num_circuits);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Empty;
}
// Leave if another vehicle arrives at station
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LEAVE_WHEN_ANOTHER_VEHICLE_ARRIVES_AT_STATION)
&& ride->num_vehicles > 1 && !ride->IsBlockSectioned())
2018-06-22 23:21:44 +02:00
{
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox;
2018-06-22 23:21:44 +02:00
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip
= STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP;
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].text = ride->GetRideTypeDescriptor()
2020-04-18 12:12:07 +02:00
.NameConvention.vehicle
== RideComponentType::Boat
? STR_LEAVE_IF_ANOTHER_BOAT_ARRIVES
: STR_LEAVE_IF_ANOTHER_TRAIN_ARRIVES;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Empty;
}
// Synchronise with adjacent stations
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS))
2018-06-22 23:21:44 +02:00
{
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox;
2018-06-22 23:21:44 +02:00
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].image
= STR_SYNCHRONISE_WITH_ADJACENT_STATIONS;
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip
= STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP;
}
else
{
window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty;
}
// Mode
window_ride_operating_widgets[WIDX_MODE].text = RideModeNames[static_cast<int>(ride->mode)];
// Waiting
window_ride_operating_widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)];
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS))
2018-06-22 23:21:44 +02:00
{
window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox;
window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu;
window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button;
ft.Rewind();
ft.Increment(10);
ft.Add<rct_string_id>(STR_FORMAT_SECONDS);
ft.Add<uint16_t>(ride->min_waiting_time);
ft.Add<rct_string_id>(STR_FORMAT_SECONDS);
ft.Add<uint16_t>(ride->max_waiting_time);
if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD)
w->pressed_widgets |= (1 << WIDX_LOAD_CHECKBOX);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty;
}
if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES)
w->pressed_widgets |= (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
ft.Rewind();
ft.Increment(18);
ft.Add<uint16_t>(ride->operation_option);
2018-06-22 23:21:44 +02:00
switch (ride->mode)
{
case RideMode::PoweredLaunchPasstrough:
case RideMode::PoweredLaunch:
case RideMode::UpwardLaunch:
case RideMode::PoweredLaunchBlockSectioned:
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment(18);
ft.Add<uint16_t>((ride->launch_speed * 9) / 4);
2018-06-22 23:21:44 +02:00
format = STR_RIDE_MODE_SPEED_VALUE;
caption = STR_LAUNCH_SPEED;
tooltip = STR_LAUNCH_SPEED_TIP;
break;
case RideMode::StationToStation:
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment(18);
ft.Add<uint16_t>((ride->speed * 9) / 4);
2018-06-22 23:21:44 +02:00
format = STR_RIDE_MODE_SPEED_VALUE;
caption = STR_SPEED;
tooltip = STR_SPEED_TIP;
break;
case RideMode::Race:
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment(18);
ft.Add<uint16_t>(ride->num_laps);
2018-06-22 23:21:44 +02:00
format = STR_NUMBER_OF_LAPS_VALUE;
caption = STR_NUMBER_OF_LAPS;
tooltip = STR_NUMBER_OF_LAPS_TIP;
break;
case RideMode::Dodgems:
2018-06-22 23:21:44 +02:00
format = STR_RIDE_MODE_TIME_LIMIT_VALUE;
caption = STR_TIME_LIMIT;
tooltip = STR_TIME_LIMIT_TIP;
break;
case RideMode::Swing:
2018-06-22 23:21:44 +02:00
format = STR_RIDE_MODE_NUMBER_OF_SWINGS_VALUE;
caption = STR_NUMBER_OF_SWINGS;
tooltip = STR_NUMBER_OF_SWINGS_TIP;
break;
case RideMode::Rotation:
case RideMode::ForwardRotation:
case RideMode::BackwardRotation:
2018-06-22 23:21:44 +02:00
format = STR_NUMBER_OF_ROTATIONS_VALUE;
caption = STR_NUMBER_OF_ROTATIONS;
tooltip = STR_NUMBER_OF_ROTATIONS_TIP;
break;
default:
format = STR_MAX_PEOPLE_ON_RIDE_VALUE;
caption = STR_MAX_PEOPLE_ON_RIDE;
tooltip = STR_MAX_PEOPLE_ON_RIDE_TIP;
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES))
2018-06-22 23:21:44 +02:00
format = 0;
break;
}
2018-06-22 23:21:44 +02:00
if (format != 0)
{
if (ride->type == RIDE_TYPE_TWIST)
{
ft = Formatter::Common();
ft.Increment(18);
ft.Add<uint16_t>(ride->operation_option * 3);
}
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label;
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text = caption;
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip;
window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner;
window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format;
window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button;
window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button;
w->pressed_widgets &= ~(1 << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty;
window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty;
}
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1001
*/
2018-06-22 23:21:44 +02:00
static void window_ride_operating_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(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->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 },
w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } },
w->colours[1], INSET_RECT_FLAG_BORDER_INSET);
// Number of block sections
if (ride->IsBlockSectioned())
2018-06-22 23:21:44 +02:00
{
auto blockSections = ride->num_block_brakes + ride->num_stations;
2018-06-22 23:21:44 +02:00
gfx_draw_string_left(
dpi, STR_BLOCK_SECTIONS, &blockSections, COLOUR_BLACK,
w->windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 });
}
}
#pragma endregion
#pragma region Maintenance
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1AE4
*/
2018-06-22 23:21:44 +02:00
static void window_ride_locate_mechanic(rct_window* w)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2014-11-04 20:33:17 +01:00
// First check if there is a mechanic assigned
Peep* mechanic = ride_get_assigned_mechanic(ride);
2014-11-04 20:33:17 +01:00
// Otherwise find the closest mechanic
2017-08-15 10:07:44 +02:00
if (mechanic == nullptr)
mechanic = ride_find_closest_mechanic(ride, 1);
2014-11-04 20:33:17 +01:00
2017-08-15 10:07:44 +02:00
if (mechanic == nullptr)
context_show_error(STR_UNABLE_TO_LOCATE_MECHANIC, STR_NONE, {});
else
2017-10-06 23:21:00 +02:00
{
auto intent = Intent(WC_PEEP);
intent.putExtra(INTENT_EXTRA_PEEP, mechanic);
context_open_intent(&intent);
}
}
/**
2015-10-20 20:16:30 +02:00
*
* 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)
{
ScreenCoordsXY coords{ x, y };
gfx_fill_rect_inset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, w->colours[1], INSET_RECT_F_30);
2018-06-22 23:21:44 +02:00
if (colour & BAR_BLINK)
{
colour &= ~BAR_BLINK;
if (game_is_not_paused() && (gCurrentRealTimeTicks & 8))
return;
}
value = ((186 * ((value * 2) & 0xFF)) >> 8) & 0xFF;
2018-06-22 23:21:44 +02:00
if (value > 2)
{
gfx_fill_rect_inset(dpi, { coords + ScreenCoordsXY{ 2, 1 }, coords + ScreenCoordsXY{ value + 1, 7 } }, colour, 0);
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1AAD
*/
2018-06-22 23:21:44 +02:00
static void window_ride_maintenance_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1D70
*/
2018-06-22 23:21:44 +02:00
static void window_ride_maintenance_resize(rct_window* w)
{
window_set_resize(w, 316, 135, 316, 135);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1ACE
*/
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
rct_widget* dropdownWidget = widget;
int32_t j, num_items;
2018-06-22 23:21:44 +02:00
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];
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, 7, widget->right - dropdownWidget->left);
Dropdown::SetChecked(ride->inspection_interval, true);
2018-06-22 23:21:44 +02:00
break;
case WIDX_FORCE_BREAKDOWN:
num_items = 1;
2018-06-22 23:21:44 +02:00
for (j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++)
{
2020-10-27 22:24:39 +01:00
if (rideEntry->ride_type[j] != RIDE_TYPE_NULL)
2018-06-22 23:21:44 +02:00
break;
}
gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[0] = STR_DEBUG_FIX_RIDE;
for (int32_t i = 0; i < 8; i++)
{
2020-05-11 14:32:56 +02:00
assert(j < static_cast<int32_t>(std::size(rideEntry->ride_type)));
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
2018-06-22 23:21:44 +02:00
{
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
2018-06-22 23:21:44 +02:00
{
if (ride->num_vehicles != 1)
continue;
}
2018-06-22 23:21:44 +02:00
gDropdownItemsFormat[num_items] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[num_items] = RideBreakdownReasonNames[i];
num_items++;
}
}
2018-06-22 23:21:44 +02:00
if (num_items == 1)
{
context_show_error(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {});
2018-06-22 23:21:44 +02:00
}
else
{
WindowDropdownShowText(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top },
dropdownWidget->height() + 1, w->colours[1], Dropdown::Flag::StayOpen, num_items);
2018-06-22 23:21:44 +02:00
num_items = 1;
int32_t breakdownReason = ride->breakdown_reason_pending;
if (breakdownReason != BREAKDOWN_NONE && (ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING))
{
for (int32_t i = 0; i < 8; i++)
{
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
2018-06-22 23:21:44 +02:00
{
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
2018-06-22 23:21:44 +02:00
{
if (ride->num_vehicles != 1)
continue;
}
if (i == breakdownReason)
{
Dropdown::SetChecked(num_items, true);
2018-06-22 23:21:44 +02:00
break;
}
gDropdownItemsFormat[num_items] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[num_items] = RideBreakdownReasonNames[i];
num_items++;
}
}
}
2018-06-22 23:21:44 +02:00
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0)
{
Dropdown::SetDisabled(0, true);
2018-06-22 23:21:44 +02:00
}
}
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1AD9
*/
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_INSPECTION_INTERVAL_DROPDOWN:
set_operating_setting(w->number, RideSetSetting::InspectionInterval, dropdownIndex);
2018-06-22 23:21:44 +02:00
break;
2018-06-22 23:21:44 +02:00
case WIDX_FORCE_BREAKDOWN:
if (dropdownIndex == 0)
{
2020-01-19 16:53:17 +01:00
Vehicle* vehicle;
2018-06-22 23:21:44 +02:00
switch (ride->breakdown_reason_pending)
{
case BREAKDOWN_SAFETY_CUT_OUT:
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
break;
for (int32_t i = 0; i < ride->num_vehicles; ++i)
{
for (vehicle = GetEntity<Vehicle>(ride->vehicles[i]); vehicle != nullptr;
vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
2018-06-22 23:21:44 +02:00
{
2020-06-07 21:57:40 +02:00
vehicle->ClearUpdateFlag(
2018-06-22 23:21:44 +02:00
VEHICLE_UPDATE_FLAG_BROKEN_CAR | VEHICLE_UPDATE_FLAG_ZERO_VELOCITY
| VEHICLE_UPDATE_FLAG_BROKEN_TRAIN);
}
}
break;
case BREAKDOWN_RESTRAINTS_STUCK_CLOSED:
case BREAKDOWN_RESTRAINTS_STUCK_OPEN:
case BREAKDOWN_DOORS_STUCK_CLOSED:
case BREAKDOWN_DOORS_STUCK_OPEN:
vehicle = GetEntity<Vehicle>(ride->vehicles[ride->broken_vehicle]);
if (vehicle != nullptr)
{
vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_CAR);
}
2018-06-22 23:21:44 +02:00
break;
case BREAKDOWN_VEHICLE_MALFUNCTION:
vehicle = GetEntity<Vehicle>(ride->vehicles[ride->broken_vehicle]);
if (vehicle != nullptr)
{
vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN);
}
2018-06-22 23:21:44 +02:00
break;
}
2018-06-22 23:21:44 +02:00
ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN);
window_invalidate_by_number(WC_RIDE, w->number);
break;
}
2018-06-22 23:21:44 +02:00
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, {});
}
2018-06-22 23:21:44 +02:00
else if (ride->status == RIDE_STATUS_CLOSED)
{
context_show_error(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_IS_CLOSED, {});
2018-06-22 23:21:44 +02:00
}
else
{
int32_t j;
for (j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++)
{
if (rideEntry->ride_type[j] != RIDE_TYPE_NULL)
break;
}
2018-06-22 23:21:44 +02:00
int32_t i;
int32_t num_items = 1;
for (i = 0; i < BREAKDOWN_COUNT; i++)
{
2020-05-11 14:32:56 +02:00
assert(j < static_cast<int32_t>(std::size(rideEntry->ride_type)));
if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast<uint8_t>(1 << i))
2018-06-22 23:21:44 +02:00
{
if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned())
2018-06-22 23:21:44 +02:00
{
if (ride->num_vehicles != 1)
continue;
}
if (num_items == dropdownIndex)
break;
num_items++;
}
}
2019-02-13 00:58:45 +01:00
ride_prepare_breakdown(ride, i);
}
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1D37
*/
2018-06-22 23:21:44 +02:00
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)
2018-06-22 23:21:44 +02:00
{
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE;
w->Invalidate();
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B17C8
*/
2018-06-22 23:21:44 +02:00
static void window_ride_maintenance_invalidate(rct_window* w)
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2019-07-21 02:28:33 +02:00
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
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);
2015-06-10 02:18:04 +02:00
if (gConfigGeneral.debugging_tools && network_get_mode() == NETWORK_MODE_NONE)
{
window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn;
}
else
{
window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty;
}
2015-06-10 02:18:04 +02:00
if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1877
*/
2018-06-22 23:21:44 +02:00
static void window_ride_maintenance_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
// Locate mechanic button image
2018-06-22 23:21:44 +02:00
rct_widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC];
auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top };
gfx_draw_sprite(
dpi, (gStaffMechanicColour << 24) | IMAGE_TYPE_REMAP | IMAGE_TYPE_REMAP_2_PLUS | SPR_MECHANIC, screenCoords, 0);
// Inspection label
widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL];
screenCoords = w->windowPos + ScreenCoordsXY{ 4, widget->top + 1 };
gfx_draw_string_left(dpi, STR_INSPECTION, nullptr, COLOUR_BLACK, screenCoords);
// Reliability
widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND];
screenCoords = w->windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 };
uint16_t reliability = ride->reliability_percentage;
gfx_draw_string_left(dpi, STR_RELIABILITY_LABEL_1757, &reliability, COLOUR_BLACK, screenCoords);
window_ride_maintenance_draw_bar(
w, dpi, screenCoords.x + 103, screenCoords.y, std::max<int32_t>(10, reliability), COLOUR_BRIGHT_GREEN);
screenCoords.y += 11;
uint16_t downTime = ride->downtime;
gfx_draw_string_left(dpi, STR_DOWN_TIME_LABEL_1889, &downTime, COLOUR_BLACK, screenCoords);
window_ride_maintenance_draw_bar(w, dpi, screenCoords.x + 103, screenCoords.y, downTime, COLOUR_BRIGHT_RED);
screenCoords.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, screenCoords);
screenCoords.y += 12;
// Last / current breakdown
if (ride->breakdown_reason == BREAKDOWN_NONE)
return;
2018-06-22 23:21:44 +02:00
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, screenCoords);
screenCoords.y += 10;
// Mechanic status
2018-06-22 23:21:44 +02:00
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
{
switch (ride->mechanic_status)
{
case RIDE_MECHANIC_STATUS_CALLING:
2019-02-28 20:28:58 +01:00
{
2018-06-22 23:21:44 +02:00
stringId = STR_NO_MECHANICS_ARE_HIRED_MESSAGE;
2020-06-17 22:42:40 +02:00
for (auto peep : EntityList<Staff>(EntityListId::Peep))
2018-06-22 23:21:44 +02:00
{
if (peep->IsMechanic())
2018-06-22 23:21:44 +02:00
{
stringId = STR_CALLING_MECHANIC;
break;
}
}
2018-06-22 23:21:44 +02:00
break;
2019-02-28 20:28:58 +01:00
}
2018-06-22 23:21:44 +02:00
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;
}
2018-06-22 23:21:44 +02:00
if (stringId != STR_EMPTY)
{
if (stringId == STR_CALLING_MECHANIC || stringId == STR_NO_MECHANICS_ARE_HIRED_MESSAGE)
{
gfx_draw_string_left_wrapped(dpi, nullptr, screenCoords + ScreenCoordsXY{ 4, 0 }, 280, stringId, COLOUR_BLACK);
2018-06-22 23:21:44 +02:00
}
else
{
auto staff = GetEntity<Staff>(ride->mechanic);
if (staff != nullptr && staff->IsMechanic())
{
auto ft = Formatter();
staff->FormatNameTo(ft);
gfx_draw_string_left_wrapped(
dpi, ft.Data(), screenCoords + ScreenCoordsXY{ 4, 0 }, 280, stringId, COLOUR_BLACK);
}
}
}
}
}
#pragma endregion
2014-09-10 19:13:07 +02:00
#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;
}
2018-06-22 23:21:44 +02:00
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->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
{
auto stationObj = ride_get_station_object(ride);
if (stationObj != nullptr)
{
stationObjFlags = stationObj->Flags;
}
}
2018-06-22 23:21:44 +02:00
switch (trackColour)
{
case 0:
return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR)
|| ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN);
2018-06-22 23:21:44 +02:00
case 1:
return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR)
|| ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL);
2018-06-22 23:21:44 +02:00
case 2:
return ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS);
2018-06-22 23:21:44 +02:00
default:
return 0;
}
}
2020-05-01 17:58:10 +02:00
static void window_ride_set_track_colour_scheme(rct_window* w, const ScreenCoordsXY& screenPos)
2014-09-18 01:31:20 +02:00
{
auto newColourScheme = static_cast<uint8_t>(w->ride_colour);
auto info = get_map_coordinates_from_pos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride));
2014-09-18 01:31:20 +02:00
if (info.SpriteType != ViewportInteractionItem::Ride)
return;
if (info.Element->GetType() != TILE_ELEMENT_TYPE_TRACK)
return;
if (info.Element->AsTrack()->GetRideIndex() != w->number)
return;
if (info.Element->AsTrack()->GetColourScheme() == newColourScheme)
return;
2014-09-18 01:31:20 +02:00
auto z = info.Element->GetBaseZ();
auto direction = info.Element->GetDirection();
2019-07-07 03:53:59 +02:00
auto gameAction = RideSetColourSchemeAction(
CoordsXYZD{ info.Loc, z, static_cast<Direction>(direction) }, info.Element->AsTrack()->GetTrackType(), newColourScheme);
GameActions::Execute(&gameAction);
2014-09-18 01:31:20 +02:00
}
2014-09-10 19:13:07 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B04FA
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_close(rct_window* w)
2014-09-10 19:13:07 +02:00
{
if (!(input_test_flag(INPUT_FLAG_TOOL_ACTIVE)))
return;
2014-09-10 19:13:07 +02:00
if (gCurrentToolWidget.window_classification != w->classification)
return;
2014-09-10 19:13:07 +02:00
if (gCurrentToolWidget.window_number != w->number)
return;
2014-09-10 19:13:07 +02:00
tool_cancel();
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B02A1
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_mouseup(rct_window* w, rct_widgetindex widgetIndex)
2014-09-10 19:13:07 +02:00
{
2018-06-22 23:21:44 +02:00
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::PaintDown);
2018-06-22 23:21:44 +02:00
break;
}
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B0AB6
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_resize(rct_window* w)
2014-09-10 19:13:07 +02:00
{
window_set_resize(w, 316, 207, 316, 207);
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B02C6
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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];
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left);
Dropdown::SetChecked(colourSchemeIndex, true);
2018-06-22 23:21:44 +02:00
break;
case WIDX_TRACK_MAIN_COLOUR:
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main);
2018-06-22 23:21:44 +02:00
break;
case WIDX_TRACK_ADDITIONAL_COLOUR:
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional);
2018-06-22 23:21:44 +02:00
break;
case WIDX_TRACK_SUPPORT_COLOUR:
WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports);
2018-06-22 23:21:44 +02:00
break;
case WIDX_MAZE_STYLE_DROPDOWN:
for (i = 0; i < 4; i++)
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = MazeOptions[i].text;
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left);
Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true);
2018-06-22 23:21:44 +02:00
break;
case WIDX_ENTRANCE_STYLE_DROPDOWN:
{
auto ddIndex = 0;
auto& objManager = GetContext()->GetObjectManager();
for (i = 0; i < MAX_STATION_OBJECTS; i++)
2018-06-22 23:21:44 +02:00
{
auto stationObj = static_cast<StationObject*>(objManager.GetLoadedObject(ObjectType::Station, i));
if (stationObj != nullptr)
2018-06-22 23:21:44 +02:00
{
gDropdownItemsFormat[ddIndex] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[ddIndex] = stationObj->NameStringId;
if (ride->entrance_style == i)
{
gDropdownItemsFormat[ddIndex] = STR_DROPDOWN_MENU_LABEL_SELECTED;
}
ddIndex++;
2018-06-22 23:21:44 +02:00
}
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widget->right - dropdownWidget->left);
2018-06-22 23:21:44 +02:00
break;
}
2018-06-22 23:21:44 +02:00
case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN:
for (i = 0; i < 3; i++)
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = (GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular
2020-04-18 12:12:07 +02:00
<< 16)
| VehicleColourSchemeNames[i];
2018-06-22 23:21:44 +02:00
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, rideEntry->max_cars_in_train > 1 ? 3 : 2,
widget->right - dropdownWidget->left);
2018-06-22 23:21:44 +02:00
Dropdown::SetChecked(ride->colour_scheme_type & 3, true);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN:
numItems = ride->num_vehicles;
if ((ride->colour_scheme_type & 3) != VEHICLE_COLOUR_SCHEME_PER_TRAIN)
numItems = ride->num_cars_per_train;
stringId = (ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_TRAIN ? STR_RIDE_COLOUR_TRAIN_OPTION
: STR_RIDE_COLOUR_VEHICLE_OPTION;
for (i = 0; i < std::min(numItems, Dropdown::ItemsMaxSize); i++)
2018-06-22 23:21:44 +02:00
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = (static_cast<int64_t>(i + 1) << 32)
| ((GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised) << 16)
2020-04-18 12:12:07 +02:00
| stringId;
2018-06-22 23:21:44 +02:00
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
2018-06-22 23:21:44 +02:00
Dropdown::SetChecked(w->vehicleIndex, true);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_MAIN_COLOUR:
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.main);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_ADDITIONAL_COLOUR_1:
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.additional_1);
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_ADDITIONAL_COLOUR_2:
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.additional_2);
2018-06-22 23:21:44 +02:00
break;
}
}
2014-09-10 19:13:07 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B0331
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
if (dropdownIndex == -1)
return;
2018-06-22 23:21:44 +02:00
switch (widgetIndex)
{
case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN:
w->ride_colour = static_cast<uint16_t>(dropdownIndex);
w->Invalidate();
2018-06-22 23:21:44 +02:00
break;
case WIDX_TRACK_MAIN_COLOUR:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::TrackColourMain, dropdownIndex, w->ride_colour);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_TRACK_ADDITIONAL_COLOUR:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::TrackColourAdditional, dropdownIndex, w->ride_colour);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_TRACK_SUPPORT_COLOUR:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::TrackColourSupports, dropdownIndex, w->ride_colour);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_MAZE_STYLE_DROPDOWN:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::MazeStyle, dropdownIndex, w->ride_colour);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_ENTRANCE_STYLE_DROPDOWN:
{
auto ddIndex = 0;
auto& objManager = GetContext()->GetObjectManager();
for (auto i = 0; i < MAX_STATION_OBJECTS; i++)
{
auto stationObj = static_cast<StationObject*>(objManager.GetLoadedObject(ObjectType::Station, i));
if (stationObj != nullptr)
{
if (ddIndex == dropdownIndex)
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::EntranceStyle, ddIndex, 0);
GameActions::Execute(&rideSetAppearanceAction);
break;
}
ddIndex++;
}
}
2018-06-22 23:21:44 +02:00
break;
}
2018-06-22 23:21:44 +02:00
case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0);
GameActions::Execute(&rideSetAppearanceAction);
2018-06-22 23:21:44 +02:00
w->vehicleIndex = 0;
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN:
w->vehicleIndex = dropdownIndex;
w->Invalidate();
2018-06-22 23:21:44 +02:00
break;
case WIDX_VEHICLE_MAIN_COLOUR:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::VehicleColourBody, dropdownIndex, w->vehicleIndex);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_VEHICLE_ADDITIONAL_COLOUR_1:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::VehicleColourTrim, dropdownIndex, w->vehicleIndex);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
2018-06-22 23:21:44 +02:00
case WIDX_VEHICLE_ADDITIONAL_COLOUR_2:
{
2019-02-06 16:05:35 +01:00
auto rideSetAppearanceAction = RideSetAppearanceAction(
w->number, RideSetAppearanceType::VehicleColourTernary, dropdownIndex, w->vehicleIndex);
GameActions::Execute(&rideSetAppearanceAction);
}
break;
}
}
2014-09-10 19:13:07 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B0A8F
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_update(rct_window* w)
2014-09-10 19:13:07 +02:00
{
w->frame_no++;
window_event_invalidate_call(w);
widget_invalidate(w, WIDX_TAB_5);
widget_invalidate(w, WIDX_VEHICLE_PREVIEW);
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B04EC
2014-09-10 19:13:07 +02:00
*/
static void window_ride_colour_tooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
2015-10-20 20:16:30 +02:00
{
if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA)
2020-05-01 17:58:10 +02:00
window_ride_set_track_colour_scheme(w, screenCoords);
2014-09-18 01:31:20 +02:00
}
2014-09-10 19:13:07 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B04F3
2014-09-10 19:13:07 +02:00
*/
static void window_ride_colour_tooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
2015-10-20 20:16:30 +02:00
{
if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA)
2020-05-01 17:58:10 +02:00
window_ride_set_track_colour_scheme(w, screenCoords);
2014-09-18 01:31:20 +02:00
}
2014-09-10 19:13:07 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AFB36
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_invalidate(rct_window* w)
2014-09-10 19:13:07 +02:00
{
TrackColour trackColour;
vehicle_colour vehicleColour;
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(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;
2019-07-21 13:09:23 +02:00
w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID;
auto ft = Formatter::Common();
ft.Increment(16);
ride->FormatNameTo(ft);
// Track colours
int32_t colourScheme = w->ride_colour;
trackColour = ride_get_track_colour(ride, colourScheme);
// Maze style
2018-06-22 23:21:44 +02:00
if (ride->type == RIDE_TYPE_MAZE)
{
window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu;
window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button;
window_ride_colour_widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty;
}
// Track, multiple colour schemes
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR))
2018-06-22 23:21:44 +02:00
{
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu;
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button;
window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty;
}
// Track main colour
2018-06-22 23:21:44 +02:00
if (window_ride_has_track_colour(ride, 0))
{
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].image = window_ride_get_colour_button_image(trackColour.main);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty;
}
// Track additional colour
2018-06-22 23:21:44 +02:00
if (window_ride_has_track_colour(ride, 1))
{
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = window_ride_get_colour_button_image(
trackColour.additional);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty;
}
// Track supports colour
2018-06-22 23:21:44 +02:00
if (window_ride_has_track_colour(ride, 2) && ride->type != RIDE_TYPE_MAZE)
{
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].image = window_ride_get_colour_button_image(trackColour.supports);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::Empty;
}
// Track preview
if (ride->GetRideTypeDescriptor().HasFlag(
2018-06-22 23:21:44 +02:00
RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL
| RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS))
window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner;
else
window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty;
// Entrance style
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
2018-06-22 23:21:44 +02:00
{
window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner;
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu;
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button;
auto stringId = STR_NONE;
auto stationObj = ride_get_station_object(ride);
if (stationObj != nullptr)
{
stringId = stationObj->NameStringId;
}
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].text = stringId;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Empty;
}
// Vehicle colours
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)
&& ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS))
2018-06-22 23:21:44 +02:00
{
int32_t vehicleColourSchemeType = ride->colour_scheme_type & 3;
if (vehicleColourSchemeType == 0)
2017-07-28 12:31:49 +02:00
w->vehicleIndex = 0;
2017-07-28 12:31:49 +02:00
vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex);
window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll;
window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].image = window_ride_get_colour_button_image(vehicleColour.main);
bool allowChangingAdditionalColour1 = false;
bool allowChangingAdditionalColour2 = false;
2018-06-22 23:21:44 +02:00
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);
2017-07-31 12:27:04 +02:00
if (rideEntry->vehicles[vehicleTypeIndex].flags & VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_1)
{
allowChangingAdditionalColour1 = true;
}
2017-07-31 12:27:04 +02:00
if (rideEntry->vehicles[vehicleTypeIndex].flags & VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_2)
{
allowChangingAdditionalColour2 = true;
}
}
// Additional colours
2018-06-22 23:21:44 +02:00
if (allowChangingAdditionalColour1)
{
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].image = window_ride_get_colour_button_image(
vehicleColour.additional_1);
2018-06-22 23:21:44 +02:00
if (allowChangingAdditionalColour2)
{
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WindowWidgetType::ColourBtn;
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].image = window_ride_get_colour_button_image(
vehicleColour.additional_2);
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WindowWidgetType::Empty;
}
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WindowWidgetType::Empty;
}
// Vehicle colour scheme type
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_VEHICLE_IS_INTEGRAL)
&& (ride->num_cars_per_train | ride->num_vehicles) > 1)
2018-06-22 23:21:44 +02:00
{
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
}
ft.Rewind();
ft.Increment(6);
ft.Add<rct_string_id>(VehicleColourSchemeNames[vehicleColourSchemeType]);
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular);
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised);
ft.Add<uint16_t>(w->vehicleIndex + 1);
// Vehicle index
2018-06-22 23:21:44 +02:00
if (vehicleColourSchemeType != 0)
{
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1
? STR_RIDE_COLOUR_TRAIN_VALUE
: STR_RIDE_COLOUR_VEHICLE_VALUE;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty;
}
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_MAIN_COLOUR].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_1].type = WindowWidgetType::Empty;
window_ride_colour_widgets[WIDX_VEHICLE_ADDITIONAL_COLOUR_2].type = WindowWidgetType::Empty;
}
ft.Rewind();
ft.Increment(14);
ft.Add<rct_string_id>(ColourSchemeNames[colourScheme]);
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AFF3E
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi)
2014-09-10 19:13:07 +02:00
{
// TODO: This should use lists and identified sprites
rct_drawpixelinfo clippedDpi;
2018-06-22 23:21:44 +02:00
rct_widget* widget;
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
// Track / shop item preview
widget = &window_ride_colour_widgets[WIDX_TRACK_PREVIEW];
if (widget->type != WindowWidgetType::Empty)
2018-06-22 23:21:44 +02:00
gfx_fill_rect(
dpi,
{ { w->windowPos + ScreenCoordsXY{ widget->left + 1, widget->top + 1 } },
{ w->windowPos + ScreenCoordsXY{ widget->right - 1, 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[0] == ShopItem::None)
2018-06-22 23:21:44 +02:00
{
auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top };
// Track
2018-06-22 23:21:44 +02:00
if (ride->type == RIDE_TYPE_MAZE)
{
int32_t spriteIndex = MazeOptions[trackColour.supports].sprite;
gfx_draw_sprite(dpi, spriteIndex, screenCoords, 0);
2018-06-22 23:21:44 +02:00
}
else
{
auto typeDescriptor = ride->GetRideTypeDescriptor();
int32_t spriteIndex = typeDescriptor.ColourPreview.Track;
2018-06-22 23:21:44 +02:00
if (spriteIndex != 0)
{
spriteIndex |= SPRITE_ID_PALETTE_COLOUR_2(trackColour.main, trackColour.additional);
gfx_draw_sprite(dpi, spriteIndex, screenCoords, 0);
}
// Supports
spriteIndex = typeDescriptor.ColourPreview.Supports;
2018-06-22 23:21:44 +02:00
if (spriteIndex != 0)
{
spriteIndex |= SPRITE_ID_PALETTE_COLOUR_1(trackColour.supports);
gfx_draw_sprite(dpi, spriteIndex, screenCoords, 0);
}
}
2018-06-22 23:21:44 +02:00
}
else
{
auto screenCoords = w->windowPos
+ ScreenCoordsXY{ (widget->left + widget->right) / 2 - 8, (widget->bottom + widget->top) / 2 - 6 };
ShopItem shopItem = rideEntry->shop_item[1] == ShopItem::None ? rideEntry->shop_item[0] : rideEntry->shop_item[1];
int32_t spriteIndex = GetShopItemDescriptor(shopItem).Image;
spriteIndex |= SPRITE_ID_PALETTE_COLOUR_1(ride->track_colour[0].main);
gfx_draw_sprite(dpi, spriteIndex, screenCoords, 0);
}
// Entrance preview
trackColour = ride_get_track_colour(ride, 0);
widget = &w->widgets[WIDX_ENTRANCE_PREVIEW];
if (widget->type != WindowWidgetType::Empty)
2018-06-22 23:21:44 +02:00
{
if (clip_drawpixelinfo(
&clippedDpi, dpi, w->windowPos + ScreenCoordsXY{ widget->left + 1, widget->top + 1 }, widget->width(),
widget->height()))
2018-06-22 23:21:44 +02:00
{
gfx_clear(&clippedDpi, PALETTE_INDEX_12);
auto stationObj = ride_get_station_object(ride);
if (stationObj != nullptr && stationObj->BaseImageId != 0)
2018-06-22 23:21:44 +02:00
{
int32_t terniaryColour = 0;
if (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT)
2018-06-22 23:21:44 +02:00
{
terniaryColour = IMAGE_TYPE_TRANSPARENT | (EnumValue(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);
}
}
DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + 103 }, 97, STR_STATION_STYLE, {}, COLOUR_BLACK);
}
2014-09-10 19:13:07 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B0192
2014-09-10 19:13:07 +02:00
*/
2018-06-22 23:21:44 +02:00
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);
// ?
auto screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 };
2015-06-29 02:24:07 +02:00
// ?
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)];
2015-03-01 15:24:05 +01:00
screenCoords.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, screenCoords, vehicleColour.additional_2);
}
2014-09-10 19:13:07 +02:00
#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, MUSIC_STYLE_CUSTOM_MUSIC_1,
MUSIC_STYLE_CUSTOM_MUSIC_2
2014-09-04 16:42:36 +02:00
};
static std::vector<ObjectEntryIndex> window_ride_current_music_style_order;
2014-09-04 16:42:36 +02:00
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B215D
2014-09-04 16:42:36 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_toggle_music(rct_window* w)
2014-09-04 16:42:36 +02:00
{
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);
}
2014-09-04 16:42:36 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1ED7
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
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;
}
2014-09-04 16:42:36 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AF4A2
2014-09-04 16:42:36 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_resize(rct_window* w)
2014-09-04 16:42:36 +02:00
{
w->flags |= WF_RESIZABLE;
window_set_resize(w, 316, 81, 316, 81);
}
2021-01-20 22:04:49 +01:00
static std::optional<size_t> GetMusicStyleOrder(ObjectEntryIndex musicObjectIndex)
{
auto& objManager = GetContext()->GetObjectManager();
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex));
// Get the index in the order list
auto originalStyleId = musicObj->GetOriginalStyleId();
if (originalStyleId)
{
auto it = std::find(std::begin(MusicStyleOrder), std::end(MusicStyleOrder), *originalStyleId);
if (it != std::end(MusicStyleOrder))
{
return std::distance(std::begin(MusicStyleOrder), it);
}
}
2021-01-20 22:04:49 +01:00
return std::nullopt;
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1EFC
*/
2018-06-22 23:21:44 +02:00
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;
// Construct list of available music
auto& musicOrder = window_ride_current_music_style_order;
musicOrder.clear();
auto& objManager = GetContext()->GetObjectManager();
for (ObjectEntryIndex i = 0; i < MAX_MUSIC_OBJECTS; i++)
2018-06-22 23:21:44 +02:00
{
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, i));
if (musicObj != nullptr)
{
// Hide custom music if the WAV file does not exist
auto originalStyleId = musicObj->GetOriginalStyleId();
if (originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_1 || originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_2)
{
auto numTracks = musicObj->GetTrackCount();
2021-01-20 22:04:49 +01:00
if (numTracks != 0)
{
auto track0 = musicObj->GetTrack(0);
if (!track0->Asset.IsAvailable())
{
continue;
}
}
else
{
continue;
}
}
if (musicObj->SupportsRideType(ride->type))
{
musicOrder.push_back(i);
}
}
}
// Sort available music by the original RCT2 list order
std::stable_sort(musicOrder.begin(), musicOrder.end(), [](const ObjectEntryIndex& a, const ObjectEntryIndex& b) {
auto orderA = GetMusicStyleOrder(a);
auto orderB = GetMusicStyleOrder(b);
return orderA < orderB;
});
// Setup dropdown list
auto numItems = musicOrder.size();
for (size_t i = 0; i < numItems; i++)
2018-06-22 23:21:44 +02:00
{
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i]));
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = musicObj->NameStringId;
}
WindowDropdownShowTextCustomWidth(
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
// Set currently checked item
for (size_t i = 0; i < numItems; i++)
2018-06-22 23:21:44 +02:00
{
if (musicOrder[i] == ride->music)
2018-06-22 23:21:44 +02:00
{
Dropdown::SetChecked(static_cast<int32_t>(i), true);
}
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1F03
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0
&& static_cast<size_t>(dropdownIndex) < window_ride_current_music_style_order.size())
{
auto musicStyle = window_ride_current_music_style_order[dropdownIndex];
set_operating_setting(w->number, RideSetSetting::MusicType, musicStyle);
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B2198
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_update(rct_window* w)
{
w->frame_no++;
window_event_invalidate_call(w);
widget_invalidate(w, WIDX_TAB_6);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1DEA
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_invalidate(rct_window* w)
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
2014-09-04 16:42:36 +02:00
window_ride_set_pressed_tab(w);
2014-09-04 16:42:36 +02:00
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
2014-09-04 16:42:36 +02:00
// Set selected music
rct_string_id musicName = STR_NONE;
auto& objManager = GetContext()->GetObjectManager();
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, ride->music));
if (musicObj != nullptr)
{
musicName = musicObj->NameStringId;
}
window_ride_music_widgets[WIDX_MUSIC].text = musicName;
2014-09-04 16:42:36 +02:00
// Set music activated
auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0;
2018-06-22 23:21:44 +02:00
if (isMusicActivated)
{
w->pressed_widgets |= (1 << WIDX_PLAY_MUSIC);
w->disabled_widgets &= ~(1 << WIDX_MUSIC);
w->disabled_widgets &= ~(1 << WIDX_MUSIC_DROPDOWN);
2018-06-22 23:21:44 +02:00
}
else
{
w->pressed_widgets &= ~(1 << WIDX_PLAY_MUSIC);
w->disabled_widgets |= (1 << WIDX_MUSIC);
w->disabled_widgets |= (1 << WIDX_MUSIC_DROPDOWN);
}
2014-09-04 16:42:36 +02:00
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006B1ECC
*/
2018-06-22 23:21:44 +02:00
static void window_ride_music_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
}
2014-08-31 13:28:23 +02:00
#pragma endregion
2014-09-04 20:59:42 +02:00
#pragma region Measurements
static rct_string_id get_rating_name(ride_rating rating)
{
2020-04-19 14:08:22 +02:00
int32_t index = std::clamp<int32_t>(rating >> 8, 0, static_cast<int32_t>(std::size(RatingNames)) - 1);
return RatingNames[index];
}
/**
*
* rct2: 0x006D2804
when al == 0*/
2018-06-22 23:21:44 +02:00
static void cancel_scenery_selection()
{
gGamePaused &= ~GAME_PAUSED_SAVING_TRACK;
gTrackDesignSaveMode = false;
OpenRCT2::Audio::Resume();
rct_window* main_w = window_get_main();
2018-06-22 23:21:44 +02:00
if (main_w)
{
main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE);
}
gfx_invalidate_screen();
tool_cancel();
}
/**
*
* rct2: 0x006D27A3
*/
2016-05-02 03:40:56 +02:00
static void setup_scenery_selection(rct_window* w)
{
2018-06-22 23:21:44 +02:00
if (gTrackDesignSaveMode)
{
cancel_scenery_selection();
}
while (tool_set(w, WIDX_BACKGROUND, Tool::Crosshair))
2018-06-22 23:21:44 +02:00
;
gTrackDesignSaveRideIndex = w->number;
2019-04-16 20:36:00 +02:00
track_design_save_init();
gGamePaused |= GAME_PAUSED_SAVING_TRACK;
gTrackDesignSaveMode = true;
OpenRCT2::Audio::StopAll();
rct_window* w_main = window_get_main();
2018-06-22 23:21:44 +02:00
if (w_main)
{
w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE);
}
gfx_invalidate_screen();
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006D3026
*/
static void window_ride_measurements_design_reset()
{
2019-04-16 20:36:00 +02:00
track_design_save_reset_scenery();
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006D303D
*/
static void window_ride_measurements_design_select_nearby_scenery()
{
2019-04-16 20:36:00 +02:00
track_design_save_select_nearby_scenery(gTrackDesignSaveRideIndex);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4DA
*/
void window_ride_measurements_design_cancel()
{
2018-06-22 23:21:44 +02:00
if (gTrackDesignSaveMode)
{
cancel_scenery_selection();
}
}
static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path)
2019-06-30 12:23:06 +02:00
{
if (result == MODAL_RESULT_OK)
{
track_repository_scan();
}
gfx_invalidate_screen();
};
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4CD
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_design_save(rct_window* w)
{
2019-06-30 12:23:06 +02:00
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, {});
2019-06-30 12:23:06 +02:00
return;
}
}
auto trackName = ride->GetName();
2019-06-30 12:23:06 +02:00
auto intent = Intent(WC_LOADSAVE);
intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK);
intent.putExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get());
intent.putExtra(INTENT_EXTRA_PATH, trackName);
intent.putExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast<void*>(&TrackDesignCallback));
2019-06-30 12:23:06 +02:00
context_open_intent(&intent);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4DA
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_close(rct_window* w)
{
window_ride_measurements_design_cancel();
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD478
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD564
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_resize(rct_window* w)
{
window_set_resize(w, 316, 234, 316, 234);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4AB
*/
2018-06-22 23:21:44 +02:00
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;
WindowDropdownShowText(
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1],
Dropdown::Flag::StayOpen, 2);
gDropdownDefaultIndex = 0;
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))
{
// Disable saving without scenery if we're a flat ride
Dropdown::SetDisabled(0, true);
gDropdownDefaultIndex = 1;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4B2
*/
2018-06-22 23:21:44 +02:00
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)
2019-04-08 20:34:24 +02:00
{
2019-06-30 12:23:06 +02:00
window_ride_measurements_design_save(w);
2019-04-08 20:34:24 +02:00
}
else
setup_scenery_selection(w);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD5DD
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_update(rct_window* w)
{
w->frame_no++;
window_event_invalidate_call(w);
widget_invalidate(w, WIDX_TAB_7);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006D2AE7
*/
static void window_ride_measurements_tooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
{
_lastSceneryX = screenCoords.x;
_lastSceneryY = screenCoords.y;
_collectTrackDesignScenery = true; // Default to true in case user does not select anything valid
auto flags = EnumsToFlags(
ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall,
ViewportInteractionItem::LargeScenery);
auto info = get_map_coordinates_from_pos(screenCoords, flags);
switch (info.SpriteType)
2018-06-22 23:21:44 +02:00
{
case ViewportInteractionItem::Scenery:
case ViewportInteractionItem::LargeScenery:
case ViewportInteractionItem::Wall:
case ViewportInteractionItem::Footpath:
_collectTrackDesignScenery = !track_design_save_contains_tile_element(info.Element);
track_design_save_select_tile_element(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery);
break;
default:
2018-06-22 23:21:44 +02:00
break;
}
}
static void window_ride_measurements_tooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
{
if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY)
return;
_lastSceneryX = screenCoords.x;
_lastSceneryY = screenCoords.y;
auto flags = EnumsToFlags(
ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall,
ViewportInteractionItem::LargeScenery);
auto info = get_map_coordinates_from_pos(screenCoords, flags);
switch (info.SpriteType)
2018-06-22 23:21:44 +02:00
{
case ViewportInteractionItem::Scenery:
case ViewportInteractionItem::LargeScenery:
case ViewportInteractionItem::Wall:
case ViewportInteractionItem::Footpath:
track_design_save_select_tile_element(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery);
break;
default:
2018-06-22 23:21:44 +02:00
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD4DA
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_toolabort(rct_window* w, rct_widgetindex widgetIndex)
{
window_ride_measurements_design_cancel();
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ACDBC
*/
2018-06-22 23:21:44 +02:00
static void window_ride_measurements_invalidate(rct_window* w)
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE;
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty;
2018-06-22 23:21:44 +02:00
if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == w->number)
{
window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button;
window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button;
window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button;
window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty;
window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty;
window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty;
window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty;
window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn;
w->disabled_widgets |= (1 << WIDX_SAVE_TRACK_DESIGN);
2018-06-22 23:21:44 +02:00
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);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ACF07
*/
2018-06-22 23:21:44 +02:00
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;
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button)
2018-06-22 23:21:44 +02:00
{
rct_widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND];
ScreenCoordsXY widgetCoords(w->windowPos.x + widget->width() / 2, w->windowPos.y + widget->top + 40);
gfx_draw_string_centred_wrapped(
dpi, nullptr, widgetCoords, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, COLOUR_BLACK);
widgetCoords.x = w->windowPos.x + 4;
widgetCoords.y = w->windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17;
gfx_fill_rect_inset(
dpi, { widgetCoords, { w->windowPos.x + 312, widgetCoords.y + 1 } }, w->colours[1], INSET_RECT_FLAG_BORDER_INSET);
2018-06-22 23:21:44 +02:00
}
else
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto screenCoords = w->windowPos
+ ScreenCoordsXY{ window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4,
window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4 };
2018-06-22 23:21:44 +02:00
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
{
// Excitement
rct_string_id ratingName = get_rating_name(ride->excitement);
auto ft = Formatter();
ft.Add<uint32_t>(ride->excitement);
ft.Add<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, ft.Data(), COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Intensity
ratingName = get_rating_name(ride->intensity);
ft = Formatter();
ft.Add<uint32_t>(ride->intensity);
ft.Add<rct_string_id>(ratingName);
stringId = STR_INTENSITY_RATING;
if (ride->excitement == RIDE_RATING_UNDEFINED)
stringId = STR_INTENSITY_RATING_NOT_YET_AVAILABLE;
2018-06-22 23:21:44 +02:00
else if (ride->intensity >= RIDE_RATING(10, 00))
stringId = STR_INTENSITY_RATING_RED;
gfx_draw_string_left(dpi, stringId, ft.Data(), COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Nausea
ratingName = get_rating_name(ride->nausea);
ft = Formatter();
ft.Add<uint32_t>(ride->nausea);
ft.Add<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, ft.Data(), COLOUR_BLACK, screenCoords);
screenCoords.y += 2 * LIST_ROW_HEIGHT;
// Horizontal rule
gfx_fill_rect_inset(
dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, w->colours[1],
INSET_RECT_FLAG_BORDER_INSET);
2018-06-22 23:21:44 +02:00
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS))
{
if (ride->type == RIDE_TYPE_MINI_GOLF)
{
// Holes
2019-03-27 11:30:18 +01:00
holes = ride->holes;
gfx_draw_string_left(dpi, STR_HOLES, &holes, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
2018-06-22 23:21:44 +02:00
}
else
{
// Max speed
maxSpeed = (ride->max_speed * 9) >> 18;
gfx_draw_string_left(dpi, STR_MAX_SPEED, &maxSpeed, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Average speed
averageSpeed = (ride->average_speed * 9) >> 18;
gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &averageSpeed, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Ride time
ft = Formatter();
int32_t numTimes = 0;
2018-06-22 23:21:44 +02:00
for (int32_t i = 0; i < ride->num_stations; i++)
{
time = ride->stations[numTimes].SegmentTime;
2018-06-22 23:21:44 +02:00
if (time != 0)
{
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY_WITH_SEPARATOR);
ft.Add<uint16_t>(time);
numTimes++;
}
}
2018-06-22 23:21:44 +02:00
if (numTimes == 0)
{
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY);
ft.Add<uint16_t>(0);
numTimes++;
2018-06-22 23:21:44 +02:00
}
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
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment((numTimes - 1) * 4);
ft.Add<uint16_t>(STR_RIDE_TIME_ENTRY);
}
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment(numTimes * 4);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_TIME, ft, COLOUR_BLACK);
screenCoords.y += LIST_ROW_HEIGHT;
}
// Ride length
ft = Formatter();
int32_t numLengths = 0;
2018-06-22 23:21:44 +02:00
for (int32_t i = 0; i < ride->num_stations; i++)
{
length = ride->stations[i].SegmentLength;
2018-06-22 23:21:44 +02:00
if (length != 0)
{
length >>= 16;
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR);
ft.Add<uint16_t>(length & 0xFFFF);
numLengths++;
}
}
2018-06-22 23:21:44 +02:00
if (numLengths == 0)
{
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY);
ft.Add<uint16_t>(0);
numLengths++;
2018-06-22 23:21:44 +02:00
}
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
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment((numLengths - 1) * 4);
ft.Add<rct_string_id>(STR_RIDE_LENGTH_ENTRY);
}
2020-06-03 12:54:39 +02:00
ft.Rewind();
ft.Increment(numLengths * 4);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
ft.Add<uint16_t>(0);
DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_LENGTH, ft, COLOUR_BLACK);
screenCoords.y += LIST_ROW_HEIGHT;
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES))
2018-06-22 23:21:44 +02:00
{
// 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, screenCoords);
screenCoords.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, screenCoords);
screenCoords.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, screenCoords);
screenCoords.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, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_DROPS))
2018-06-22 23:21:44 +02:00
{
// Drops
drops = ride->drops & 0x3F;
gfx_draw_string_left(dpi, STR_DROPS, &drops, COLOUR_BLACK, screenCoords);
screenCoords.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, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
2018-06-22 23:21:44 +02:00
if (ride->type != RIDE_TYPE_MINI_GOLF)
{
// Inversions
2019-03-27 11:30:18 +01:00
inversions = ride->inversions;
2018-06-22 23:21:44 +02:00
if (inversions != 0)
{
gfx_draw_string_left(dpi, STR_INVERSIONS, &inversions, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
}
}
2018-06-22 23:21:44 +02:00
}
else
{
gfx_draw_string_left(dpi, STR_NO_TEST_RESULTS_YET, nullptr, COLOUR_BLACK, screenCoords);
}
}
}
#pragma endregion
#pragma region Graphs
2018-06-22 23:21:44 +02:00
enum
{
GRAPH_VELOCITY,
GRAPH_ALTITUDE,
GRAPH_VERTICAL,
GRAPH_LATERAL
};
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE8A6
*/
2018-06-22 23:21:44 +02:00
static void window_ride_set_graph(rct_window* w, int32_t type)
{
2018-06-22 23:21:44 +02:00
if ((w->list_information_type & 0xFF) == type)
{
w->list_information_type ^= 0x8000;
2018-06-22 23:21:44 +02:00
}
else
{
w->list_information_type &= 0xFF00;
w->list_information_type |= type;
}
w->Invalidate();
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE85D
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE8DA
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_resize(rct_window* w)
{
window_set_resize(w, 316, 182, 500, 450);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE878
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE95D
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_update(rct_window* w)
{
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
if (!(w->list_information_type & 0x8000))
{
2019-02-13 00:58:45 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
{
2019-06-09 11:55:41 +02:00
RideMeasurement* measurement{};
std::tie(measurement, std::ignore) = ride->GetMeasurement();
x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3);
2019-02-13 00:58:45 +01:00
}
}
w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - (widget->width() - 2));
2020-11-03 22:29:22 +01:00
WidgetScrollUpdateThumbs(w, WIDX_GRAPH);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEA75
*/
2018-06-22 23:21:44 +02:00
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].width() - 2;
// Get measurement size
2019-02-13 00:58:45 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
{
2019-06-09 11:55:41 +02:00
RideMeasurement* measurement{};
std::tie(measurement, std::ignore) = ride->GetMeasurement();
2019-02-13 00:58:45 +01:00
if (measurement != nullptr)
{
*width = std::max<int32_t>(*width, measurement->num_items);
}
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE953
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_15(rct_window* w, int32_t scrollIndex, int32_t scrollAreaType)
{
w->list_information_type |= 0x8000;
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AEA05
*/
static OpenRCT2String window_ride_graphs_tooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback)
{
2018-06-22 23:21:44 +02:00
if (widgetIndex == WIDX_GRAPH)
{
2019-02-13 00:58:45 +01:00
auto ride = get_ride(w->number);
if (ride != nullptr)
2018-06-22 23:21:44 +02:00
{
auto [measurement, message] = ride->GetMeasurement();
2019-02-13 00:58:45 +01:00
if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING))
{
auto ft = Formatter();
ft.Increment(2);
ft.Add<rct_string_id>(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number);
ft.Add<uint16_t>(measurement->vehicle_index + 1);
return { fallback, ft };
2019-02-13 00:58:45 +01:00
}
else
{
return message;
2019-02-13 00:58:45 +01:00
}
}
2018-06-22 23:21:44 +02:00
}
else
{
return { STR_NONE, {} };
}
return { fallback, {} };
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE372
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_invalidate(rct_window* w)
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
// 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->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES))
2018-06-22 23:21:44 +02:00
{
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button;
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button;
2018-06-22 23:21:44 +02:00
}
else
{
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty;
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty;
}
// Anchor graph widget
auto x = w->width - 4;
auto y = w->height - BUTTON_FACE_HEIGHT - 8;
window_ride_graphs_widgets[WIDX_GRAPH].right = x;
window_ride_graphs_widgets[WIDX_GRAPH].bottom = y;
y += 3;
window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].top = y;
window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].top = y;
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].top = y;
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].top = y;
y += BUTTON_FACE_HEIGHT + 1;
window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].bottom = y;
window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].bottom = y;
window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].bottom = y;
window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].bottom = y;
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE4BC
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE4C7
*/
2018-06-22 23:21:44 +02:00
static void window_ride_graphs_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
{
gfx_clear(dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker);
2019-02-13 00:58:45 +01:00
auto widget = &window_ride_graphs_widgets[WIDX_GRAPH];
auto ride = get_ride(w->number);
if (ride == nullptr)
2019-02-13 00:58:45 +01:00
{
return;
2019-02-13 00:58:45 +01:00
}
auto [measurement, message] = ride->GetMeasurement();
2018-06-22 23:21:44 +02:00
if (measurement == nullptr)
{
// No measurement message
ScreenCoordsXY stringCoords(widget->width() / 2, widget->height() / 2 - 5);
int32_t width = widget->width() - 2;
gfx_draw_string_centred_wrapped(dpi, message.args.Data(), stringCoords, width, message.str, 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;
2018-06-22 23:21:44 +02:00
for (int32_t x = 0; x < dpi->x + dpi->width; x += 80)
{
if (x + 80 >= dpi->x)
{
auto coord1 = ScreenCoordsXY{ x, dpi->y };
auto coord2 = ScreenCoordsXY{ x, dpi->y + dpi->height - 1 };
gfx_fill_rect(dpi, { coord1, coord2 }, lightColour);
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 16, 0 }, coord2 + ScreenCoordsXY{ 16, 0 } }, darkColour);
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 32, 0 }, coord2 + ScreenCoordsXY{ 32, 0 } }, darkColour);
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 48, 0 }, coord2 + ScreenCoordsXY{ 48, 0 } }, darkColour);
gfx_fill_rect(dpi, { coord1 + ScreenCoordsXY{ 64, 0 }, coord2 + ScreenCoordsXY{ 64, 0 } }, darkColour);
}
time += 5;
}
// Horizontal grid lines
int32_t listType = w->list_information_type & 0xFF;
int16_t yUnit = window_graphs_y_axi[listType].unit;
rct_string_id stringID = window_graphs_y_axi[listType].label;
int16_t yUnitInterval = window_graphs_y_axi[listType].unit_interval;
int16_t yInterval = window_graphs_y_axi[listType].interval;
// Scale modifier
2018-06-22 23:21:44 +02:00
if (listType == GRAPH_ALTITUDE)
{
yUnit -= gMapBaseZ * 3;
}
for (int32_t y = widget->height() - 13; y >= 8; y -= yInterval, yUnit += yUnitInterval)
2018-06-22 23:21:44 +02:00
{
// 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;
2018-06-22 23:21:44 +02:00
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;
2018-09-08 23:34:05 +02:00
// 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;
2018-06-22 23:21:44 +02:00
for (int32_t width = 0; width < dpi->width; width++, x++)
{
if (x < 0 || x >= measurement->num_items - 1)
continue;
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
break;
default:
log_error("Wrong graph type %d", listType);
top = bottom = 0;
2018-09-08 23:34:05 +02:00
break;
}
// Adjust line to match graph widget position.
top = widget->height() - top - 13;
bottom = widget->height() - bottom - 13;
2018-09-08 23:34:05 +02:00
if (top > bottom)
{
std::swap(top, bottom);
}
2018-09-08 23:34:05 +02:00
// Adjust threshold line position as well
if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL)
{
intensityThresholdPositive = widget->height() - intensityThresholdPositive - 13;
intensityThresholdNegative = widget->height() - intensityThresholdNegative - 13;
}
2018-09-08 23:34:05 +02:00
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).
2018-09-08 23:34:05 +02:00
if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL)
{
2018-09-08 23:34:05 +02:00
const auto redLineColour = previousMeasurement ? PALETTE_INDEX_171 : PALETTE_INDEX_173;
// Line exceeds negative threshold (at bottom of graph).
if (bottom >= intensityThresholdNegative)
{
const auto redLineTop = ScreenCoordsXY{ x, std::max(top, intensityThresholdNegative) };
const auto redLineBottom = ScreenCoordsXY{ x, std::max(bottom, intensityThresholdNegative) };
gfx_fill_rect(dpi, { redLineTop, redLineBottom }, redLineColour);
}
2018-09-08 23:34:05 +02:00
// Line exceeds positive threshold (at top of graph).
if (top <= intensityThresholdPositive)
{
const auto redLineTop = ScreenCoordsXY{ x, std::min(top, intensityThresholdPositive) };
const auto redLineBottom = ScreenCoordsXY{ x, std::min(bottom, intensityThresholdPositive) };
gfx_fill_rect(dpi, { redLineTop, redLineBottom }, redLineColour);
}
}
}
}
#pragma endregion
#pragma region Income
static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH];
static void update_same_price_throughout_flags(ShopItem shop_item)
{
2019-03-16 08:33:21 +01:00
uint64_t newFlags;
if (GetShopItemDescriptor(shop_item).IsPhoto())
2018-06-22 23:21:44 +02:00
{
2019-03-16 08:33:21 +01:00
newFlags = gSamePriceThroughoutPark;
newFlags ^= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4);
auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags);
2019-03-16 08:33:21 +01:00
GameActions::Execute(&parkSetParameter);
}
2018-06-22 23:21:44 +02:00
else
{
2019-03-16 08:33:21 +01:00
newFlags = gSamePriceThroughoutPark;
newFlags ^= EnumToFlag(shop_item);
auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags);
2019-03-16 08:33:21 +01:00
GameActions::Execute(&parkSetParameter);
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADEFD
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_toggle_primary_price(rct_window* w)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2015-10-20 20:16:30 +02:00
ShopItem shop_item;
2018-06-22 23:21:44 +02:00
if (ride->type == RIDE_TYPE_TOILETS)
{
shop_item = ShopItem::Admission;
}
2018-06-22 23:21:44 +02:00
else
{
auto rideEntry = get_ride_entry(ride->subtype);
if (rideEntry != nullptr)
{
2020-05-11 17:50:30 +02:00
shop_item = rideEntry->shop_item[0];
if (shop_item == ShopItem::None)
return;
}
else
{
return;
}
}
2015-06-09 23:00:11 +02:00
update_same_price_throughout_flags(shop_item);
2020-05-11 17:50:30 +02:00
auto rideSetPriceAction = RideSetPriceAction(w->number, ride->price[0], true);
GameActions::Execute(&rideSetPriceAction);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE06E
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_toggle_secondary_price(rct_window* w)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2015-06-09 23:00:11 +02:00
auto rideEntry = get_ride_entry(ride->subtype);
if (rideEntry == nullptr)
return;
2015-10-20 20:16:30 +02:00
2020-05-11 17:50:30 +02:00
auto shop_item = rideEntry->shop_item[1];
if (shop_item == ShopItem::None)
shop_item = ride->GetRideTypeDescriptor().PhotoItem;
2015-06-09 23:00:11 +02:00
update_same_price_throughout_flags(shop_item);
2015-06-09 23:00:11 +02:00
2020-05-11 17:50:30 +02:00
auto rideSetPriceAction = RideSetPriceAction(w->number, ride->price[1], false);
GameActions::Execute(&rideSetPriceAction);
}
2018-06-22 23:21:44 +02:00
static void window_ride_income_set_primary_price(rct_window* w, money16 price)
{
auto rideSetPriceAction = RideSetPriceAction(w->number, price, true);
GameActions::Execute(&rideSetPriceAction);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE1E4
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_increase_primary_price(rct_window* w)
{
2018-06-01 14:23:46 +02:00
if (!window_ride_income_can_modify_primary_price(w))
return;
2015-10-20 20:16:30 +02:00
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2020-05-11 17:50:30 +02:00
money16 price = ride->price[0];
if (price < MONEY(20, 00))
price++;
2015-06-09 03:28:07 +02:00
window_ride_income_set_primary_price(w, price);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE237
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_decrease_primary_price(rct_window* w)
{
2018-06-01 14:23:46 +02:00
if (!window_ride_income_can_modify_primary_price(w))
return;
2015-06-09 03:28:07 +02:00
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2020-05-11 17:50:30 +02:00
money16 price = ride->price[0];
if (price > MONEY(0, 00))
price--;
2015-06-09 03:28:07 +02:00
window_ride_income_set_primary_price(w, price);
}
2018-06-22 23:21:44 +02:00
static money16 window_ride_income_get_secondary_price(rct_window* w)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return 0;
2020-05-11 17:50:30 +02:00
money16 price = ride->price[1];
return price;
}
2018-06-22 23:21:44 +02:00
static void window_ride_income_set_secondary_price(rct_window* w, money16 price)
{
2019-02-08 16:38:16 +01:00
auto rideSetPriceAction = RideSetPriceAction((w->number & 0x00FF), price, false);
GameActions::Execute(&rideSetPriceAction);
}
2018-06-01 14:23:46 +02:00
static bool window_ride_income_can_modify_primary_price(rct_window* w)
{
auto ride = get_ride(w->number);
if (ride == nullptr)
return false;
2018-06-01 14:23:46 +02:00
auto rideEntry = ride->GetRideEntry();
return park_ride_prices_unlocked() || ride->type == RIDE_TYPE_TOILETS
|| (rideEntry != nullptr && rideEntry->shop_item[0] != ShopItem::None);
2018-06-01 14:23:46 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE269
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_increase_secondary_price(rct_window* w)
{
money16 price = window_ride_income_get_secondary_price(w);
2015-10-20 20:16:30 +02:00
if (price < MONEY(20, 00))
price++;
2015-06-09 03:28:07 +02:00
window_ride_income_set_secondary_price(w, price);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE28D
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_decrease_secondary_price(rct_window* w)
{
money16 price = window_ride_income_get_secondary_price(w);
2018-06-22 23:21:44 +02:00
if (price > MONEY(0, 00))
price--;
2015-06-09 03:28:07 +02:00
window_ride_income_set_secondary_price(w, price);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADEA9
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
switch (widgetIndex)
{
2018-06-22 23:21:44 +02:00
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)
{
2020-05-11 17:50:30 +02:00
money_to_string(static_cast<money32>(ride->price[0]), _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;
}
2018-06-22 23:21:44 +02:00
case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK:
window_ride_income_toggle_primary_price(w);
break;
case WIDX_SECONDARY_PRICE:
{
money32 price32 = static_cast<money32>(window_ride_income_get_secondary_price(w));
2018-06-22 23:21:44 +02:00
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;
2018-06-22 23:21:44 +02:00
case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK:
window_ride_income_toggle_secondary_price(w);
break;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE2F8
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_resize(rct_window* w)
{
window_set_resize(w, 316, 194, 316, 194);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADED4
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
2018-06-22 23:21:44 +02:00
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;
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AE2BF
*/
2018-06-22 23:21:44 +02:00
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)
2018-06-22 23:21:44 +02:00
{
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 = static_cast<money16>(price);
if (widgetIndex == WIDX_PRIMARY_PRICE)
{
window_ride_income_set_primary_price(w, price16);
}
else
{
window_ride_income_set_secondary_price(w, price16);
}
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADAA3
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_invalidate(rct_window* w)
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
window_ride_set_pressed_tab(w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
2019-07-21 13:09:23 +02:00
w->widgets[WIDX_TITLE].text = STR_ARG_14_STRINGID;
auto ft = Formatter::Common();
ft.Increment(14);
ride->FormatNameTo(ft);
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[0] == ShopItem::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 = WindowWidgetType::Empty;
window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_ARG_6_CURRENCY2DP;
money16 ridePrimaryPrice = ride_get_price(ride);
ft.Rewind();
ft.Increment(6);
ft.Add<money32>(ridePrimaryPrice);
if (ridePrimaryPrice == 0)
window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE;
ShopItem primaryItem = ShopItem::Admission;
if (ride->type == RIDE_TYPE_TOILETS || ((primaryItem = rideEntry->shop_item[0]) != ShopItem::None))
2018-06-22 23:21:44 +02:00
{
window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::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 = GetShopItemDescriptor(primaryItem).Naming.PriceLabel;
}
// Get secondary item
auto secondaryItem = ride->GetRideTypeDescriptor().PhotoItem;
2018-06-22 23:21:44 +02:00
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO))
{
if ((secondaryItem = rideEntry->shop_item[1]) != ShopItem::None)
2018-06-22 23:21:44 +02:00
{
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem)
.Naming.PriceLabel;
}
}
if (secondaryItem == ShopItem::None)
2018-06-22 23:21:44 +02:00
{
// Hide secondary item widgets
window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty;
window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty;
2018-06-22 23:21:44 +02:00
}
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 = WindowWidgetType::Label;
window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button;
window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox;
// Set secondary item price
window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_RIDE_SECONDARY_PRICE_VALUE;
ft.Add<money32>(ride->price[1]);
2020-05-11 17:50:30 +02:00
if (ride->price[1] == 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);
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADCE5
*/
2018-06-22 23:21:44 +02:00
static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
rct_string_id stringId;
money32 profit, costPerHour;
ShopItem primaryItem, secondaryItem;
WindowDrawWidgets(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;
auto screenCoords = w->windowPos
+ ScreenCoordsXY{ window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4,
window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33 };
// Primary item profit / loss per item sold
2020-05-11 17:50:30 +02:00
primaryItem = rideEntry->shop_item[0];
if (primaryItem != ShopItem::None)
{
2020-05-11 17:50:30 +02:00
profit = ride->price[0];
stringId = STR_PROFIT_PER_ITEM_SOLD;
profit -= GetShopItemDescriptor(primaryItem).Cost;
2018-06-22 23:21:44 +02:00
if (profit < 0)
{
profit *= -1;
stringId = STR_LOSS_PER_ITEM_SOLD;
}
gfx_draw_string_left(dpi, stringId, &profit, COLOUR_BLACK, screenCoords);
}
screenCoords.y += 44;
// Secondary item profit / loss per item sold
secondaryItem = ride->GetRideTypeDescriptor().PhotoItem;
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO))
2020-05-11 17:50:30 +02:00
secondaryItem = rideEntry->shop_item[1];
if (secondaryItem != ShopItem::None)
{
2020-05-11 17:50:30 +02:00
profit = ride->price[1];
stringId = STR_PROFIT_PER_ITEM_SOLD;
profit -= GetShopItemDescriptor(secondaryItem).Cost;
2018-06-22 23:21:44 +02:00
if (profit < 0)
{
profit *= -1;
stringId = STR_LOSS_PER_ITEM_SOLD;
}
gfx_draw_string_left(dpi, stringId, &profit, COLOUR_BLACK, screenCoords);
}
screenCoords.y += 18;
// Income per hour
2018-06-22 23:21:44 +02:00
if (ride->income_per_hour != MONEY32_UNDEFINED)
{
gfx_draw_string_left(dpi, STR_INCOME_PER_HOUR, &ride->income_per_hour, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
// Running cost per hour
costPerHour = ride->upkeep_cost * 16;
2017-10-17 17:12:49 +02:00
stringId = ride->upkeep_cost == MONEY16_UNDEFINED ? STR_RUNNING_COST_UNKNOWN : STR_RUNNING_COST_PER_HOUR;
gfx_draw_string_left(dpi, stringId, &costPerHour, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Profit per hour
2018-06-22 23:21:44 +02:00
if (ride->profit != MONEY32_UNDEFINED)
{
gfx_draw_string_left(dpi, STR_PROFIT_PER_HOUR, &ride->profit, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
screenCoords.y += 5;
// Total profit
gfx_draw_string_left(dpi, STR_TOTAL_PROFIT, &ride->total_profit, COLOUR_BLACK, screenCoords);
}
#pragma endregion
2014-09-04 20:59:42 +02:00
#pragma region Customer
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD986
2014-09-04 20:59:42 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_customer_mouseup(rct_window* w, rct_widgetindex widgetIndex)
2014-09-04 20:59:42 +02:00
{
2017-09-10 11:02:16 +02:00
switch (widgetIndex)
{
2018-06-22 23:21:44 +02:00
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, static_cast<int32_t>(GuestListFilterType::GuestsThinkingAboutRide));
2018-06-22 23:21:44 +02:00
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
context_open_intent(&intent);
break;
}
case WIDX_SHOW_GUESTS_ON_RIDE:
{
auto intent = Intent(WC_GUEST_LIST);
intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast<int32_t>(GuestListFilterType::GuestsOnRide));
2018-06-22 23:21:44 +02:00
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
context_open_intent(&intent);
break;
}
case WIDX_SHOW_GUESTS_QUEUING:
{
auto intent = Intent(WC_GUEST_LIST);
intent.putExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast<int32_t>(GuestListFilterType::GuestsInQueue));
2018-06-22 23:21:44 +02:00
intent.putExtra(INTENT_EXTRA_RIDE_ID, w->number);
context_open_intent(&intent);
break;
}
2017-09-10 11:02:16 +02:00
}
2014-09-04 20:59:42 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006ADA29
2014-09-04 20:59:42 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_customer_resize(rct_window* w)
2014-09-04 20:59:42 +02:00
{
w->flags |= WF_RESIZABLE;
window_set_resize(w, 316, 163, 316, 163);
2014-09-04 20:59:42 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD9DD
2014-09-04 20:59:42 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_customer_update(rct_window* w)
2014-09-04 20:59:42 +02:00
{
2020-03-01 21:51:29 +01:00
w->picked_peep_frame++;
if (w->picked_peep_frame >= 24)
w->picked_peep_frame = 0;
2014-09-04 20:59:42 +02:00
window_event_invalidate_call(w);
widget_invalidate(w, WIDX_TAB_10);
2014-09-04 20:59:42 +02:00
auto ride = get_ride(w->number);
if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER)
2018-06-22 23:21:44 +02:00
{
ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER;
w->Invalidate();
}
2014-09-04 20:59:42 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD5F8
2014-09-04 20:59:42 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_customer_invalidate(rct_window* w)
2014-09-04 20:59:42 +02:00
{
auto widgets = window_ride_page_widgets[w->page];
2018-06-22 23:21:44 +02:00
if (w->widgets != widgets)
{
w->widgets = widgets;
WindowInitScrollWidgets(w);
}
2014-09-04 20:59:42 +02:00
window_ride_set_pressed_tab(w);
2014-09-04 20:59:42 +02:00
auto ride = get_ride(w->number);
if (ride != nullptr)
2018-06-22 23:21:44 +02:00
{
auto ft = Formatter::Common();
ride->FormatNameTo(ft);
2014-09-04 20:59:42 +02:00
window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn;
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP))
{
window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty;
window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty;
}
else
{
window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn;
window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn;
}
window_ride_anchor_border_widgets(w);
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
}
2014-09-04 20:59:42 +02:00
}
/**
2015-10-20 20:16:30 +02:00
*
* rct2: 0x006AD6CD
2014-09-04 20:59:42 +02:00
*/
2018-06-22 23:21:44 +02:00
static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi)
2014-09-04 20:59:42 +02:00
{
ShopItem shopItem;
int16_t popularity, satisfaction, queueTime;
int32_t customersPerHour;
rct_string_id stringId;
WindowDrawWidgets(w, dpi);
window_ride_draw_tab_images(dpi, w);
auto ride = get_ride(w->number);
if (ride == nullptr)
return;
auto screenCoords = w->windowPos
+ ScreenCoordsXY{ window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4,
window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4 };
// Customers currently on ride
2019-08-03 19:11:30 +02:00
if (ride->IsRide())
2018-06-22 23:21:44 +02:00
{
int16_t customersOnRide = ride->num_riders;
gfx_draw_string_left(dpi, STR_CUSTOMERS_ON_RIDE, &customersOnRide, COLOUR_BLACK, screenCoords);
screenCoords.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, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Popularity
popularity = ride->popularity;
2018-06-22 23:21:44 +02:00
if (popularity == 255)
{
stringId = STR_POPULARITY_UNKNOWN;
2018-06-22 23:21:44 +02:00
}
else
{
stringId = STR_POPULARITY_PERCENT;
popularity *= 4;
}
gfx_draw_string_left(dpi, stringId, &popularity, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Satisfaction
satisfaction = ride->satisfaction;
2018-06-22 23:21:44 +02:00
if (satisfaction == 255)
{
stringId = STR_SATISFACTION_UNKNOWN;
2018-06-22 23:21:44 +02:00
}
else
{
stringId = STR_SATISFACTION_PERCENT;
satisfaction *= 5;
}
gfx_draw_string_left(dpi, stringId, &satisfaction, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Queue time
2019-08-03 19:11:30 +02:00
if (ride->IsRide())
2018-06-22 23:21:44 +02:00
{
queueTime = ride->GetMaxQueueTime();
stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES;
screenCoords.y += gfx_draw_string_left_wrapped(dpi, &queueTime, screenCoords, 308, stringId, COLOUR_BLACK);
screenCoords.y += 5;
}
// Primary shop items sold
2020-05-11 17:50:30 +02:00
shopItem = ride->GetRideEntry()->shop_item[0];
if (shopItem != ShopItem::None)
2018-06-22 23:21:44 +02:00
{
auto ft = Formatter();
ft.Add<rct_string_id>(GetShopItemDescriptor(shopItem).Naming.Plural);
ft.Add<uint32_t>(ride->no_primary_items_sold);
gfx_draw_string_left(dpi, STR_ITEMS_SOLD, ft.Data(), COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
// Secondary shop items sold / on-ride photos sold
shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? ride->GetRideTypeDescriptor().PhotoItem
2020-05-11 17:50:30 +02:00
: ride->GetRideEntry()->shop_item[1];
if (shopItem != ShopItem::None)
2018-06-22 23:21:44 +02:00
{
auto ft = Formatter();
ft.Add<rct_string_id>(GetShopItemDescriptor(shopItem).Naming.Plural);
ft.Add<uint32_t>(ride->no_secondary_items_sold);
gfx_draw_string_left(dpi, STR_ITEMS_SOLD, ft.Data(), COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
// Total customers
gfx_draw_string_left(dpi, STR_TOTAL_CUSTOMERS, &ride->total_customers, COLOUR_BLACK, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
// Guests favourite
2019-08-03 19:11:30 +02:00
if (ride->IsRide())
2018-06-22 23:21:44 +02:00
{
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, screenCoords);
screenCoords.y += LIST_ROW_HEIGHT;
}
screenCoords.y += 2;
// Age
2018-06-22 23:21:44 +02:00
// If the ride has a build date that is in the future, show it as built this year.
int16_t age = std::max(date_get_year(ride->GetAge()), 0);
2018-06-22 23:21:44 +02:00
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, screenCoords);
2014-09-04 20:59:42 +02:00
}
2015-03-14 16:52:48 +01:00
#pragma endregion