1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "../Context.h"
11 #include "../object/Object.h"
12 #include "../object/ObjectManager.h"
13 #include "../ride/Ride.h"
14 #include "../ride/RideData.h"
15 #include "../ride/Track.h"
16 
17 #include <cstdint>
18 
RCT2RideTypeToOpenRCT2RideType(uint8_t rct2RideType,const rct_ride_entry * rideEntry)19 ObjectEntryIndex RCT2RideTypeToOpenRCT2RideType(uint8_t rct2RideType, const rct_ride_entry* rideEntry)
20 {
21     switch (rct2RideType)
22     {
23         case RIDE_TYPE_CORKSCREW_ROLLER_COASTER:
24             if (rideEntry != nullptr && !(ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_VERTICAL_LOOP)))
25                 return RIDE_TYPE_HYPERCOASTER;
26             return RIDE_TYPE_CORKSCREW_ROLLER_COASTER;
27         case RIDE_TYPE_JUNIOR_ROLLER_COASTER:
28             if (rideEntry != nullptr && ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP))
29                 return RIDE_TYPE_CLASSIC_MINI_ROLLER_COASTER;
30             return RIDE_TYPE_JUNIOR_ROLLER_COASTER;
31         case RIDE_TYPE_CAR_RIDE:
32             if (rideEntry != nullptr && ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP))
33                 return RIDE_TYPE_MONSTER_TRUCKS;
34             return RIDE_TYPE_CAR_RIDE;
35         case RIDE_TYPE_TWISTER_ROLLER_COASTER:
36             if (rideEntry != nullptr && rideEntry->flags & RIDE_ENTRY_FLAG_NO_INVERSIONS)
37                 return RIDE_TYPE_HYPER_TWISTER;
38             return RIDE_TYPE_TWISTER_ROLLER_COASTER;
39         case RIDE_TYPE_STEEL_WILD_MOUSE:
40             if (rideEntry != nullptr && !(ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP)))
41                 return RIDE_TYPE_SPINNING_WILD_MOUSE;
42             return RIDE_TYPE_STEEL_WILD_MOUSE;
43 
44         default:
45             return rct2RideType;
46     }
47 }
48 
RCT2RideTypeNeedsConversion(uint8_t rct2RideType)49 bool RCT2RideTypeNeedsConversion(uint8_t rct2RideType)
50 {
51     switch (rct2RideType)
52     {
53         case RIDE_TYPE_CORKSCREW_ROLLER_COASTER:
54         case RIDE_TYPE_JUNIOR_ROLLER_COASTER:
55         case RIDE_TYPE_CAR_RIDE:
56         case RIDE_TYPE_TWISTER_ROLLER_COASTER:
57         case RIDE_TYPE_STEEL_WILD_MOUSE:
58             return true;
59 
60         default:
61             return false;
62     }
63 }
64 
OpenRCT2RideTypeToRCT2RideType(ObjectEntryIndex openrct2Type)65 uint8_t OpenRCT2RideTypeToRCT2RideType(ObjectEntryIndex openrct2Type)
66 {
67     switch (openrct2Type)
68     {
69         case RIDE_TYPE_HYPERCOASTER:
70             return RIDE_TYPE_CORKSCREW_ROLLER_COASTER;
71         case RIDE_TYPE_CLASSIC_MINI_ROLLER_COASTER:
72             return RIDE_TYPE_JUNIOR_ROLLER_COASTER;
73         case RIDE_TYPE_MONSTER_TRUCKS:
74             return RIDE_TYPE_CAR_RIDE;
75         case RIDE_TYPE_HYPER_TWISTER:
76             return RIDE_TYPE_TWISTER_ROLLER_COASTER;
77         case RIDE_TYPE_SPINNING_WILD_MOUSE:
78             return RIDE_TYPE_STEEL_WILD_MOUSE;
79 
80         default:
81             return openrct2Type;
82     }
83 }
84 
GetRCT2StringBufferLen(const char * buffer,size_t maxBufferLen)85 size_t GetRCT2StringBufferLen(const char* buffer, size_t maxBufferLen)
86 {
87     constexpr char MULTIBYTE = static_cast<char>(255);
88     size_t len = 0;
89     for (size_t i = 0; i < maxBufferLen; i++)
90     {
91         auto ch = buffer[i];
92         if (ch == MULTIBYTE)
93         {
94             i += 2;
95 
96             // Check if reading two more bytes exceeds max buffer len
97             if (i < maxBufferLen)
98             {
99                 len += 3;
100             }
101         }
102         else if (ch == '\0')
103         {
104             break;
105         }
106         else
107         {
108             len++;
109         }
110     }
111     return len;
112 }
113 
GetMinCarsPerTrain() const114 uint8_t rct2_ride::GetMinCarsPerTrain() const
115 {
116     return min_max_cars_per_train >> 4;
117 }
118 
GetMaxCarsPerTrain() const119 uint8_t rct2_ride::GetMaxCarsPerTrain() const
120 {
121     return min_max_cars_per_train & 0xF;
122 }
123 
SetMinCarsPerTrain(uint8_t newValue)124 void rct2_ride::SetMinCarsPerTrain(uint8_t newValue)
125 {
126     min_max_cars_per_train &= ~0xF0;
127     min_max_cars_per_train |= (newValue << 4);
128 }
129 
SetMaxCarsPerTrain(uint8_t newValue)130 void rct2_ride::SetMaxCarsPerTrain(uint8_t newValue)
131 {
132     min_max_cars_per_train &= ~0x0F;
133     min_max_cars_per_train |= newValue & 0x0F;
134 }
135 
RCT2TrackTypeIsBooster(uint8_t rideType,uint16_t trackType)136 bool RCT2TrackTypeIsBooster(uint8_t rideType, uint16_t trackType)
137 {
138     // Boosters share their ID with the Spinning Control track.
139     return rideType != RIDE_TYPE_SPINNING_WILD_MOUSE && rideType != RIDE_TYPE_STEEL_WILD_MOUSE
140         && trackType == TrackElemType::Booster;
141 }
142 
RCT2TrackTypeToOpenRCT2(RCT12TrackType origTrackType,uint8_t rideType,bool convertFlat)143 track_type_t RCT2TrackTypeToOpenRCT2(RCT12TrackType origTrackType, uint8_t rideType, bool convertFlat)
144 {
145     if (convertFlat && GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
146         return RCT12FlatTrackTypeToOpenRCT2(origTrackType);
147     if (origTrackType == TrackElemType::RotationControlToggleAlias && !RCT2TrackTypeIsBooster(rideType, origTrackType))
148         return TrackElemType::RotationControlToggle;
149 
150     return origTrackType;
151 }
152 
OpenRCT2TrackTypeToRCT2(track_type_t origTrackType)153 RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType)
154 {
155     if (origTrackType == TrackElemType::RotationControlToggle)
156         return TrackElemType::RotationControlToggleAlias;
157 
158     // This function is safe to run this way round.
159     return OpenRCT2FlatTrackTypeToRCT12(origTrackType);
160 }
161 
162 static FootpathMapping _footpathMappings[] = {
163     // RCT2 mappings
164     { "PATHASH ", "rct2.footpath_surface.ash", "rct2.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_black" },
165     { "PATHCRZY", "rct2.footpath_surface.crazy_paving", "rct2.footpath_surface.queue_yellow",
166       "rct2.footpath_railings.concrete" },
167     { "PATHDIRT", "rct2.footpath_surface.dirt", "rct2.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_brown" },
168     { "PATHSPCE", "rct2.footpath_surface.tarmac_red", "rct2.footpath_surface.queue_red", "rct2.footpath_railings.space" },
169     { "ROAD    ", "rct2.footpath_surface.road", "rct2.footpath_surface.queue_blue", "rct2.footpath_railings.wood" },
170     { "TARMACB ", "rct2.footpath_surface.tarmac_brown", "rct2.footpath_surface.queue_yellow",
171       "rct2.footpath_railings.concrete" },
172     { "TARMACG ", "rct2.footpath_surface.tarmac_green", "rct2.footpath_surface.queue_green",
173       "rct2.footpath_railings.concrete_green" },
174     { "TARMAC  ", "rct2.footpath_surface.tarmac", "rct2.footpath_surface.queue_blue", "rct2.footpath_railings.wood" },
175     // Time Twister
176     { "1920PATH", "rct2tt.footpath_surface.pavement", "rct2tt.footpath_surface.queue_pavement",
177       "rct2tt.footpath_railings.pavement" },
178     { "FUTRPATH", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard",
179       "rct2tt.footpath_railings.circuitboard" },
180     { "FUTRPAT2", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard",
181       "openrct2.footpath_railings.invisible" },
182     { "JURRPATH", "rct2tt.footpath_surface.rocky", "rct2.footpath_surface.queue_yellow", "rct2tt.footpath_railings.rocky" },
183     { "MEDIPATH", "rct2tt.footpath_surface.medieval", "rct2.footpath_surface.queue_yellow",
184       "rct2tt.footpath_railings.medieval" },
185     { "MYTHPATH", "rct2tt.footpath_surface.mosaic", "rct2.footpath_surface.queue_yellow",
186       "rct2tt.footpath_railings.balustrade" },
187     { "RANBPATH", "rct2tt.footpath_surface.rainbow", "rct2tt.footpath_surface.queue_rainbow",
188       "rct2tt.footpath_railings.rainbow" },
189 
190     // RCT 1 mappings (for reverse lookup)
191     { "PATHASH ", "rct1aa.footpath_surface.ash", "rct1aa.footpath_surface.queue_yellow",
192       "rct2.footpath_railings.bamboo_black" },
193     { "PATHCRZY", "rct1.footpath_surface.crazy_paving", "rct1aa.footpath_surface.queue_yellow",
194       "rct2.footpath_railings.concrete" },
195     { "PATHDIRT", "rct1.footpath_surface.dirt", "rct1aa.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_brown" },
196     { "PATHSPCE", "rct1aa.footpath_surface.tarmac_red", "rct1.footpath_surface.queue_red", "rct1ll.footpath_railings.space" },
197     { "TARMACB ", "rct1aa.footpath_surface.tarmac_brown", "rct1aa.footpath_surface.queue_yellow",
198       "rct2.footpath_railings.concrete" },
199     { "TARMACG ", "rct1aa.footpath_surface.tarmac_green", "rct1aa.footpath_surface.queue_green",
200       "rct2.footpath_railings.concrete_green" },
201     { "TARMAC  ", "rct1.footpath_surface.tarmac", "rct1.footpath_surface.queue_blue", "rct2.footpath_railings.wood" },
202     { "PATHCRZY", "rct1.footpath_surface.tiles_brown", "rct1aa.footpath_surface.queue_yellow",
203       "rct2.footpath_railings.concrete" },
204     { "PATHCRZY", "rct1aa.footpath_surface.tiles_grey", "rct1.footpath_surface.queue_blue", "rct2.footpath_railings.concrete" },
205     { "PATHCRZY", "rct1ll.footpath_surface.tiles_red", "rct1.footpath_surface.queue_red", "rct2.footpath_railings.concrete" },
206     { "PATHCRZY", "rct1ll.footpath_surface.tiles_green", "rct1aa.footpath_surface.queue_green",
207       "rct2.footpath_railings.concrete" },
208 };
209 
GetFootpathSurfaceId(const ObjectEntryDescriptor & desc,bool ideallyLoaded,bool isQueue)210 const FootpathMapping* GetFootpathSurfaceId(const ObjectEntryDescriptor& desc, bool ideallyLoaded, bool isQueue)
211 {
212     auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
213 
214     auto name = desc.Entry.GetName();
215     for (const auto& mapping : _footpathMappings)
216     {
217         if (mapping.Original == name)
218         {
219             if (ideallyLoaded)
220             {
221                 auto obj = objManager.GetLoadedObject(
222                     ObjectEntryDescriptor(isQueue ? mapping.QueueSurface : mapping.NormalSurface));
223                 if (obj == nullptr)
224                     continue;
225             }
226             return &mapping;
227         }
228     }
229     return nullptr;
230 }
231 
GetBestObjectEntryForSurface(std::string_view surface,std::string_view railings)232 std::optional<rct_object_entry> GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings)
233 {
234     rct_object_entry result;
235     std::memset(&result, 0, sizeof(result));
236 
237     result.SetType(ObjectType::Paths);
238 
239     auto foundMapping = false;
240     for (const auto& mapping : _footpathMappings)
241     {
242         if (surface == mapping.NormalSurface || surface == mapping.QueueSurface)
243         {
244             if (railings == mapping.Railing)
245             {
246                 // Best match found
247                 foundMapping = true;
248                 result.SetName(mapping.Original);
249                 break;
250             }
251 
252             if (!foundMapping)
253             {
254                 // Found a mapping, but keep searching to see if there is a closer match
255                 foundMapping = true;
256                 result.SetName(mapping.Original);
257             }
258         }
259     }
260 
261     if (foundMapping)
262         return result;
263     return {};
264 }
265