diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 1d8f46a1fe..0dd50c5fff 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -49,6 +49,7 @@ - Improved: [#7555] Allow setting the Twitch API URL, allowing custom API servers. - Improved: [#7567] Improve the performance of loading parks and the title sequence. - Improved: [#7577] Allow fine-tuning the virtual floor style. +- Improved: [#7608] The vehicle selection dropdown is now sorted orthographically. 0.1.2 (2018-03-18) ------------------------------------------------------------------------ diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 21f726597a..9639642c16 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -1236,6 +1236,19 @@ struct RideTypeLabel static sint32 RideDropdownDataLanguage = LANGUAGE_UNDEFINED; static std::vector RideDropdownData; +// Used for sorting the vehicle type dropdown. +struct VehicleTypeLabel +{ + sint32 subtype_id; + rct_string_id label_id; + const char* label_string; +}; + +static sint32 VehicleDropdownDataLanguage = LANGUAGE_UNDEFINED; +static rct_ride_entry *VehicleDropdownRideType = nullptr; +static bool VehicleDropdownExpanded = false; +static std::vector VehicleDropdownData; + static void window_ride_draw_tab_image(rct_drawpixelinfo *dpi, rct_window *w, sint32 page, sint32 spriteIndex) { rct_widgetindex widgetIndex = WIDX_TAB_1 + page; @@ -2183,6 +2196,8 @@ static void populate_ride_type_dropdown() } std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) { return std::strcmp(a.label_string, b.label_string) < 0; }); + + RideDropdownDataLanguage = ls.GetCurrentLanguage(); } static void window_ride_show_ride_type_dropdown(rct_window *w, rct_widget *widget) @@ -2223,6 +2238,115 @@ static void window_ride_show_ride_type_dropdown(rct_window *w, rct_widget *widge dropdown_set_checked(pos, true); } +static void populate_vehicle_type_dropdown(Ride *ride) +{ + rct_ride_entry *rideEntry = get_ride_entry_by_ride(ride); + + bool selectionShouldBeExpanded; + sint32 rideTypeIterator, rideTypeIteratorMax; + if (gCheatsShowVehiclesFromOtherTrackTypes && !(ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE) || ride->type == RIDE_TYPE_MAZE || ride->type == RIDE_TYPE_MINI_GOLF)) + { + selectionShouldBeExpanded = true; + rideTypeIterator = 0; + rideTypeIteratorMax = RIDE_TYPE_COUNT - 1; + } + else + { + selectionShouldBeExpanded = false; + rideTypeIterator = ride->type; + rideTypeIteratorMax = ride->type; + } + + // Don't repopulate the list if we just did. + auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + if (VehicleDropdownExpanded == selectionShouldBeExpanded && + VehicleDropdownRideType == rideEntry && + VehicleDropdownDataLanguage == ls.GetCurrentLanguage()) + return; + + VehicleDropdownData.clear(); + + for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++) + { + if (selectionShouldBeExpanded && ride_type_has_flag(rideTypeIterator, RIDE_TYPE_FLAG_FLAT_RIDE)) + continue; + if (selectionShouldBeExpanded && (rideTypeIterator == RIDE_TYPE_MAZE || rideTypeIterator == RIDE_TYPE_MINI_GOLF)) + continue; + + uint8 *rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideTypeIterator); + + for (uint8 *currentRideEntryIndex = rideEntryIndexPtr; *currentRideEntryIndex != RIDE_ENTRY_INDEX_NULL; currentRideEntryIndex++) + { + sint32 rideEntryIndex = *currentRideEntryIndex; + rct_ride_entry *currentRideEntry = get_ride_entry(rideEntryIndex); + + // Skip if vehicle type has not been invented yet + if (!ride_entry_is_invented(rideEntryIndex) && !gCheatsIgnoreResearchStatus) + continue; + + // Skip if vehicle does not belong to the same ride group + if (RideGroupManager::RideTypeHasRideGroups(ride->type) && !selectionShouldBeExpanded) + { + const RideGroup *rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry); + const RideGroup *currentRideGroup = RideGroupManager::GetRideGroup(ride->type, currentRideEntry); + + if (!rideGroup->Equals(currentRideGroup)) + continue; + } + + VehicleTypeLabel label = { rideEntryIndex, currentRideEntry->naming.name, ls.GetString(currentRideEntry->naming.name) }; + VehicleDropdownData.push_back(label); + } + } + + std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) { return std::strcmp(a.label_string, b.label_string) < 0; }); + + VehicleDropdownExpanded = selectionShouldBeExpanded; + VehicleDropdownRideType = rideEntry; + VehicleDropdownDataLanguage = ls.GetCurrentLanguage(); +} + +static void window_ride_show_vehicle_type_dropdown(rct_window *w, rct_widget *widget) +{ + Ride *ride = get_ride(w->number); + populate_vehicle_type_dropdown(ride); + + size_t numItems = std::min(VehicleDropdownData.size(), DROPDOWN_ITEMS_MAX_SIZE); + + for (size_t i = 0; i < numItems; i++) + { + gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[i] = VehicleDropdownData[i].label_id; + } + + rct_widget *dropdownWidget = widget - 1; + window_dropdown_show_text_custom_width( + w->x + dropdownWidget->left, + w->y + dropdownWidget->top, + dropdownWidget->bottom - dropdownWidget->top + 1, + w->colours[1], + 0, + DROPDOWN_FLAG_STAY_OPEN, + numItems, + widget->right - dropdownWidget->left + ); + + // Find the current vehicle type in the ordered list. + uint8 pos = 0; + for (uint8 i = 0; i < VehicleDropdownData.size(); i++) + { + if (VehicleDropdownData[i].subtype_id == ride->subtype) + { + pos = i; + break; + } + } + + gDropdownHighlightedIndex = pos; + gDropdownDefaultIndex = pos; + dropdown_set_checked(pos, true); +} + /** * * rct2: 0x006AF1BD @@ -2702,83 +2826,11 @@ static void window_ride_vehicle_resize(rct_window *w) */ static void window_ride_vehicle_mousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget *widget) { - rct_widget *dropdownWidget = widget - 1; - Ride *ride; - rct_ride_entry *rideEntry, *currentRideEntry; - const RideGroup * rideGroup, * currentRideGroup; - sint32 numItems, rideEntryIndex, selectedIndex, rideTypeIterator, rideTypeIteratorMax; - uint8 *rideEntryIndexPtr; - bool selectionShouldBeExpanded; - - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); - - if(gCheatsShowVehiclesFromOtherTrackTypes && !(ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE) || ride->type==RIDE_TYPE_MAZE || ride->type==RIDE_TYPE_MINI_GOLF)) { - selectionShouldBeExpanded = true; - rideTypeIterator = 0; - rideTypeIteratorMax = RIDE_TYPE_COUNT - 1; - } - else { - selectionShouldBeExpanded = false; - rideTypeIterator = ride->type; - rideTypeIteratorMax = ride->type; - } + Ride *ride = get_ride(w->number); switch (widgetIndex) { case WIDX_VEHICLE_TYPE_DROPDOWN: - selectedIndex = -1; - numItems = 0; - - // Dropdowns with more items start acting weird, so cap it. - for (; rideTypeIterator <= rideTypeIteratorMax && numItems < DROPDOWN_ITEMS_MAX_SIZE; rideTypeIterator++) { - - if(selectionShouldBeExpanded && ride_type_has_flag(rideTypeIterator, RIDE_TYPE_FLAG_FLAT_RIDE)) - continue; - if(selectionShouldBeExpanded && (rideTypeIterator == RIDE_TYPE_MAZE || rideTypeIterator == RIDE_TYPE_MINI_GOLF)) - continue; - - rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideTypeIterator); - - for (uint8 *currentRideEntryIndex = rideEntryIndexPtr; *currentRideEntryIndex != RIDE_ENTRY_INDEX_NULL && numItems < DROPDOWN_ITEMS_MAX_SIZE; currentRideEntryIndex++) { - rideEntryIndex = *currentRideEntryIndex; - currentRideEntry = get_ride_entry(rideEntryIndex); - - // Skip if vehicle type is not invented yet - if (!ride_entry_is_invented(rideEntryIndex) && !gCheatsIgnoreResearchStatus) - continue; - - // Skip if vehicle does not belong to the same ride group - if (RideGroupManager::RideTypeHasRideGroups(ride->type) && !selectionShouldBeExpanded) - { - rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry); - currentRideGroup = RideGroupManager::GetRideGroup(ride->type, currentRideEntry); - - if (!rideGroup->Equals(currentRideGroup)) - continue; - } - - if (ride->subtype == rideEntryIndex) - selectedIndex = numItems; - - gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[numItems] = (rideEntryIndex << 16) | currentRideEntry->naming.name; - - numItems++; - } - } - - window_dropdown_show_text_custom_width( - w->x + dropdownWidget->left, - w->y + dropdownWidget->top, - dropdownWidget->bottom - dropdownWidget->top + 1, - w->colours[1], - 0, - DROPDOWN_FLAG_STAY_OPEN, - numItems, - widget->right - dropdownWidget->left - ); - - dropdown_set_checked(selectedIndex, true); + window_ride_show_vehicle_type_dropdown(w, &w->widgets[widgetIndex]); break; case WIDX_VEHICLE_TRAINS_INCREASE: if (ride->num_vehicles < 32) @@ -2793,6 +2845,7 @@ static void window_ride_vehicle_mousedown(rct_window *w, rct_widgetindex widgetI ride_set_num_cars_per_vehicle(w->number, ride->num_cars_per_train + 1); break; case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: + rct_ride_entry * rideEntry = get_ride_entry_by_ride(ride); if (ride->num_cars_per_train > rideEntry->zero_cars + 1) ride_set_num_cars_per_vehicle(w->number, ride->num_cars_per_train - 1); break; @@ -2810,8 +2863,8 @@ static void window_ride_vehicle_dropdown(rct_window *w, rct_widgetindex widgetIn switch (widgetIndex) { case WIDX_VEHICLE_TYPE_DROPDOWN: - dropdownIndex = (gDropdownItemsArgs[dropdownIndex] >> 16) & 0xFFFF; - ride_set_ride_entry(w->number, dropdownIndex); + sint32 newRideType = VehicleDropdownData[dropdownIndex].subtype_id; + ride_set_ride_entry(w->number, newRideType); break; } }