From 96a7a8ab508f09e3d0220ca6b363fa8ee8874201 Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Wed, 14 Jun 2017 13:31:27 +0200 Subject: [PATCH] Sort rides into ride groups when in select-by-track-type mode * Add ride groups * Convert ride groups to C++ * Fix linking and crashes in Xcode * Comment out .field syntax to make Visual Studio happy * Fix alignment, fix changelog, cleanup * Properly save ride group index in the ride object repository and bump its version * Fix fallback behaviour when the ride entry is not available or not invented * Fix include * Rename RideGroup to RideGroupManager, add const, cleanup * Break after finding matching ride group --- OpenRCT2.xcodeproj/project.pbxproj | 6 + data/language/en-GB.txt | 18 ++ distribution/changelog.txt | 1 + src/openrct2/localisation/string_ids.h | 19 ++ src/openrct2/management/research.c | 64 +++++- src/openrct2/object/ObjectRepository.cpp | 6 +- src/openrct2/object/ObjectRepository.h | 4 +- src/openrct2/object/RideObject.cpp | 60 +++-- src/openrct2/object/RideObject.h | 2 +- src/openrct2/ride/RideGroupManager.cpp | 239 ++++++++++++++++++++ src/openrct2/ride/RideGroupManager.h | 60 +++++ src/openrct2/ride/TrackDesignRepository.cpp | 77 ++++++- src/openrct2/ride/TrackDesignRepository.h | 8 + src/openrct2/ride/ride.c | 186 ++++++++++----- src/openrct2/ride/ride.h | 27 ++- src/openrct2/ride/ride_data.h | 5 - src/openrct2/ride/track.c | 21 +- src/openrct2/ride/track_design.c | 42 +++- src/openrct2/windows/new_ride.c | 210 +++++++++++------ src/openrct2/windows/research.c | 3 +- src/openrct2/windows/ride.c | 28 +-- src/openrct2/windows/ride_construction.c | 20 +- src/openrct2/windows/track_list.c | 28 ++- 23 files changed, 937 insertions(+), 197 deletions(-) create mode 100644 src/openrct2/ride/RideGroupManager.cpp create mode 100644 src/openrct2/ride/RideGroupManager.h diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index eeb63e4e8b..89c808c262 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 4C8667821EEFDCDF0024AAB8 /* RideGroupManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8667801EEFDCDF0024AAB8 /* RideGroupManager.cpp */; }; 4C8B42701EEB1ABD00F015CA /* X8DrawingEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8B426E1EEB1ABD00F015CA /* X8DrawingEngine.cpp */; }; 4C8B42721EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8B42711EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp */; }; 4C8B42741EEB1B6F00F015CA /* Screenshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8B42731EEB1B6F00F015CA /* Screenshot.cpp */; }; @@ -562,6 +563,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4C8667801EEFDCDF0024AAB8 /* RideGroupManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RideGroupManager.cpp; sourceTree = ""; }; + 4C8667811EEFDCDF0024AAB8 /* RideGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RideGroupManager.h; sourceTree = ""; }; 4C8B426E1EEB1ABD00F015CA /* X8DrawingEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = X8DrawingEngine.cpp; sourceTree = ""; }; 4C8B426F1EEB1ABD00F015CA /* X8DrawingEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X8DrawingEngine.h; sourceTree = ""; }; 4C8B42711EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HardwareDisplayDrawingEngine.cpp; sourceTree = ""; }; @@ -2171,6 +2174,8 @@ F76C84BB1EC4E7CC00FA49E2 /* ride.h */, F76C84BC1EC4E7CC00FA49E2 /* ride_data.c */, F76C84BD1EC4E7CC00FA49E2 /* ride_data.h */, + 4C8667801EEFDCDF0024AAB8 /* RideGroupManager.cpp */, + 4C8667811EEFDCDF0024AAB8 /* RideGroupManager.h */, F76C84BE1EC4E7CC00FA49E2 /* ride_ratings.c */, F76C84BF1EC4E7CC00FA49E2 /* ride_ratings.h */, F76C84C41EC4E7CC00FA49E2 /* station.c */, @@ -2972,6 +2977,7 @@ F775F5341EE35A6B001F00E7 /* DummyUiContext.cpp in Sources */, F76C887F1EC5324E00FA49E2 /* CopyFramebufferShader.cpp in Sources */, F7CB864A1EEDA1330030C877 /* KeyboardShortcuts.cpp in Sources */, + 4C8667821EEFDCDF0024AAB8 /* RideGroupManager.cpp in Sources */, F7CB86431EEDA0F50030C877 /* shortcut_key_change.c in Sources */, F76C88801EC5324E00FA49E2 /* DrawImageShader.cpp in Sources */, F775F5371EE3724F001F00E7 /* DummyAudioContext.cpp in Sources */, diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 2bdc6b201f..22de0f2ed3 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4412,6 +4412,24 @@ STR_6100 :You disconnected from the server. STR_6101 :Rides don't decrease in value over time STR_6102 :{SMALLFONT}{BLACK}The value of a ride won't decrease over time, so guests will not suddenly think that a ride is too expensive. STR_6103 :{SMALLFONT}{BLACK}This option is disabled during network play. +STR_6104 :Corkscrew Roller Coaster +STR_6105 :Hypercoaster +STR_6106 :Car Ride +STR_6107 :Monster Trucks +STR_6108 :Steel Twister +STR_6109 :Hyper-Twister +STR_6110 :Junior Roller Coaster +STR_6111 :Midi Coaster +STR_6112 :A compact steel-tracked roller coaster where the train travels through corkscrews and loops +STR_6113 :A tall non-inverting roller coaster with large drops, high speed, and comfortable trains with only lap bar restraints +STR_6114 :Riders travel slowly in powered vehicles along a track-based route +STR_6115 :Powered giant 4 x 4 trucks which can climb steep slopes +STR_6116 :Wide roller coaster trains glide along smooth steel track, traveling through a variety of inversions +STR_6117 :Sitting in comfortable trains with only simple lap restraints riders enjoy giant smooth drops and twisting track as well as plenty of 'air time' over the hills +STR_6118 :A gentle roller coaster for people who haven't yet got the courage to face the larger rides +STR_6119 :A cheap and easy to build roller coaster, but with a limited height +STR_6120 :{BABYBLUE}New vehicle now available for {STRINGID}:{NEWLINE}{STRINGID} + ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 8e54e410f1..ef961650da 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -3,6 +3,7 @@ - Feature: [#5056] Add cheat to own all land. - Feature: [#5133] Add option to display guest expenditure (as seen in RCTC). - Feature: [#5196] Add cheat to disable ride ageing. +- Feature: [#5504] Group vehicles into ride groups - Feature: [#5576] Add a persistent 'display real names of guests' setting. - Feature: OpenRCT2 now starts up on the display it was last shown on. - Improved: Mouse can now be dragged to select scenery when saving track designs diff --git a/src/openrct2/localisation/string_ids.h b/src/openrct2/localisation/string_ids.h index 3bfd243474..9e2dc1efc6 100644 --- a/src/openrct2/localisation/string_ids.h +++ b/src/openrct2/localisation/string_ids.h @@ -3768,6 +3768,25 @@ enum { STR_CHEAT_DISABLE_RIDE_VALUE_AGING_TIP = 6102, STR_OPTION_DISABLED_DURING_NETWORK_PLAY = 6103, + STR_CORKSCREW_RC_GROUP = 6104, + STR_HYPERCOASTER_GROUP = 6105, + STR_CAR_RIDE_GROUP = 6106, + STR_MONSTER_TRUCKS_GROUP = 6107, + STR_STEEL_TWISTER_GROUP = 6108, + STR_HYPER_TWISTER_GROUP = 6109, + STR_JUNIOR_RC_GROUP = 6110, + STR_MIDI_COASTER_GROUP = 6111, + STR_CORKSCREW_RC_GROUP_DESC = 6112, + STR_HYPERCOASTER_GROUP_DESC = 6113, + STR_CAR_RIDE_GROUP_DESC = 6114, + STR_MONSTER_TRUCKS_GROUP_DESC = 6115, + STR_STEEL_TWISTER_GROUP_DESC = 6116, + STR_HYPER_TWISTER_GROUP_DESC = 6117, + STR_JUNIOR_RC_GROUP_DESC = 6118, + STR_MIDI_COASTER_GROUP_DESC = 6119, + + STR_NEWS_ITEM_RESEARCH_NEW_VEHICLE_AVAILABLE = 6120, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/management/research.c b/src/openrct2/management/research.c index db0fc9edf7..8f99663c90 100644 --- a/src/openrct2/management/research.c +++ b/src/openrct2/management/research.c @@ -26,6 +26,7 @@ #include "../rct1.h" #include "../ride/ride.h" #include "../ride/ride_data.h" +#include "../ride/RideGroupManager.h" #include "../ride/track_data.h" #include "../world/scenery.h" #include "news_item.h" @@ -184,15 +185,31 @@ void research_finish_item(sint32 entryIndex) sint32 base_ride_type = (entryIndex >> 8) & 0xFF; sint32 rideEntryIndex = entryIndex & 0xFF; rct_ride_entry *rideEntry = get_ride_entry(rideEntryIndex); + if (rideEntry != NULL && rideEntry != (rct_ride_entry *)-1 && base_ride_type != RIDE_TYPE_NULL) { + bool ride_group_was_invented_before = false; + bool track_type_was_invented_before = ride_type_is_invented(base_ride_type); + rct_string_id availabilityString; + + // Determine if the ride group this entry belongs to was invented before. + if (gConfigInterface.select_by_track_type && track_type_has_ride_groups(base_ride_type)) + { + const ride_group * rideGroup = get_ride_group(base_ride_type, rideEntry); + + if (ride_group_is_invented(rideGroup)) + { + ride_group_was_invented_before = true; + } + } + ride_type_set_invented(base_ride_type); openrct2_assert(base_ride_type < countof(RideTypePossibleTrackConfigurations), "Invalid base_ride_type = %d", base_ride_type); ride_entry_set_invented(rideEntryIndex); if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE)) { - for (sint32 i = 0; i < 128; i++) { + for (sint32 i = 0; i < MAX_RESEARCHED_TRACK_TYPES; i++) { rct_ride_entry *rideEntry2 = get_ride_entry(i); if (rideEntry2 == (rct_ride_entry*)-1) continue; @@ -208,12 +225,51 @@ void research_finish_item(sint32 entryIndex) } } - set_format_arg(0, rct_string_id, ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME)) ? - rideEntry->name : RideNaming[base_ride_type].name); + if (!gConfigInterface.select_by_track_type) + { + availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; + + if (rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) + { + set_format_arg(0, rct_string_id, rideEntry->name); + } + else + { + // Makes sure the correct track name is displayed, + // e.g. Hyper-Twister instead of Steel Twister. + set_format_arg(0, rct_string_id, get_friendly_track_type_name(base_ride_type, rideEntry)); + } + } + else + { + // If a vehicle should be listed separately (maze, mini golf, flat rides, shops) + if (!rideTypeShouldLoseSeparateFlag(rideEntry)) + { + availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; + set_format_arg(0, rct_string_id, rideEntry->name); + } + // If a vehicle is the first to be invented for its ride group, show the ride group name. + else if (!track_type_was_invented_before || (track_type_has_ride_groups(base_ride_type) && !ride_group_was_invented_before)) + { + rct_ride_name naming = get_ride_naming(base_ride_type, rideEntry); + availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; + set_format_arg(0, rct_string_id, naming.name); + } + // If the vehicle should not be listed separately and it isn't the first to be invented for its ride group, + // report it as a new vehicle for the existing ride group. + else + { + availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_VEHICLE_AVAILABLE; + rct_ride_name baseRideNaming = get_ride_naming(base_ride_type, rideEntry); + + set_format_arg(0, rct_string_id, baseRideNaming.name); + set_format_arg(2, rct_string_id, rideEntry->name); + } + } if (!gSilentResearch) { if (gConfigNotifications.ride_researched) { - news_item_add_to_queue(NEWS_ITEM_RESEARCH, STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE, entryIndex); + news_item_add_to_queue(NEWS_ITEM_RESEARCH, availabilityString, entryIndex); } } diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index a8d780f98d..3b6cad5dff 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -55,7 +55,7 @@ extern "C" using namespace OpenRCT2; -constexpr uint16 OBJECT_REPOSITORY_VERSION = 10; +constexpr uint16 OBJECT_REPOSITORY_VERSION = 11; #pragma pack(push, 1) struct ObjectRepositoryHeader @@ -467,10 +467,11 @@ private: { item.RideCategory[i] = stream->ReadValue(); } - for (sint32 i = 0; i < 3; i++) + for (sint32 i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++) { item.RideType[i] = stream->ReadValue(); } + item.RideGroupIndex = stream->ReadValue(); break; case OBJECT_TYPE_SCENERY_SETS: item.NumThemeObjects = stream->ReadValue(); @@ -501,6 +502,7 @@ private: { stream->WriteValue(item.RideType[i]); } + stream->WriteValue(item.RideGroupIndex); break; case OBJECT_TYPE_SCENERY_SETS: stream->WriteValue(item.NumThemeObjects); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index 0ebc14f8a5..af15504c20 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -27,6 +27,7 @@ extern "C" { #endif #include "../object.h" + #include "../ride/ride.h" #ifdef __cplusplus } #endif @@ -55,7 +56,8 @@ typedef struct ObjectRepositoryItem { uint8 RideFlags; uint8 RideCategory[2]; - uint8 RideType[3]; + uint8 RideType[MAX_RIDE_TYPES_PER_RIDE_ENTRY]; + uint8 RideGroupIndex; }; struct { diff --git a/src/openrct2/object/RideObject.cpp b/src/openrct2/object/RideObject.cpp index 355de4e802..832dac1ec9 100644 --- a/src/openrct2/object/RideObject.cpp +++ b/src/openrct2/object/RideObject.cpp @@ -20,6 +20,7 @@ #include "../core/Util.hpp" #include "ObjectRepository.h" #include "RideObject.h" +#include "../ride/RideGroupManager.h" extern "C" { @@ -27,6 +28,7 @@ extern "C" #include "../drawing/drawing.h" #include "../localisation/localisation.h" #include "../rct1.h" + #include "../ride/ride.h" #include "../ride/track.h" } @@ -76,26 +78,18 @@ void RideObject::ReadLegacy(IReadObjectContext * context, IStream * stream) GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME); GetStringTable()->Read(context, stream, OBJ_STRING_ID_DESCRIPTION); - // TODO: Move to its own function when ride construction window is merged. - if (gConfigInterface.select_by_track_type) + // Add boosters if the track type is eligible + for (sint32 i = 0; i < 3; i++) { - _legacyType.enabledTrackPieces = 0xFFFFFFFFFFFFFFFF; - } - else - { - // When not in select by track type mode, add boosters if the track type is eligible - for (sint32 i = 0; i < 3; i++) - { - if (_legacyType.ride_type[i] == RIDE_TYPE_LOOPING_ROLLER_COASTER || - _legacyType.ride_type[i] == RIDE_TYPE_CORKSCREW_ROLLER_COASTER || - _legacyType.ride_type[i] == RIDE_TYPE_TWISTER_ROLLER_COASTER || - _legacyType.ride_type[i] == RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER || - _legacyType.ride_type[i] == RIDE_TYPE_GIGA_COASTER || - _legacyType.ride_type[i] == RIDE_TYPE_JUNIOR_ROLLER_COASTER) - { - - _legacyType.enabledTrackPieces |= (1ULL << TRACK_BOOSTER); - } + if ( + _legacyType.ride_type[i] == RIDE_TYPE_LOOPING_ROLLER_COASTER || + _legacyType.ride_type[i] == RIDE_TYPE_CORKSCREW_ROLLER_COASTER || + _legacyType.ride_type[i] == RIDE_TYPE_TWISTER_ROLLER_COASTER || + _legacyType.ride_type[i] == RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER || + _legacyType.ride_type[i] == RIDE_TYPE_GIGA_COASTER || + _legacyType.ride_type[i] == RIDE_TYPE_JUNIOR_ROLLER_COASTER + ) { + _legacyType.enabledTrackPieces |= (1ULL << TRACK_BOOSTER); } } @@ -414,6 +408,34 @@ void RideObject::SetRepositoryItem(ObjectRepositoryItem * item) const flags |= ORI_RIDE_FLAG_SEPARATE; } item->RideFlags = flags; + + // Find the first non-null ride type, to be used when checking the ride group + uint8 rideTypeIdx = ride_entry_get_first_non_null_ride_type((rct_ride_entry *)&_legacyType); + + // Determines the ride group. Will fall back to 0 if there is none found. + uint8 rideGroupIndex = 0; + + const ride_group * rideGroup = get_ride_group(rideTypeIdx, (rct_ride_entry *)&_legacyType); + + // If the ride group is NULL, the track type does not have ride groups. + if (rideGroup != NULL) + { + for (uint8 i = rideGroupIndex + 1; i < MAX_RIDE_GROUPS_PER_RIDE_TYPE; i++) + { + ride_group * irg = ride_group_find(rideTypeIdx, i); + + if (irg != NULL) + { + if (ride_groups_are_equal(irg, rideGroup)) + { + rideGroupIndex = i; + break; + } + } + } + } + + item->RideGroupIndex = rideGroupIndex; } void RideObject::ReadLegacyVehicle(IReadObjectContext * context, IStream * stream, rct_ride_entry_vehicle * vehicle) diff --git a/src/openrct2/object/RideObject.h b/src/openrct2/object/RideObject.h index 46062c64b9..58dd1105ee 100644 --- a/src/openrct2/object/RideObject.h +++ b/src/openrct2/object/RideObject.h @@ -26,7 +26,7 @@ extern "C" class RideObject final : public Object { private: - rct_ride_entry _legacyType = { 0 }; + rct_ride_entry _legacyType = { }; vehicle_colour_preset_list _presetColours = { 0 }; sint8 * _peepLoadingPositions[4] = { nullptr }; uint16 _peepLoadingPositionsCount[4] = { 0 }; diff --git a/src/openrct2/ride/RideGroupManager.cpp b/src/openrct2/ride/RideGroupManager.cpp new file mode 100644 index 0000000000..be2c6d77be --- /dev/null +++ b/src/openrct2/ride/RideGroupManager.cpp @@ -0,0 +1,239 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include +#include "RideGroupManager.h" +#include "../config/Config.h" + +extern "C" +{ + #include "../localisation/string_ids.h" + #include "../management/research.h" + #include "ride.h" + #include "ride_data.h" + #include "track.h" + #include "track_data.h" +} + +class RideGroupManager final : public IRideGroupManager +{ +public: + ride_group ride_group_corkscrew_rc = { + /*.track_type =*/ RIDE_TYPE_CORKSCREW_ROLLER_COASTER, + /*.maximum_height =*/ 28, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_VERTICAL_LOOP) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_SLOPE_CURVE_STEEP) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HALF_LOOP) | (1ULL << TRACK_CORKSCREW) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_ON_RIDE_PHOTO) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_BOOSTER), + /*.naming =*/ { STR_CORKSCREW_RC_GROUP, STR_CORKSCREW_RC_GROUP_DESC }, + }; + + ride_group ride_group_hypercoaster = { + /*.track_type =*/ RIDE_TYPE_CORKSCREW_ROLLER_COASTER, + /*.maximum_height =*/ 45, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_SLOPE_CURVE_STEEP) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_ON_RIDE_PHOTO) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_SLOPE_STEEP_LONG), + /*.naming =*/ { STR_HYPERCOASTER_GROUP, STR_HYPERCOASTER_GROUP_DESC }, + }; + + ride_group ride_group_car_ride = { + /*.track_type =*/ RIDE_TYPE_CAR_RIDE, + /*.maximum_height =*/ 6, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_CURVE_VERY_SMALL) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_SPINNING_TUNNEL), + /*.naming =*/ { STR_CAR_RIDE_GROUP, STR_CAR_RIDE_GROUP_DESC }, + }; + + ride_group ride_group_monster_trucks = { + /*.track_type =*/ RIDE_TYPE_CAR_RIDE, + /*.maximum_height =*/ 18, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_CURVE_VERY_SMALL) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_RAPIDS), + /*.naming =*/ { STR_MONSTER_TRUCKS_GROUP, STR_MONSTER_TRUCKS_GROUP_DESC }, + }; + + ride_group ride_group_steel_twister_rc = { + /*.track_type =*/ RIDE_TYPE_TWISTER_ROLLER_COASTER, + /*.maximum_height =*/ 34, + /*.available_track_pieces =*/ (1ULL << TRACK_FLAT) | (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_VERTICAL_LOOP) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_SLOPE_CURVE_STEEP) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HALF_LOOP) | (1ULL << TRACK_CORKSCREW) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_ON_RIDE_PHOTO) | (1ULL << TRACK_SLOPE_VERTICAL) | (1ULL << TRACK_BARREL_ROLL) | (1ULL << TRACK_POWERED_LIFT) | (1ULL << TRACK_HALF_LOOP_LARGE) | (1ULL << TRACK_SLOPE_CURVE_BANKED) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_SLOPE_ROLL_BANKING) | (1ULL << TRACK_SLOPE_STEEP_LONG) | (1ULL << TRACK_CURVE_VERTICAL) | (1ULL << TRACK_QUARTER_LOOP) | (1ULL << TRACK_BOOSTER), + /*.naming =*/ { STR_STEEL_TWISTER_GROUP, STR_STEEL_TWISTER_GROUP_DESC }, + }; + + ride_group ride_group_hyper_twister = { + /*.track_type =*/ RIDE_TYPE_TWISTER_ROLLER_COASTER, + /*.maximum_height =*/ 54, + /*.available_track_pieces =*/ (1ULL << TRACK_FLAT) | (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_SLOPE_CURVE_STEEP) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_ON_RIDE_PHOTO) | (1ULL << TRACK_POWERED_LIFT) | (1ULL << TRACK_SLOPE_CURVE_BANKED) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_SLOPE_ROLL_BANKING) | (1ULL << TRACK_SLOPE_STEEP_LONG), + /*.naming =*/ { STR_HYPER_TWISTER_GROUP, STR_HYPER_TWISTER_GROUP_DESC }, + }; + + ride_group ride_group_junior_rc = { + /*.track_type =*/ RIDE_TYPE_JUNIOR_ROLLER_COASTER, + /*.maximum_height =*/ 12, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_LIFT_HILL_CURVE) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_LONG) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_BOOSTER), + /*.naming =*/ { STR_JUNIOR_RC_GROUP, STR_JUNIOR_RC_GROUP_DESC }, + }; + + ride_group ride_group_midi_coaster = { + /*.track_type =*/ RIDE_TYPE_JUNIOR_ROLLER_COASTER, + /*.maximum_height =*/ 15, + /*.available_track_pieces =*/ (1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_LIFT_HILL_CURVE) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_LONG) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) | (1ULL << TRACK_HELIX_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_BOOSTER), + /*.naming =*/ { STR_MIDI_COASTER_GROUP, STR_MIDI_COASTER_GROUP_DESC }, + }; + + ride_group corkscrew_rc_groups[MAX_RIDE_GROUPS_PER_RIDE_TYPE] = { ride_group_corkscrew_rc, ride_group_hypercoaster }; + ride_group junior_rc_groups[MAX_RIDE_GROUPS_PER_RIDE_TYPE] = { ride_group_junior_rc, ride_group_midi_coaster }; + ride_group car_ride_groups[MAX_RIDE_GROUPS_PER_RIDE_TYPE] = { ride_group_car_ride, ride_group_monster_trucks }; + ride_group twister_rc_groups[MAX_RIDE_GROUPS_PER_RIDE_TYPE] = { ride_group_steel_twister_rc, ride_group_hyper_twister }; + + const ride_group * GetRideGroup(uint8 trackType, rct_ride_entry * rideEntry) const override + { + switch (trackType) { + case RIDE_TYPE_CORKSCREW_ROLLER_COASTER: + if (rideEntry->enabledTrackPieces & (1ULL << TRACK_VERTICAL_LOOP)) + return (ride_group *) &ride_group_corkscrew_rc; + else + return (ride_group *) &ride_group_hypercoaster; + case RIDE_TYPE_JUNIOR_ROLLER_COASTER: + if (ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP)) + return (ride_group *) &ride_group_midi_coaster; + else + return (ride_group *) &ride_group_junior_rc; + case RIDE_TYPE_CAR_RIDE: + if (rideEntry->enabledTrackPieces & (1ULL << TRACK_SLOPE_STEEP)) + return (ride_group *) &ride_group_monster_trucks; + else + return (ride_group *) &ride_group_car_ride; + case RIDE_TYPE_TWISTER_ROLLER_COASTER: + if (rideEntry->enabledTrackPieces & (1ULL << TRACK_VERTICAL_LOOP)) + return (ride_group *) &ride_group_steel_twister_rc; + else + return (ride_group *) &ride_group_hyper_twister; + default: + return NULL; + } + } + + bool TrackTypeHasRideGroups(uint8 trackType) const override + { + if (!gConfigInterface.select_by_track_type) { + return false; + } + + switch (trackType) + { + case RIDE_TYPE_CORKSCREW_ROLLER_COASTER: + case RIDE_TYPE_JUNIOR_ROLLER_COASTER: + case RIDE_TYPE_CAR_RIDE: + case RIDE_TYPE_TWISTER_ROLLER_COASTER: + return true; + default: + return false; + } + } + + ride_group * RideGroupFind(uint8 rideType, uint8 index) const override + { + ride_group * rideGroup; + + switch(rideType) + { + case RIDE_TYPE_CORKSCREW_ROLLER_COASTER: + rideGroup = (ride_group *) &corkscrew_rc_groups[index]; + break; + case RIDE_TYPE_JUNIOR_ROLLER_COASTER: + rideGroup = (ride_group *) &junior_rc_groups[index]; + break; + case RIDE_TYPE_CAR_RIDE: + rideGroup = (ride_group *) &car_ride_groups[index]; + break; + case RIDE_TYPE_TWISTER_ROLLER_COASTER: + rideGroup = (ride_group *) &twister_rc_groups[index]; + break; + default: + return NULL; + } + + return rideGroup; + } + + bool RideGroupsAreEqual(const ride_group * a, const ride_group * b) const override + { + if (a != NULL && b != NULL && (a->naming.name == b->naming.name && a->naming.description == b->naming.description)) + { + return true; + } + return false; + } + + bool RideGroupIsInvented(const ride_group * rideGroup) const override + { + if (!ride_type_is_invented(rideGroup->track_type)) + return false; + + uint8 *rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideGroup->track_type); + + while (*rideEntryIndexPtr != 255) + { + uint8 rideEntryIndex = *rideEntryIndexPtr++; + + if (!ride_entry_is_invented(rideEntryIndex)) + continue; + + rct_ride_entry *rideEntry = get_ride_entry(rideEntryIndex); + const ride_group * rideEntryRideGroup = GetRideGroup(rideGroup->track_type, rideEntry); + + if (!RideGroupsAreEqual(rideGroup, rideEntryRideGroup)) + continue; + + // The ride entry is invented and belongs to the same ride group. This means the ride group is invented. + return true; + } + + return false; + } +}; + +static std::unique_ptr _rideGroupManager = std::unique_ptr(new RideGroupManager()); +IRideGroupManager * GetRideGroupManager() +{ + return _rideGroupManager.get(); +} + +extern "C" +{ + const ride_group * get_ride_group(uint8 trackType, rct_ride_entry * rideEntry) + { + const IRideGroupManager * rideGroupManager = GetRideGroupManager(); + return rideGroupManager->GetRideGroup(trackType, rideEntry); + } + + bool track_type_has_ride_groups(uint8 trackType) + { + const IRideGroupManager * rideGroupManager = GetRideGroupManager(); + return rideGroupManager->TrackTypeHasRideGroups(trackType); + } + + ride_group * ride_group_find(uint8 rideType, uint8 index) + { + const IRideGroupManager * rideGroupManager = GetRideGroupManager(); + return rideGroupManager->RideGroupFind(rideType, index); + } + + bool ride_groups_are_equal(const ride_group * a, const ride_group * b) + { + const IRideGroupManager * rideGroupManager = GetRideGroupManager(); + return rideGroupManager->RideGroupsAreEqual(a, b); + } + bool ride_group_is_invented(const ride_group * rideGroup) + { + const IRideGroupManager * rideGroupManager = GetRideGroupManager(); + return rideGroupManager->RideGroupIsInvented(rideGroup); + } +} diff --git a/src/openrct2/ride/RideGroupManager.h b/src/openrct2/ride/RideGroupManager.h new file mode 100644 index 0000000000..ac60f99cff --- /dev/null +++ b/src/openrct2/ride/RideGroupManager.h @@ -0,0 +1,60 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + #include "../common.h" + #include "ride_data.h" +#ifdef __cplusplus +} +#endif + +#define MAX_RIDE_GROUPS_PER_RIDE_TYPE 2 + +typedef struct ride_group { + uint8 track_type; + uint16 maximum_height; + uint64 available_track_pieces; + rct_ride_name naming; +} ride_group; + +#ifdef __cplusplus +interface IRideGroupManager +{ + virtual const ride_group * GetRideGroup(uint8 trackType, rct_ride_entry * rideEntry) const abstract; + virtual bool TrackTypeHasRideGroups(uint8 trackType) const abstract; + virtual ride_group * RideGroupFind(uint8 rideType, uint8 index) const abstract; + virtual bool RideGroupsAreEqual(const ride_group * a, const ride_group * b) const abstract; + virtual bool RideGroupIsInvented(const ride_group * rideGroup) const abstract; +}; + +IRideGroupManager * GetRideGroupManager(); + +extern "C" +{ +#endif + const ride_group * get_ride_group(uint8 trackType, rct_ride_entry * rideEntry); + bool track_type_has_ride_groups(uint8 trackType); + ride_group * ride_group_find(uint8 rideType, uint8 index); + bool ride_groups_are_equal(const ride_group * a, const ride_group * b); + bool ride_group_is_invented(const ride_group * rideGroup); +#ifdef __cplusplus +} +#endif diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 444f29837c..408e70021d 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -24,6 +24,7 @@ #include "../core/FileScanner.h" #include "../core/FileStream.hpp" #include "../core/Path.hpp" +#include "RideGroupManager.h" #include "../core/String.hpp" #include "../object/ObjectRepository.h" #include "../object/RideObject.h" @@ -104,12 +105,14 @@ public: size_t GetCountForObjectEntry(uint8 rideType, const std::string &entry) const override { size_t count = 0; - IObjectRepository * repo = GetObjectRepository(); + const IObjectRepository * repo = GetObjectRepository(); - for (const auto item : _items) + for (const auto &item : _items) { if (item.RideType != rideType) + { continue; + } bool entryIsNotSeparate = false; if (entry.empty()) @@ -128,6 +131,30 @@ public: return count; } + size_t GetCountForRideGroup(uint8 rideType, const ride_group * rideGroup) const override + { + size_t count = 0; + const IObjectRepository * repo = GetObjectRepository(); + + for (const auto &item : _items) + { + if (item.RideType != rideType) + { + continue; + } + + const ObjectRepositoryItem * ori = repo->FindObject(item.ObjectEntry.c_str()); + ride_group * itemRideGroup = ride_group_find(rideType, ori->RideGroupIndex); + + if (itemRideGroup != NULL && ride_groups_are_equal(itemRideGroup, rideGroup)) + { + count++; + } + } + + return count; + } + /** * * @param outRefs @@ -138,12 +165,14 @@ public: size_t GetItemsForObjectEntry(track_design_file_ref * * outRefs, uint8 rideType, const std::string &entry) const override { std::vector refs; - IObjectRepository * repo = GetObjectRepository(); + const IObjectRepository * repo = GetObjectRepository(); - for (const auto item : _items) + for (const auto &item : _items) { if (item.RideType != rideType) + { continue; + } bool entryIsNotSeparate = false; if (entry.empty()) @@ -167,6 +196,34 @@ public: return refs.size(); } + size_t GetItemsForRideGroup(track_design_file_ref **outRefs, uint8 rideType, const ride_group * rideGroup) const override + { + std::vector refs; + const IObjectRepository * repo = GetObjectRepository(); + + for (const auto &item : _items) + { + if (item.RideType != rideType) + { + continue; + } + + const ObjectRepositoryItem * ori = repo->FindObject(item.ObjectEntry.c_str()); + ride_group * itemRideGroup = ride_group_find(rideType, ori->RideGroupIndex); + + if (itemRideGroup != NULL && ride_groups_are_equal(itemRideGroup, rideGroup)) + { + track_design_file_ref ref; + ref.name = String::Duplicate(GetNameFromTrackPath(item.Path)); + ref.path = String::Duplicate(item.Path); + refs.push_back(ref); + } + } + + *outRefs = Collections::ToArray(refs); + return refs.size(); + } + void Scan() override { std::string rct2Directory = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::TRACK); @@ -424,12 +481,24 @@ extern "C" return repo->GetCountForObjectEntry(rideType, String::ToStd(entry)); } + size_t track_repository_get_count_for_ride_group(uint8 rideType, const ride_group * rideGroup) + { + ITrackDesignRepository * repo = GetTrackDesignRepository(); + return repo->GetCountForRideGroup(rideType, rideGroup); + } + size_t track_repository_get_items_for_ride(track_design_file_ref * * outRefs, uint8 rideType, const utf8 * entry) { ITrackDesignRepository * repo = GetTrackDesignRepository(); return repo->GetItemsForObjectEntry(outRefs, rideType, String::ToStd(entry)); } + size_t track_repository_get_items_for_ride_group(track_design_file_ref * * outRefs, uint8 rideType, const ride_group * rideGroup) + { + ITrackDesignRepository * repo = GetTrackDesignRepository(); + return repo->GetItemsForRideGroup(outRefs, rideType, rideGroup); + } + bool track_repository_delete(const utf8 * path) { ITrackDesignRepository * repo = GetTrackDesignRepository(); diff --git a/src/openrct2/ride/TrackDesignRepository.h b/src/openrct2/ride/TrackDesignRepository.h index 8fa5ac0319..3cce08be5d 100644 --- a/src/openrct2/ride/TrackDesignRepository.h +++ b/src/openrct2/ride/TrackDesignRepository.h @@ -17,6 +17,8 @@ #pragma once #include "../common.h" +#include "RideGroupManager.h" + typedef struct track_design_file_ref { @@ -39,9 +41,13 @@ interface ITrackDesignRepository virtual size_t GetCount() const abstract; virtual size_t GetCountForObjectEntry(uint8 rideType, const std::string &entry) const abstract; + virtual size_t GetCountForRideGroup(uint8 rideType, const ride_group * rideGroup) const abstract; virtual size_t GetItemsForObjectEntry(track_design_file_ref * * outRefs, uint8 rideType, const std::string &entry) const abstract; + virtual size_t GetItemsForRideGroup(track_design_file_ref **outRefs, + uint8 rideType, + const ride_group * rideGroup) const abstract; virtual void Scan() abstract; virtual bool Delete(const std::string &path) abstract; @@ -60,7 +66,9 @@ extern "C" #endif void track_repository_scan(); size_t track_repository_get_count_for_ride(uint8 rideType, const utf8 * entry); + size_t track_repository_get_count_for_ride_group(uint8 rideType, const ride_group * rideGroup); size_t track_repository_get_items_for_ride(track_design_file_ref * * outRefs, uint8 rideType, const utf8 * entry); + size_t track_repository_get_items_for_ride_group(track_design_file_ref * * outRefs, uint8 rideType, const ride_group * rideGroup); utf8 * track_repository_get_name_from_path(const utf8 *path); bool track_repository_delete(const utf8 *path); bool track_repository_rename(const utf8 *path, const utf8 *newName); diff --git a/src/openrct2/ride/ride.c b/src/openrct2/ride/ride.c index 3cea23e55c..a264acb7a4 100644 --- a/src/openrct2/ride/ride.c +++ b/src/openrct2/ride/ride.c @@ -48,6 +48,7 @@ #include "cable_lift.h" #include "ride.h" #include "ride_data.h" +#include "RideGroupManager.h" #include "station.h" #include "track.h" #include "track_data.h" @@ -218,6 +219,9 @@ static void ride_set_vehicle_colours_to_random_preset(rct_ride *ride, uint8 pres void loc_6DDF9C(rct_ride *ride, rct_map_element *mapElement); bool sub_6CA2DF(sint32 *trackType, sint32 *trackDirection, sint32 *rideIndex, sint32 *edxRS16, sint32 *x, sint32 *y, sint32 *z, sint32 *properties); money32 place_provisional_track_piece(sint32 rideIndex, sint32 trackType, sint32 trackDirection, sint32 edxRS16, sint32 x, sint32 y, sint32 z); +static void ride_set_name_to_track_default(rct_ride * ride, rct_ride_entry * rideEntry); +static void ride_set_name_to_vehicle_default(rct_ride * ride, rct_ride_entry * rideEntry); + rct_ride *get_ride(sint32 index) { @@ -5923,7 +5927,6 @@ money32 ride_create_command(sint32 type, sint32 subType, sint32 flags, uint8 *ou */ static money32 ride_create(sint32 type, sint32 subType, sint32 flags, sint32 *outRideIndex, sint32 *outRideColour) { - char rideNameBuffer[256]; rct_ride *ride; rct_ride_entry *rideEntry; sint32 rideIndex, rideEntryIndex; @@ -5938,7 +5941,14 @@ static money32 ride_create(sint32 type, sint32 subType, sint32 flags, sint32 *ou uint8 *availableRideEntries = get_ride_entry_indices_for_ride_type(type); for (uint8 *rei = availableRideEntries; *rei != 255; rei++) { rideEntry = get_ride_entry(*rei); - if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { + + // Can happen in select-by-track-type mode + if (!ride_entry_is_invented(*rei)) + { + continue; + } + + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { subType = *rei; goto foundRideEntry; } @@ -5989,51 +5999,13 @@ foundRideEntry: ride_set_colour_preset(ride, *outRideColour & 0xFF); ride->overall_view = 0xFFFF; -#pragma pack(push, 1) - struct { - uint16 type_name; - uint16 number; - } name_args; - assert_struct_size(name_args, 4); -#pragma pack(pop) - // Ride name if (rideEntryIndex == 255) { - useDefaultName: - ride->name = STR_NONE; - name_args.type_name = 2 + ride->type; - name_args.number = 0; - do { - name_args.number++; - format_string(rideNameBuffer, 256, 1, &name_args); - } while (ride_name_exists(rideNameBuffer)); - ride->name = 1; - ride->name_arguments_type_name = name_args.type_name; - ride->name_arguments_number = name_args.number; + ride_set_name_to_track_default(ride, rideEntry); + } else if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { + ride_set_name_to_track_default(ride, rideEntry); } else { - if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { - goto useDefaultName; - } - ride->name = 1; - ride->name_arguments_type_name = rideEntry->name; - rct_string_id rideNameStringId = 0; - name_args.type_name = rideEntry->name; - name_args.number = 0; - - do { - name_args.number++; - format_string(rideNameBuffer, 256, ride->name, &name_args); - } while (ride_name_exists(rideNameBuffer)); - - ride->name_arguments_type_name = name_args.type_name; - ride->name_arguments_number = name_args.number; - - rideNameStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, rideNameBuffer); - if (rideNameStringId != 0) { - ride->name = rideNameStringId; - } else { - goto useDefaultName; - } + ride_set_name_to_vehicle_default(ride, rideEntry); } for (size_t i = 0; i < 4; i++) { @@ -6185,6 +6157,104 @@ foundRideEntry: return 0; } +static void ride_set_name_to_track_default(rct_ride *ride, rct_ride_entry * rideEntry) +{ + char rideNameBuffer[256]; + ride_name_args name_args; + + ride->name = STR_NONE; + + // This fixes the Hyper-Twister being displayed as the generic Steel Twister when not in select-by-track-type mode. + if (!gConfigInterface.select_by_track_type && ride->type == RIDE_TYPE_TWISTER_ROLLER_COASTER && !(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE)) { + name_args.type_name = STR_HYPER_TWISTER_GROUP; + } + else if (track_type_has_ride_groups(ride->type)) { + const ride_group * rideGroup = get_ride_group(ride->type, rideEntry); + name_args.type_name = rideGroup->naming.name; + } else { + name_args.type_name = RideNaming[ride->type].name; + } + + name_args.number = 0; + do { + name_args.number++; + format_string(rideNameBuffer, 256, 1, &name_args); + } while (ride_name_exists(rideNameBuffer)); + ride->name = 1; + ride->name_arguments_type_name = name_args.type_name; + ride->name_arguments_number = name_args.number; +} + +static void ride_set_name_to_vehicle_default(rct_ride * ride, rct_ride_entry * rideEntry) +{ + char rideNameBuffer[256]; + ride_name_args name_args; + + ride->name = 1; + ride->name_arguments_type_name = rideEntry->name; + rct_string_id rideNameStringId = 0; + name_args.type_name = rideEntry->name; + name_args.number = 0; + + do { + name_args.number++; + format_string(rideNameBuffer, 256, ride->name, &name_args); + } while (ride_name_exists(rideNameBuffer)); + + ride->name_arguments_type_name = name_args.type_name; + ride->name_arguments_number = name_args.number; + + rideNameStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, rideNameBuffer); + if (rideNameStringId != 0) { + ride->name = rideNameStringId; + } else { + ride_set_name_to_track_default(ride, rideEntry); + } +} + +/** + * This will return the name of the track type or ride group, as seen in the research window. + */ +rct_string_id get_friendly_track_type_name(uint8 trackType, rct_ride_entry * rideEntry) +{ + if (!gConfigInterface.select_by_track_type) { + if (trackType == RIDE_TYPE_TWISTER_ROLLER_COASTER) { + return STR_HYPER_TWISTER_GROUP; + } else { + return RideNaming[trackType].name; + } + } else { + if (track_type_has_ride_groups(trackType)) { + const ride_group * rideGroup = get_ride_group(trackType, rideEntry); + return rideGroup->naming.name; + } else { + return RideNaming[trackType].name; + } + } +} + +/** + * This will return the name of the ride, as seen in the New Ride window. + */ +rct_ride_name get_ride_naming(uint8 rideType, rct_ride_entry * rideEntry) +{ + // This fixes the Hyper-Twister being displayed as the generic Steel Twister when not in select-by-track-type mode. + if (!gConfigInterface.select_by_track_type && rideType == RIDE_TYPE_TWISTER_ROLLER_COASTER && !(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE)) { + return (rct_ride_name){ STR_HYPER_TWISTER_GROUP, STR_HYPER_TWISTER_GROUP_DESC }; + } + + if (track_type_has_ride_groups(rideType)) { + const ride_group * rideGroup = get_ride_group(rideType, rideEntry); + return rideGroup->naming; + } + else if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { + return RideNaming[rideType]; + } + else { + return rideEntry->naming; + } +} + /** * * rct2: 0x006B3F0F @@ -7436,7 +7506,7 @@ uint8 ride_entry_get_vehicle_at_position(sint32 rideEntryIndex,sint32 numCarsPer } } -//Finds track pieces that a given ride entry doesn't have the sprites for +// Finds track pieces that a given ride entry has sprites for uint64 ride_entry_get_supported_track_pieces(rct_ride_entry* rideEntry) { uint64 supportedPieces = 0xFFFFFFFFFFFFFFFFULL; @@ -8193,19 +8263,13 @@ void ride_reset_all_names() sint32 i; rct_ride *ride; char rideNameBuffer[256]; + ride_name_args name_args; FOR_ALL_RIDES(i, ride) { ride->name = STR_NONE; -#pragma pack(push, 1) - struct { - uint16 type_name; - uint16 number; - } name_args; - assert_struct_size(name_args, 4); -#pragma pack(pop) - name_args.type_name = 2 + ride->type; + name_args.type_name = RideNaming[ride->type].name; name_args.number = 0; do { name_args.number++; @@ -8500,3 +8564,19 @@ const char * ride_type_get_enum_name(sint32 rideType) return RideTypeEnumNames[rideType]; } + +/** + * Searches for a non-null ride type in a ride entry. + * If none is found, it will still return RIDE_TYPE_NULL. + */ +uint8 ride_entry_get_first_non_null_ride_type(rct_ride_entry * rideEntry) +{ + for (uint8 i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++) + { + if (rideEntry->ride_type[i] != RIDE_TYPE_NULL) + { + return rideEntry->ride_type[i]; + } + } + return RIDE_TYPE_NULL; +} diff --git a/src/openrct2/ride/ride.h b/src/openrct2/ride/ride.h index 308cd8b95c..2cbcdc2753 100644 --- a/src/openrct2/ride/ride.h +++ b/src/openrct2/ride/ride.h @@ -80,16 +80,27 @@ typedef struct vehicle_colour_preset_list { } vehicle_colour_preset_list; assert_struct_size(vehicle_colour_preset_list, (1 + 256 * 3)); +typedef struct rct_ride_name { + rct_string_id name; + rct_string_id description; +} rct_ride_name; +assert_struct_size(rct_ride_name, 4); + /** * Ride type structure. * size: unknown */ typedef struct rct_ride_entry { - rct_string_id name; // 0x000 - rct_string_id description; // 0x002 + union { + rct_ride_name naming; + struct { + rct_string_id name; // 0x000 + rct_string_id description; // 0x002 + }; + }; uint32 images_offset; // 0x004 uint32 flags; // 0x008 - uint8 ride_type[MAX_RIDE_TYPES_PER_RIDE_ENTRY]; // 0x00C + uint8 ride_type[MAX_RIDE_TYPES_PER_RIDE_ENTRY]; // 0x00C uint8 min_cars_in_train; // 0x00F uint8 max_cars_in_train; // 0x010 uint8 cars_per_flat_ride; // 0x011 @@ -371,6 +382,12 @@ typedef struct track_begin_end { assert_struct_size(track_begin_end, 36); #endif +typedef struct ride_name_args { + uint16 type_name; + uint16 number; +} ride_name_args; +assert_struct_size(ride_name_args, 4); + #pragma pack(pop) /* @@ -1053,6 +1070,8 @@ void game_command_set_ride_setting(sint32 *eax, sint32 *ebx, sint32 *ecx, sint32 sint32 ride_get_refund_price(sint32 ride_id); bool shop_item_is_photo(sint32 shopItem); bool shop_item_has_common_price(sint32 shopItem); +rct_string_id get_friendly_track_type_name(uint8 trackType, rct_ride_entry * rideEntry); +rct_ride_name get_ride_naming(uint8 rideType, rct_ride_entry * rideEntry); void game_command_create_ride(sint32 *eax, sint32 *ebx, sint32 *ecx, sint32 *edx, sint32 *esi, sint32 *edi, sint32 *ebp); void game_command_callback_ride_construct_new(sint32 eax, sint32 ebx, sint32 ecx, sint32 edx, sint32 esi, sint32 edi, sint32 ebp); void game_command_callback_ride_construct_placed_front(sint32 eax, sint32 ebx, sint32 ecx, sint32 edx, sint32 esi, sint32 edi, sint32 ebp); @@ -1166,4 +1185,6 @@ bool ride_has_ratings(const rct_ride * ride); const char * ride_type_get_enum_name(sint32 rideType); +uint8 ride_entry_get_first_non_null_ride_type(rct_ride_entry * rideEntry); + #endif diff --git a/src/openrct2/ride/ride_data.h b/src/openrct2/ride/ride_data.h index 8977979b54..56159c2c1b 100644 --- a/src/openrct2/ride/ride_data.h +++ b/src/openrct2/ride/ride_data.h @@ -54,11 +54,6 @@ typedef struct rct_ride_name_convention { ride_component_type station; } rct_ride_name_convention; -typedef struct rct_ride_name { - rct_string_id name; - rct_string_id description; -} rct_ride_name; - typedef struct rct_ride_entrance_definition { uint32 sprite_index; uint16 height; diff --git a/src/openrct2/ride/track.c b/src/openrct2/ride/track.c index 88ae0733c6..6e23a670be 100644 --- a/src/openrct2/ride/track.c +++ b/src/openrct2/ride/track.c @@ -34,6 +34,7 @@ #include "ride.h" #include "ride_data.h" #include "ride_ratings.h" +#include "RideGroupManager.h" #include "station.h" #include "track.h" #include "track_data.h" @@ -1239,10 +1240,24 @@ static money32 track_place(sint32 rideIndex, sint32 type, sint32 originX, sint32 if (!gCheatsDisableSupportLimits){ sint32 ride_height = clearanceZ - mapElement->base_height; if (ride_height >= 0) { - sint32 maxHeight = rideEntry->max_height; - if (maxHeight == 0) { - maxHeight = RideData5[ride->type].max_height; + + uint16 maxHeight; + if (gConfigInterface.select_by_track_type) { + if (track_type_has_ride_groups(ride->type)) { + const ride_group * rideGroup = get_ride_group(ride->type, rideEntry); + maxHeight = rideGroup->maximum_height; + } else { + maxHeight = RideData5[ride->type].max_height; + } + } else { + maxHeight = rideEntry->max_height; + // If a rideEntry specifies 0 as max height, it means OpenRCT2 + // should fall back on the default for the track type. + if (maxHeight == 0) { + maxHeight = RideData5[ride->type].max_height; + } } + ride_height /= 2; if (ride_height > maxHeight && !byte_9D8150) { gGameCommandErrorText = STR_TOO_HIGH_FOR_SUPPORTS; diff --git a/src/openrct2/ride/track_design.c b/src/openrct2/ride/track_design.c index 77adcdc379..6f5bfe9212 100644 --- a/src/openrct2/ride/track_design.c +++ b/src/openrct2/ride/track_design.c @@ -22,6 +22,7 @@ #include "../management/finance.h" #include "../network/network.h" #include "../object/ObjectManager.h" +#include "../object/ObjectRepository.h" #include "../rct1.h" #include "../rct1/Tables.h" #include "../util/sawyercoding.h" @@ -1379,7 +1380,11 @@ static bool track_design_place_preview(rct_track_td6 *td6, money32 *cost, uint8 gParkFlags = backup_park_flags; if (resultCost != MONEY32_UNDEFINED) { - if (!find_object_in_entry_group(&td6->vehicle_object, &entry_type, &entry_index)){ + if (!find_object_in_entry_group(&td6->vehicle_object, &entry_type, &entry_index)) { + *flags |= TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE; + } + else if (!ride_entry_is_invented(entry_index)) + { *flags |= TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE; } @@ -1422,6 +1427,41 @@ static money32 place_track_design(sint16 x, sint16 y, sint16 z, uint8 flags, uin if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex)) { entryIndex = 0xFF; } + // Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode. + else if (!ride_entry_is_invented(entryIndex)) + { + entryIndex = 0xFF; + } + + // The rest of the cases are handled by the code in ride_create() + if (track_type_has_ride_groups(td6->type) && entryIndex == 0xFF) + { + const ObjectRepositoryItem * ori = object_repository_find_object_by_name(rideEntryObject->name); + if (ori != NULL) + { + uint8 rideGroupIndex = ori->RideGroupIndex; + ride_group * td6RideGroup = ride_group_find(td6->type, rideGroupIndex); + rct_ride_entry * ire; + + uint8 *availableRideEntries = get_ride_entry_indices_for_ride_type(td6->type); + for (uint8 *rei = availableRideEntries; *rei != 255; rei++) + { + ire = get_ride_entry(*rei); + + if (!ride_entry_is_invented(*rei)) + { + continue; + } + + const ride_group * irg = get_ride_group(td6->type, ire); + if (ride_groups_are_equal(td6RideGroup, irg)) + { + entryIndex = *rei; + break; + } + } + } + } uint8 rideIndex; uint8 rideColour; diff --git a/src/openrct2/windows/new_ride.c b/src/openrct2/windows/new_ride.c index a89e7c6e45..ade08073df 100644 --- a/src/openrct2/windows/new_ride.c +++ b/src/openrct2/windows/new_ride.c @@ -27,6 +27,7 @@ #include "../rct1.h" #include "../ride/ride.h" #include "../ride/ride_data.h" +#include "../ride/RideGroupManager.h" #include "../ride/track.h" #include "../ride/track_data.h" #include "../ride/track_design.h" @@ -263,6 +264,7 @@ static void window_new_ride_refresh_widget_sizing(rct_window *w); static ride_list_item window_new_ride_scroll_get_ride_list_item_at(rct_window *w, sint32 x, sint32 y); static void window_new_ride_paint_ride_information(rct_window *w, rct_drawpixelinfo *dpi, ride_list_item item, sint32 x, sint32 y, sint32 width); static void window_new_ride_select(rct_window *w); +static ride_list_item * window_new_ride_iterate_over_ride_group(uint8 rideType, uint8 rideGroupIndex, ride_list_item * nextListItem); static ride_list_item _lastTrackDesignCountRideType; static sint32 _lastTrackDesignCount; @@ -311,63 +313,14 @@ static void window_new_ride_populate_list() continue; } - char preferredVehicleName[9]; - safe_strcpy(preferredVehicleName, " ", sizeof(preferredVehicleName)); - if (ride_type_is_invented(rideType)) { - sint32 dh = 0; - uint8 *rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideType); - - // For each ride entry for this ride type - while (*rideEntryIndexPtr != 255) { - uint8 rideEntryIndex = *rideEntryIndexPtr++; - char rideEntryName[9]; - memcpy(rideEntryName,object_entry_groups[OBJECT_TYPE_RIDE].entries[rideEntryIndex].name,8); - rideEntryName[8]=0; - - // Skip if vehicle type is not invented yet - if (!ride_entry_is_invented(rideEntryIndex)) - continue; - - // Ride entries - rct_ride_entry *rideEntry = get_ride_entry(rideEntryIndex); - - // Check if ride is in this category - if (!gConfigInterface.select_by_track_type && (currentCategory != rideEntry->category[0] && currentCategory != rideEntry->category[1])) - continue; - - // Skip if the vehicle isn't the preferred vehicle for this generic track type - if (gConfigInterface.select_by_track_type && (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) || rideTypeShouldLoseSeparateFlag(rideEntry))) { - if (strcmp(preferredVehicleName, " \0") == 0) { - safe_strcpy(preferredVehicleName, rideEntryName, sizeof(preferredVehicleName)); - preferredVehicleName[8] = 0; - } else { - if (vehicle_preference_compare(rideType, preferredVehicleName, rideEntryName) == 1) { - safe_strcpy(preferredVehicleName, rideEntryName, sizeof(preferredVehicleName)); - preferredVehicleName[8] = 0; - } else { - continue; - } - } - } - - if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { - dh &= ~4; - nextListItem->type = rideType; - nextListItem->entry_index = rideEntryIndex; - nextListItem++; - } else if (!(dh & 1)) { - dh |= 5; - nextListItem->type = rideType; - nextListItem->entry_index = rideEntryIndex; - nextListItem++; - } else if (dh & 4) { - if (rideType == rideEntry->ride_type[0]) { - nextListItem--; - nextListItem->type = rideType; - nextListItem->entry_index = rideEntryIndex; - nextListItem++; - } + + if (!track_type_has_ride_groups(rideType)) { + nextListItem = window_new_ride_iterate_over_ride_group(rideType, 0, nextListItem); + } + else { + for (uint8 j = 0; j < MAX_RIDE_GROUPS_PER_RIDE_TYPE; j++) { + nextListItem = window_new_ride_iterate_over_ride_group(rideType, j, nextListItem); } } } @@ -378,6 +331,85 @@ static void window_new_ride_populate_list() _trackSelectionByType = gConfigInterface.select_by_track_type; } +static ride_list_item * window_new_ride_iterate_over_ride_group(uint8 rideType, uint8 rideGroupIndex, ride_list_item * nextListItem) +{ + uint8 currentCategory = _windowNewRideCurrentTab; + + bool buttonForRideTypeCreated = false; + bool allowDrawingOverLastButton = false; + uint8 *rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(rideType); + + char preferredVehicleName[9]; + safe_strcpy(preferredVehicleName, " ", sizeof(preferredVehicleName)); + + // For each ride entry for this ride type + while (*rideEntryIndexPtr != 255) { + uint8 rideEntryIndex = *rideEntryIndexPtr++; + char rideEntryName[9]; + memcpy(rideEntryName,object_entry_groups[OBJECT_TYPE_RIDE].entries[rideEntryIndex].name,8); + rideEntryName[8]=0; + + // Skip if vehicle type is not invented yet + if (!ride_entry_is_invented(rideEntryIndex)) + continue; + + // Ride entries + rct_ride_entry *rideEntry = get_ride_entry(rideEntryIndex); + + // Check if ride is in this category + if (!gConfigInterface.select_by_track_type && (currentCategory != rideEntry->category[0] && currentCategory != rideEntry->category[1])) + continue; + + if (track_type_has_ride_groups(rideType)) + { + const ride_group * rideEntryRideGroup = get_ride_group(rideType, rideEntry); + const ride_group * rideGroup = ride_group_find(rideType, rideGroupIndex); + + if (!ride_groups_are_equal(rideEntryRideGroup, rideGroup)) + continue; + } + + // Skip if the vehicle isn't the preferred vehicle for this generic track type + if (gConfigInterface.select_by_track_type && (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) || rideTypeShouldLoseSeparateFlag(rideEntry))) { + if (strcmp(preferredVehicleName, " \0") == 0) { + safe_strcpy(preferredVehicleName, rideEntryName, sizeof(preferredVehicleName)); + preferredVehicleName[8] = 0; + } else { + if (vehicle_preference_compare(rideType, preferredVehicleName, rideEntryName) == 1) { + safe_strcpy(preferredVehicleName, rideEntryName, sizeof(preferredVehicleName)); + preferredVehicleName[8] = 0; + } else { + continue; + } + } + } + + // Determines how and where to draw a button for this ride type/vehicle. + if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { // Separate, draw apart + allowDrawingOverLastButton = false; + nextListItem->type = rideType; + nextListItem->entry_index = rideEntryIndex; + nextListItem++; + } else if (!buttonForRideTypeCreated) { // Non-separate, draw-apart + // Draw apart + buttonForRideTypeCreated = true; + allowDrawingOverLastButton = true; + nextListItem->type = rideType; + nextListItem->entry_index = rideEntryIndex; + nextListItem++; + } else if (allowDrawingOverLastButton) { // Non-separate, draw over previous + if (rideType == rideEntry->ride_type[0]) { + nextListItem--; + nextListItem->type = rideType; + nextListItem->entry_index = rideEntryIndex; + nextListItem++; + } + } + } + + return nextListItem; +} + /** * * rct2: 0x006B7220 @@ -477,27 +509,52 @@ rct_window *window_new_ride_open_research() void window_new_ride_focus(ride_list_item rideItem) { rct_window *w; - rct_ride_entry *rideType; + rct_ride_entry *rideEntry; + bool entryFound = false; + // Find the first non-null ride type index. w = window_find_by_class(WC_CONSTRUCT_RIDE); if (w == NULL) return; - rideType = get_ride_entry(rideItem.entry_index); + rideEntry = get_ride_entry(rideItem.entry_index); + uint8 rideTypeIndex = ride_entry_get_first_non_null_ride_type(rideEntry); - if(!gConfigInterface.select_by_track_type) - window_new_ride_set_page(w, rideType->category[0]); - else - window_new_ride_set_page(w, gRideCategories[rideType->ride_type[0]]); + if(!gConfigInterface.select_by_track_type) { + window_new_ride_set_page(w, rideEntry->category[0]); + } + else { + window_new_ride_set_page(w, gRideCategories[rideTypeIndex]); + } - ride_list_item *listItem = _windowNewRideListItems; - while (listItem->type != RIDE_TYPE_NULL) { + for (ride_list_item *listItem = _windowNewRideListItems; listItem->type != RIDE_TYPE_NULL; listItem++) { if (listItem->type == rideItem.type && listItem->entry_index == rideItem.entry_index) { _windowNewRideHighlightedItem[0] = rideItem; w->new_ride.highlighted_ride_id = rideItem.ride_type_and_entry; window_new_ride_scroll_to_focused_ride(w); + entryFound = true; + break; + } + } + + // If this entry was not found it was most likely hidden due to it not being the preferential type. + // In this case, select the first entry that belongs to the same ride group. + if (!entryFound && gConfigInterface.select_by_track_type) + { + const ride_group * rideGroup = get_ride_group(rideTypeIndex, rideEntry); + + for (ride_list_item *listItem = _windowNewRideListItems; listItem->type != RIDE_TYPE_NULL; listItem++) { + if (listItem->type == rideItem.type) { + const ride_group * irg = get_ride_group(rideTypeIndex, rideEntry); + + if (!track_type_has_ride_groups(rideTypeIndex) || ride_groups_are_equal(rideGroup, irg)) { + _windowNewRideHighlightedItem[0] = rideItem; + w->new_ride.highlighted_ride_id = rideItem.ride_type_and_entry; + window_new_ride_scroll_to_focused_ride(w); + break; + } + } } - listItem++; } } @@ -850,14 +907,22 @@ static sint32 get_num_track_designs(ride_list_item item) { char entry[9]; const char *entryPtr = NULL; + rct_ride_entry * rideEntry = NULL; + if (item.type < 0x80) { - rct_ride_entry *rideEntry = get_ride_entry(item.entry_index); + rideEntry = get_ride_entry(item.entry_index); if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { get_ride_entry_name(entry, item.entry_index); entryPtr = entry; } } + if (rideEntry != NULL && track_type_has_ride_groups(item.type)) + { + const ride_group * rideGroup = get_ride_group(item.type, rideEntry); + return (sint32)track_repository_get_count_for_ride_group(item.type, rideGroup); + } + return (sint32)track_repository_get_count_for_ride(item.type, entryPtr); } @@ -868,17 +933,12 @@ static sint32 get_num_track_designs(ride_list_item item) static void window_new_ride_paint_ride_information(rct_window *w, rct_drawpixelinfo *dpi, ride_list_item item, sint32 x, sint32 y, sint32 width) { rct_ride_entry *rideEntry = get_ride_entry(item.entry_index); + rct_ride_name rideNaming; // Ride name and description - rct_string_id rideName = rideEntry->name; - rct_string_id rideDescription = rideEntry->description; - if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { - rideName = RideNaming[item.type].name; - rideDescription = RideNaming[item.type].description; - } - - set_format_arg(0, rct_string_id, rideName); - set_format_arg(2, rct_string_id, rideDescription); + rideNaming = get_ride_naming(item.type , rideEntry); + set_format_arg(0, rct_string_id, rideNaming.name); + set_format_arg(2, rct_string_id, rideNaming.description); gfx_draw_string_left_wrapped(dpi, gCommonFormatArgs, x, y, width, STR_NEW_RIDE_NAME_AND_DESCRIPTION, COLOUR_BLACK); // Number of designs available diff --git a/src/openrct2/windows/research.c b/src/openrct2/windows/research.c index 4e510afef0..862e8f30d6 100644 --- a/src/openrct2/windows/research.c +++ b/src/openrct2/windows/research.c @@ -359,10 +359,11 @@ void window_research_development_page_paint(rct_window *w, rct_drawpixelinfo *dp if (gResearchProgressStage != RESEARCH_STAGE_DESIGNING) { uint32 typeId = gResearchNextItem; if (typeId >= 0x10000) { + uint8 baseRideType = (typeId >> 8) & 0xFF; rct_ride_entry *rideEntry = get_ride_entry(typeId & 0xFF); stringId = (rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) ? rideEntry->name : - ((typeId >> 8) & 0xFF) + 2; + get_friendly_track_type_name(baseRideType, rideEntry); // Makes sure the correct track name is displayed, e.g. Hyper-Twister instead of Steel Twister. } else { stringId = get_scenery_group_entry(typeId)->name; } diff --git a/src/openrct2/windows/ride.c b/src/openrct2/windows/ride.c index dd66902ecb..e3381b08b8 100644 --- a/src/openrct2/windows/ride.c +++ b/src/openrct2/windows/ride.c @@ -33,6 +33,7 @@ #include "../rct2.h" #include "../ride/ride.h" #include "../ride/ride_data.h" +#include "../ride/RideGroupManager.h" #include "../ride/track.h" #include "../ride/track_data.h" #include "../ride/track_design.h" @@ -1477,8 +1478,6 @@ static rct_window *window_ride_open(sint32 rideIndex) { rct_window *w; rct_ride *ride; - uint8 *rideEntryIndexPtr; - sint32 numSubTypes; w = window_create_auto_pos(316, 207, window_ride_page_events[0], WC_RIDE, WF_10 | WF_RESIZABLE); w->widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN]; @@ -1499,14 +1498,6 @@ static rct_window *window_ride_open(sint32 rideIndex) ride = get_ride(rideIndex); _rideType = ride->type; - numSubTypes = 0; - rideEntryIndexPtr = get_ride_entry_indices_for_ride_type(ride->type); - for (; *rideEntryIndexPtr != 0xFF; rideEntryIndexPtr++) { - if (ride_entry_is_invented(*rideEntryIndexPtr)) { - numSubTypes++; - } - } - w->var_496 = numSubTypes; window_ride_update_overall_view((uint8) rideIndex); @@ -2639,6 +2630,7 @@ static void window_ride_vehicle_mousedown(rct_widgetindex widgetIndex, rct_windo rct_widget *dropdownWidget = widget - 1; rct_ride *ride; rct_ride_entry *rideEntry, *currentRideEntry; + const ride_group * rideGroup, * currentRideGroup; sint32 numItems, rideEntryIndex, selectedIndex, rideTypeIterator, rideTypeIteratorMax; uint8 *rideEntryIndexPtr; bool selectionShouldBeExpanded; @@ -2675,7 +2667,7 @@ static void window_ride_vehicle_mousedown(rct_widgetindex widgetIndex, rct_windo for (uint8 *currentRideEntryIndex = rideEntryIndexPtr; *currentRideEntryIndex != 0xFF && numItems <= 63; currentRideEntryIndex++) { rideEntryIndex = *currentRideEntryIndex; currentRideEntry = get_ride_entry(rideEntryIndex); - // Skip if vehicle has the same track type, but not same subtype, unless subtype switching is enabled + // Skip if vehicle wants to be separate, unless subtype switching is enabled if ((currentRideEntry->flags & (RIDE_ENTRY_FLAG_SEPARATE_RIDE | RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME)) && !(gConfigInterface.select_by_track_type || selectionShouldBeExpanded)) continue; @@ -2683,6 +2675,16 @@ static void window_ride_vehicle_mousedown(rct_widgetindex widgetIndex, rct_windo if (!ride_entry_is_invented(rideEntryIndex)) continue; + // Skip if vehicle does not belong to the same ride group + if (track_type_has_ride_groups(ride->type) && !selectionShouldBeExpanded) + { + rideGroup = get_ride_group(ride->type, rideEntry); + currentRideGroup = get_ride_group(ride->type, currentRideEntry); + + if (!ride_groups_are_equal(rideGroup, currentRideGroup)) + continue; + } + if (ride->subtype == rideEntryIndex) selectedIndex = numItems; @@ -2785,7 +2787,7 @@ static void window_ride_vehicle_invalidate(rct_window *w) // Vehicle type window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].text = rideEntry->name; // Always show a dropdown button when changing subtypes is allowed - if ((w->var_496 <= 1 || (rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE)) && !(gConfigInterface.select_by_track_type || gCheatsShowVehiclesFromOtherTrackTypes)) { + if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !(gConfigInterface.select_by_track_type || gCheatsShowVehiclesFromOtherTrackTypes)) { window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].type = WWT_14; window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE_DROPDOWN].type = WWT_EMPTY; w->enabled_widgets &= ~(1 << WIDX_VEHICLE_TYPE); @@ -2889,7 +2891,7 @@ static void window_ride_vehicle_paint(rct_window *w, rct_drawpixelinfo *dpi) } y += 5; - if ((!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) || rideTypeShouldLoseSeparateFlag(rideEntry)) && w->var_496 > 1) { + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) || rideTypeShouldLoseSeparateFlag(rideEntry)) { // Excitement Factor factor = rideEntry->excitement_multipler; if (factor > 0) { diff --git a/src/openrct2/windows/ride_construction.c b/src/openrct2/windows/ride_construction.c index fe5e153dfa..fc885d1dd2 100644 --- a/src/openrct2/windows/ride_construction.c +++ b/src/openrct2/windows/ride_construction.c @@ -34,6 +34,7 @@ #include "../sprites.h" #include "../world/map.h" #include "../world/entrance.h" +#include "../ride/RideGroupManager.h" #pragma region Widgets @@ -2745,7 +2746,24 @@ static void window_ride_construction_update_enabled_track_pieces() return; sint32 rideType = (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? RideData4[ride->type].alternate_type : ride->type; - _enabledRidePieces.ab = rideEntry->enabledTrackPieces & RideTypePossibleTrackConfigurations[rideType]; + if (!gConfigInterface.select_by_track_type) + { + _enabledRidePieces.ab = rideEntry->enabledTrackPieces & RideTypePossibleTrackConfigurations[rideType]; + } + else + { + if (track_type_has_ride_groups(rideType)) + { + const ride_group * rideGroup = get_ride_group(rideType, rideEntry); + _enabledRidePieces.ab = rideGroup->available_track_pieces; + } + else + { + _enabledRidePieces.ab = RideTypePossibleTrackConfigurations[rideType]; + } + + } + } /** diff --git a/src/openrct2/windows/track_list.c b/src/openrct2/windows/track_list.c index 70ebaaf936..82dd8457af 100644 --- a/src/openrct2/windows/track_list.c +++ b/src/openrct2/windows/track_list.c @@ -345,10 +345,10 @@ static void window_track_list_invalidate(rct_window *w) { rct_string_id stringId = STR_NONE; rct_ride_entry *entry = get_ride_entry(_window_track_list_item.entry_index); + if (entry != NULL && entry != (rct_ride_entry*)-1) { - stringId = entry->name; - if (!(entry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(entry)) - stringId = _window_track_list_item.type + 2; + rct_ride_name rideName = get_ride_naming(_window_track_list_item.type, entry); + stringId = rideName.name; } set_format_arg(0, rct_string_id, stringId); @@ -611,16 +611,22 @@ static void window_track_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, static void track_list_load_designs(ride_list_item item) { - char entry[9]; - const char *entryPtr = NULL; - if (item.type < 0x80) { - rct_ride_entry *rideEntry = get_ride_entry(item.entry_index); - if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { - get_ride_entry_name(entry, item.entry_index); - entryPtr = entry; + if (!track_type_has_ride_groups(item.type)) { + char entry[9]; + const char *entryPtr = NULL; + if (item.type < 0x80) { + rct_ride_entry *rideEntry = get_ride_entry(item.entry_index); + if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { + get_ride_entry_name(entry, item.entry_index); + entryPtr = entry; + } } + _trackDesignsCount = track_repository_get_items_for_ride(&_trackDesigns, item.type, entryPtr); + } else { + rct_ride_entry *rideEntry = get_ride_entry(item.entry_index); + const ride_group * rideGroup = get_ride_group(item.type, rideEntry); + _trackDesignsCount = track_repository_get_items_for_ride_group(&_trackDesigns, item.type, rideGroup); } - _trackDesignsCount = track_repository_get_items_for_ride(&_trackDesigns, item.type, entryPtr); } static bool track_list_load_design_for_preview(utf8 *path)