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